зеркало из https://github.com/mozilla/pjs.git
Bug 341821: removing the previous commit, the patch was broken.
This commit is contained in:
Родитель
34b875046d
Коммит
f0e402e1f9
|
@ -971,7 +971,7 @@ struct JSExtendedClass {
|
|||
JSEqualityOp equality;
|
||||
JSObjectOp outerObject;
|
||||
JSObjectOp innerObject;
|
||||
JSCloseOp close;
|
||||
JSFinalizeOp close;
|
||||
jsword reserved0;
|
||||
jsword reserved1;
|
||||
jsword reserved2;
|
||||
|
|
|
@ -417,7 +417,7 @@ js_FinishAtomState(JSAtomState *state)
|
|||
}
|
||||
|
||||
typedef struct MarkArgs {
|
||||
JSBool keepAtoms;
|
||||
uintN gcflags;
|
||||
JSGCThingMarker mark;
|
||||
void *data;
|
||||
} MarkArgs;
|
||||
|
@ -431,9 +431,9 @@ js_atom_marker(JSHashEntry *he, intN i, void *arg)
|
|||
|
||||
atom = (JSAtom *)he;
|
||||
args = (MarkArgs *)arg;
|
||||
if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->keepAtoms) {
|
||||
if (!args->keepAtoms)
|
||||
atom->flags |= ATOM_MARK;
|
||||
if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) ||
|
||||
(args->gcflags & GC_KEEP_ATOMS)) {
|
||||
atom->flags |= ATOM_MARK;
|
||||
key = ATOM_KEY(atom);
|
||||
if (JSVAL_IS_GCTHING(key))
|
||||
args->mark(JSVAL_TO_GCTHING(key), args->data);
|
||||
|
@ -442,14 +442,14 @@ js_atom_marker(JSHashEntry *he, intN i, void *arg)
|
|||
}
|
||||
|
||||
void
|
||||
js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark,
|
||||
js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark,
|
||||
void *data)
|
||||
{
|
||||
MarkArgs args;
|
||||
|
||||
if (!state->table)
|
||||
return;
|
||||
args.keepAtoms = keepAtoms;
|
||||
args.gcflags = gcflags;
|
||||
args.mark = mark;
|
||||
args.data = data;
|
||||
JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args);
|
||||
|
@ -475,11 +475,8 @@ js_atom_sweeper(JSHashEntry *he, intN i, void *arg)
|
|||
}
|
||||
|
||||
void
|
||||
js_SweepAtomState(JSAtomState *state, JSBool keepAtoms)
|
||||
js_SweepAtomState(JSAtomState *state)
|
||||
{
|
||||
if (keepAtoms)
|
||||
return;
|
||||
|
||||
state->liveAtoms = 0;
|
||||
if (state->table)
|
||||
JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, state);
|
||||
|
|
|
@ -343,11 +343,11 @@ typedef void
|
|||
(*JSGCThingMarker)(void *thing, void *data);
|
||||
|
||||
extern void
|
||||
js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark,
|
||||
js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark,
|
||||
void *data);
|
||||
|
||||
extern void
|
||||
js_SweepAtomState(JSAtomState *state, JSBool keepAtoms);
|
||||
js_SweepAtomState(JSAtomState *state);
|
||||
|
||||
extern JSBool
|
||||
js_InitPinnedAtoms(JSContext *cx, JSAtomState *state);
|
||||
|
|
|
@ -145,7 +145,8 @@ struct JSRuntime {
|
|||
*/
|
||||
JSPackedBool gcPoke;
|
||||
JSPackedBool gcRunning;
|
||||
uint16 gcPadding;
|
||||
JSPackedBool gcClosePhase;
|
||||
uint8 gcPadding;
|
||||
|
||||
JSGCCallback gcCallback;
|
||||
uint32 gcMallocBytes;
|
||||
|
@ -167,18 +168,18 @@ struct JSRuntime {
|
|||
*/
|
||||
uint32 gcPrivateBytes;
|
||||
|
||||
/*
|
||||
* Table for tracking objects of extended classes that have non-null close
|
||||
* hooks, and need the GC to perform two-phase finalization.
|
||||
*/
|
||||
JSPtrTable gcCloseTable;
|
||||
|
||||
/*
|
||||
* Table for tracking iterators to ensure that we close iterator's state
|
||||
* before finalizing the iterable object.
|
||||
*/
|
||||
JSPtrTable gcIteratorTable;
|
||||
|
||||
/*
|
||||
* Singly linked list of items tracking objects of extended classes that
|
||||
* have non-null close hooks.
|
||||
*/
|
||||
JSGCCloseListItem *gcCloseList;
|
||||
|
||||
#ifdef JS_GCMETER
|
||||
JSGCStats gcStats;
|
||||
#endif
|
||||
|
@ -673,15 +674,6 @@ struct JSContext {
|
|||
/* Top of the GC mark stack. */
|
||||
void *gcCurrentMarkNode;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* List of objects with close hooks that the current context is trying to
|
||||
* close.
|
||||
*/
|
||||
JSGCCloseListItem *gcObjectsToClose;
|
||||
|
||||
/* Flag indicating that the current thread is excuting close hooks. */
|
||||
JSBool gcRunningCloseHooks;
|
||||
};
|
||||
|
||||
#define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
|
||||
|
|
388
js/src/jsgc.c
388
js/src/jsgc.c
|
@ -61,7 +61,6 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jsconfig.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsinterp.h"
|
||||
|
@ -249,9 +248,16 @@ typedef struct JSPtrTableInfo {
|
|||
uint16 linearGrowthThreshold;
|
||||
} JSPtrTableInfo;
|
||||
|
||||
#define GC_CLOSE_TABLE_MIN 4
|
||||
#define GC_CLOSE_TABLE_LINEAR 1024
|
||||
#define GC_ITERATOR_TABLE_MIN 4
|
||||
#define GC_ITERATOR_TABLE_LINEAR 1024
|
||||
|
||||
static const JSPtrTableInfo closeTableInfo = {
|
||||
GC_CLOSE_TABLE_MIN,
|
||||
GC_CLOSE_TABLE_LINEAR
|
||||
};
|
||||
|
||||
static const JSPtrTableInfo iteratorTableInfo = {
|
||||
GC_ITERATOR_TABLE_MIN,
|
||||
GC_ITERATOR_TABLE_LINEAR
|
||||
|
@ -480,6 +486,9 @@ FinishGCArenaLists(JSRuntime *rt)
|
|||
DestroyGCArena(rt, arenaList, &arenaList->last);
|
||||
arenaList->freeList = NULL;
|
||||
}
|
||||
|
||||
FreePtrTable(&rt->gcCloseTable, &closeTableInfo);
|
||||
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
|
||||
}
|
||||
|
||||
uint8 *
|
||||
|
@ -690,9 +699,6 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
|
|||
fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree));
|
||||
fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg));
|
||||
fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots));
|
||||
fprintf(fp, " close hooks: %lu\n", ULSTAT(nclose));
|
||||
fprintf(fp, " max close hooks: %lu\n", ULSTAT(maxnclose));
|
||||
fprintf(fp, "max close hooks not yet run: %lu\n", ULSTAT(maxcloselater));
|
||||
#undef UL
|
||||
#undef US
|
||||
|
||||
|
@ -727,16 +733,6 @@ js_FinishGC(JSRuntime *rt)
|
|||
#ifdef JS_GCMETER
|
||||
js_DumpGCStats(rt, stdout);
|
||||
#endif
|
||||
|
||||
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
|
||||
#ifdef DEBUG
|
||||
if (rt->gcCloseList) {
|
||||
fprintf(stderr,
|
||||
"JS engine warning: Some close hooks were not executed before destroying the\n"
|
||||
"JSRuntime at %p. This may result in a memory leak.\n", rt);
|
||||
}
|
||||
#endif
|
||||
rt->gcCloseList = NULL;
|
||||
FinishGCArenaLists(rt);
|
||||
|
||||
if (rt->gcRootsHash.ops) {
|
||||
|
@ -850,7 +846,7 @@ js_RegisterCloseableIterator(JSContext *cx, JSObject *obj)
|
|||
JSBool ok;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj);
|
||||
|
@ -882,19 +878,18 @@ CloseIteratorStates(JSContext *cx)
|
|||
}
|
||||
|
||||
JSBool
|
||||
js_RegisterObjectWithCloseHook(JSContext *cx, JSObject *obj)
|
||||
js_AddObjectToCloseTable(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSGCCloseListItem *item;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
JSBool ok;
|
||||
|
||||
/*
|
||||
* Return early without doing anything if shutting down, to prevent a bad
|
||||
* close hook from ilooping the GC. This could result in shutdown leaks,
|
||||
* so printf in DEBUG builds.
|
||||
*/
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
if (rt->state == JSRTS_LANDING) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,
|
||||
|
@ -907,108 +902,76 @@ js_RegisterObjectWithCloseHook(JSContext *cx, JSObject *obj)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
item = (JSGCCloseListItem *)js_NewGCThing(cx, GCX_PRIVATE, sizeof *item);
|
||||
if (!item)
|
||||
return JS_FALSE;
|
||||
|
||||
item->object = obj;
|
||||
JS_LOCK_GC(rt);
|
||||
item->next = rt->gcCloseList;
|
||||
rt->gcCloseList = item;
|
||||
METER(rt->gcStats.nclose++);
|
||||
METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose,
|
||||
rt->gcStats.nclose));
|
||||
ok = AddToPtrTable(cx, &rt->gcCloseTable, &closeTableInfo, obj);
|
||||
JS_UNLOCK_GC(rt);
|
||||
|
||||
return JS_TRUE;
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Struct to define object to close after the finalization phase of GC:
|
||||
* GC executes the close hooks for elements of JSRuntime.gcCloseTable.array
|
||||
* with indexes from the [startIndex, startIndex + count) range.
|
||||
*/
|
||||
typedef struct JSObjectsToClose {
|
||||
size_t count;
|
||||
size_t startIndex;
|
||||
#ifdef DEBUG
|
||||
JSPtrTable tableSnapshot;
|
||||
#endif
|
||||
} JSObjectsToClose;
|
||||
|
||||
static void
|
||||
ScanDelayedChildren(JSContext *cx);
|
||||
|
||||
static void
|
||||
MarkCloseList(JSContext *cx, JSGCCloseListItem *item)
|
||||
{
|
||||
while (item) {
|
||||
GC_MARK(cx, item, "close-phase list item");
|
||||
GC_MARK(cx, item->object, "close-phase object");
|
||||
item = item->next;
|
||||
}
|
||||
}
|
||||
|
||||
static JSGCCloseListItem **
|
||||
GetCloseListTail(JSGCCloseListItem **itemp)
|
||||
{
|
||||
while (*itemp)
|
||||
itemp = &(*itemp)->next;
|
||||
return itemp;
|
||||
}
|
||||
|
||||
/*
|
||||
* First half of the close phase: loop over the objects that we set aside
|
||||
* in the close table and mark them to protect them against finalization
|
||||
* during sweeping phase.
|
||||
*/
|
||||
static void
|
||||
FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind)
|
||||
FindAndMarkObjectsToClose(JSContext *cx, JSObjectsToClose *toClose)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSGCCloseListItem *todo, **itemp, *item;
|
||||
|
||||
rt = cx->runtime;
|
||||
void **array;
|
||||
JSObject *obj;
|
||||
size_t count, index, pivot;
|
||||
|
||||
/*
|
||||
* We delay the execution of close hooks for unreachable objects from
|
||||
* rt->gcCloseList when the current thread has not yet finished execution
|
||||
* of close hooks found during the previous GC cycle or when GC is the
|
||||
* last ditch.
|
||||
*
|
||||
* The former prevents an infinite loop via close hooks creating more
|
||||
* objects with close hooks.
|
||||
*
|
||||
* The latter is necessary to preserve the invariant that an allocation of
|
||||
* GC thing of one kind should not affect affect cx->newborn[anotherKind].
|
||||
* A close hook can violate it since it can allocate arbitrary things.
|
||||
*
|
||||
* Not running close hooks when GC is the last ditch also preserves the
|
||||
* assumption that such GC should keep all atoms. A recursive invocation
|
||||
* of GC from a close hook can break it.
|
||||
* Find unmarked objects reachable from rt->gcCloseTable and set them
|
||||
* aside at the end of the table. These are the objects to close.
|
||||
*/
|
||||
if (cx->gcRunningCloseHooks || gckind == GC_LAST_DITCH) {
|
||||
MarkCloseList(cx, rt->gcCloseList);
|
||||
} else {
|
||||
todo = NULL;
|
||||
itemp = &rt->gcCloseList;
|
||||
while ((item = *itemp) != NULL) {
|
||||
if (*js_GetGCThingFlags(item->object) & GCF_MARK) {
|
||||
GC_MARK(cx, item, "reachable close-phase list item");
|
||||
itemp = &item->next;
|
||||
} else {
|
||||
*itemp = item->next;
|
||||
item->next = todo;
|
||||
todo = item;
|
||||
METER(JS_ASSERT(rt->gcStats.nclose));
|
||||
METER(rt->gcStats.nclose--);
|
||||
}
|
||||
}
|
||||
|
||||
if (todo) {
|
||||
MarkCloseList(cx, todo);
|
||||
itemp = GetCloseListTail(&cx->gcObjectsToClose);
|
||||
*itemp = todo;
|
||||
#ifdef JS_GCMETER
|
||||
{
|
||||
uint32 toCloseSize = 0;
|
||||
|
||||
for (item = cx->gcObjectsToClose; item; item = item->next)
|
||||
toCloseSize++;
|
||||
if (toCloseSize > rt->gcStats.maxcloselater)
|
||||
rt->gcStats.maxcloselater = rt->gcStats.maxcloselater;
|
||||
}
|
||||
#endif
|
||||
rt = cx->runtime;
|
||||
array = rt->gcCloseTable.array;
|
||||
count = pivot = rt->gcCloseTable.count;
|
||||
index = 0;
|
||||
while (index != pivot) {
|
||||
obj = (JSObject *)array[index];
|
||||
if (*js_GetGCThingFlags(obj) & GCF_MARK) {
|
||||
++index;
|
||||
} else {
|
||||
array[index] = array[--pivot];
|
||||
array[pivot] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (pivot == count) {
|
||||
/* Skip the close phase when threre are no objects to close. */
|
||||
toClose->count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
toClose->count = count - pivot;
|
||||
toClose->startIndex = pivot;
|
||||
#ifdef DEBUG
|
||||
toClose->tableSnapshot = rt->gcCloseTable;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* First half of the close phase: loop over the objects that we set aside
|
||||
* in the close table and mark them to protect them against finalization
|
||||
* during sweeping phase.
|
||||
*/
|
||||
index = pivot;
|
||||
do {
|
||||
GC_MARK(cx, array[index], "close-phase object");
|
||||
} while (++index != count);
|
||||
|
||||
/*
|
||||
* Mark children of things that caused too deep recursion during the
|
||||
* just-completed marking half of the close phase.
|
||||
|
@ -1016,78 +979,56 @@ FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind)
|
|||
ScanDelayedChildren(cx);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
RunCloseHooks(JSContext *cx)
|
||||
static void
|
||||
ExecuteCloseHooks(JSContext *cx, const JSObjectsToClose *toClose)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSTempValueRooter tvr;
|
||||
JSStackFrame *fp;
|
||||
uint32 index, endIndex;
|
||||
JSObject *obj;
|
||||
JSExtendedClass *xclasp;
|
||||
JSBool ok;
|
||||
void **array;
|
||||
|
||||
/* Ignore recursive invocations. */
|
||||
if (cx->gcRunningCloseHooks)
|
||||
return JS_TRUE;
|
||||
|
||||
if (!cx->gcObjectsToClose)
|
||||
return JS_TRUE;
|
||||
|
||||
/* Prepare for execution of at least one close hook. */
|
||||
rt = cx->runtime;
|
||||
cx->gcRunningCloseHooks = JS_TRUE;
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
|
||||
JS_ASSERT(toClose->count > 0);
|
||||
|
||||
/* Close table manupulations are not allowed during the marking phase. */
|
||||
JS_ASSERT(memcmp(&toClose->tableSnapshot, &rt->gcCloseTable,
|
||||
sizeof toClose->tableSnapshot) == 0);
|
||||
|
||||
/*
|
||||
* Temporarily set aside cx->fp here to prevent the close hooks from
|
||||
* running on the GC's interpreter stack.
|
||||
* Execute the close hooks. Temporarily set aside cx->fp here to prevent
|
||||
* the close hooks from running on the GC's interpreter stack.
|
||||
*/
|
||||
rt->gcClosePhase = JS_TRUE;
|
||||
fp = cx->fp;
|
||||
cx->fp = NULL;
|
||||
|
||||
index = toClose->startIndex;
|
||||
endIndex = index + toClose->count;
|
||||
do {
|
||||
obj = cx->gcObjectsToClose->object;
|
||||
|
||||
/* Root obj before unlinking the item and executing the hook. */
|
||||
tvr.u.value = OBJECT_TO_JSVAL(obj);
|
||||
cx->gcObjectsToClose = cx->gcObjectsToClose->next;
|
||||
|
||||
xclasp = (JSExtendedClass *) OBJ_GET_CLASS(cx, obj);
|
||||
/*
|
||||
* Reload rt->gcCloseTable.array because close hooks may create
|
||||
* new objects that have close hooks and reallocate the table.
|
||||
*/
|
||||
obj = (JSObject *)rt->gcCloseTable.array[index];
|
||||
xclasp = (JSExtendedClass *) LOCKED_OBJ_GET_CLASS(obj);
|
||||
JS_ASSERT(xclasp->base.flags & JSCLASS_IS_EXTENDED);
|
||||
JS_ASSERT(xclasp->close);
|
||||
ok = xclasp->close(cx, obj);
|
||||
|
||||
/* One more object with a close hook becomes unreachable. */
|
||||
rt->gcPoke = JS_TRUE;
|
||||
if (cx->throwing) {
|
||||
/*
|
||||
* Report the exception thrown by the close hook and continue to
|
||||
* execute the rest of the hooks.
|
||||
*/
|
||||
if (!js_ReportUncaughtException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
ok = JS_TRUE;
|
||||
} else if (!ok) {
|
||||
/*
|
||||
* Assume this is a stop signal from the branch callback or other
|
||||
* quit ASAP condition. Break execution until the next invocation
|
||||
* of GC and put the rest of handlers back to the global list.
|
||||
*/
|
||||
if (cx->gcObjectsToClose) {
|
||||
JS_LOCK_GC(rt);
|
||||
*GetCloseListTail(&rt->gcCloseList) = cx->gcObjectsToClose;
|
||||
cx->gcObjectsToClose = NULL;
|
||||
JS_UNLOCK_GC(rt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (cx->gcObjectsToClose);
|
||||
xclasp->close(cx, obj);
|
||||
} while (++index != endIndex);
|
||||
|
||||
rt->gcClosePhase = JS_FALSE;
|
||||
cx->fp = fp;
|
||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||
cx->gcRunningCloseHooks = JS_FALSE;
|
||||
|
||||
return ok;
|
||||
/*
|
||||
* Move any added object pointers down over the span just
|
||||
* processed, and update the table's count.
|
||||
*/
|
||||
array = rt->gcCloseTable.array;
|
||||
memmove(array + toClose->startIndex, array + endIndex,
|
||||
(rt->gcCloseTable.count - endIndex) * sizeof array[0]);
|
||||
ShrinkPtrTable(&rt->gcCloseTable, &closeTableInfo,
|
||||
rt->gcCloseTable.count - toClose->count);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_brendan) || defined(DEBUG_timeless)
|
||||
|
@ -1158,8 +1099,8 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
|
|||
rt->gcMallocBytes += localMallocBytes;
|
||||
}
|
||||
#endif
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
if (rt->gcRunning) {
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
if (rt->gcRunning && !rt->gcClosePhase) {
|
||||
METER(rt->gcStats.finalfail++);
|
||||
JS_UNLOCK_GC(rt);
|
||||
return NULL;
|
||||
|
@ -1176,7 +1117,7 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
|
|||
|
||||
arenaList = &rt->gcArenaList[flindex];
|
||||
for (;;) {
|
||||
if (doGC) {
|
||||
if (doGC && !rt->gcClosePhase) {
|
||||
/*
|
||||
* Keep rt->gcLock across the call into js_GC so we don't starve
|
||||
* and lose to racing threads who deplete the heap just after
|
||||
|
@ -1185,7 +1126,7 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
|
|||
* can happen on certain operating systems. For the gory details,
|
||||
* see bug 162779 at https://bugzilla.mozilla.org/.
|
||||
*/
|
||||
if (!js_GC(cx, GC_LAST_DITCH)) {
|
||||
if (!js_GC(cx, GC_KEEP_ATOMS | GC_LAST_DITCH)) {
|
||||
/*
|
||||
* js_GC ensures that GC is unlocked when the branch callback
|
||||
* wants to stop execution, and in this case we do not report
|
||||
|
@ -2298,16 +2239,16 @@ gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg)
|
|||
}
|
||||
|
||||
void
|
||||
js_ForceGC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
js_ForceGC(JSContext *cx, uintN gcflags)
|
||||
{
|
||||
uintN i;
|
||||
|
||||
JS_ASSERT(gckind == GC_NORMAL || gckind == GC_LAST_CONTEXT);
|
||||
JS_ASSERT((gcflags & ~(GC_KEEP_ATOMS | GC_LAST_CONTEXT)) == 0);
|
||||
for (i = 0; i < GCX_NTYPES; i++)
|
||||
cx->newborn[i] = NULL;
|
||||
cx->lastAtom = NULL;
|
||||
cx->runtime->gcPoke = JS_TRUE;
|
||||
js_GC(cx, gckind);
|
||||
js_GC(cx, gcflags);
|
||||
JS_ArenaFinish();
|
||||
}
|
||||
|
||||
|
@ -2407,30 +2348,30 @@ js_MarkStackFrame(JSContext *cx, JSStackFrame *fp)
|
|||
* Return false when the branch callback wants to stop exeution ASAP and true
|
||||
* otherwise.
|
||||
*
|
||||
* When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with
|
||||
* rt->gcLock already held. On return to js_NewGCThing the lock is kept when
|
||||
* js_GC returns false and released when it returns true. This asymmetry helps
|
||||
* avoid re-taking the lock just to release it immediately in js_NewGCThing
|
||||
* when the branch callback called outside the lock cancels the allocation and
|
||||
* js_GC returns false. See bug 341896.
|
||||
* When gcflags contains GC_LAST_DITCH, it indicates a call from js_NewGCThing
|
||||
* with rt->gcLock already held. On return to js_NewGCThing the lock is kept
|
||||
* when js_GC returns false and released when it returns true. This asymmetry
|
||||
* helps avoid re-taking the lock just to release it immediately in
|
||||
* js_NewGCThing when the branch callback called outside the lock cancels the
|
||||
* allocation and js_GC returns false. See bug 341896.
|
||||
*/
|
||||
JSBool
|
||||
js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
js_GC(JSContext *cx, uintN gcflags)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSBool keepAtoms;
|
||||
JSContext *iter, *acx;
|
||||
JSStackFrame *fp, *chain;
|
||||
uintN i, type;
|
||||
JSStackHeader *sh;
|
||||
JSTempValueRooter *tvr;
|
||||
JSObjectsToClose objectsToClose;
|
||||
size_t nbytes, limit, offset;
|
||||
JSGCArena *a, **ap;
|
||||
uint8 flags, *flagp, *firstPage;
|
||||
JSGCThing *thing, *freeList;
|
||||
JSGCArenaList *arenaList;
|
||||
GCFinalizeOp finalizer;
|
||||
JSBool allClear, checkBranchCallback;
|
||||
JSBool allClear, shouldRestart, checkBranchCallback;
|
||||
#ifdef JS_THREADSAFE
|
||||
uint32 requestDebit;
|
||||
#endif
|
||||
|
@ -2447,7 +2388,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
* should suppress that final collection or there may be shutdown leaks,
|
||||
* or runtime bloat until the next context is created.
|
||||
*/
|
||||
if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT)
|
||||
if (rt->state != JSRTS_UP && !(gcflags & GC_LAST_CONTEXT))
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
|
@ -2456,18 +2397,18 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
*/
|
||||
if (rt->gcCallback &&
|
||||
!rt->gcCallback(cx, JSGC_BEGIN) &&
|
||||
gckind != GC_LAST_CONTEXT) {
|
||||
!(gcflags & GC_LAST_CONTEXT)) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Lock out other GC allocator and collector invocations. */
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
JS_LOCK_GC(rt);
|
||||
|
||||
/* Do nothing if no mutator has executed since the last GC. */
|
||||
if (!rt->gcPoke) {
|
||||
METER(rt->gcStats.nopoke++);
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -2481,7 +2422,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
rt->gcLevel++;
|
||||
METER(if (rt->gcLevel > rt->gcStats.maxlevel)
|
||||
rt->gcStats.maxlevel = rt->gcLevel);
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -2538,7 +2479,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
JS_AWAIT_GC_DONE(rt);
|
||||
if (requestDebit)
|
||||
rt->requestCount += requestDebit;
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -2574,11 +2515,9 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
rt->gcRunning = JS_TRUE;
|
||||
JS_UNLOCK_GC(rt);
|
||||
|
||||
/*
|
||||
* Keep atoms if a suspended compile is running on another context or we
|
||||
* are doing the last ditch GC.
|
||||
*/
|
||||
keepAtoms = rt->gcKeepAtoms != 0 || gckind == GC_LAST_DITCH;
|
||||
/* If a suspended compile is running on another context, keep atoms. */
|
||||
if (rt->gcKeepAtoms)
|
||||
gcflags |= GC_KEEP_ATOMS;
|
||||
|
||||
/* Reset malloc counter. */
|
||||
rt->gcMallocBytes = 0;
|
||||
|
@ -2618,9 +2557,9 @@ restart:
|
|||
JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx);
|
||||
if (rt->gcLocksHash)
|
||||
JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx);
|
||||
js_MarkAtomState(&rt->atomState, keepAtoms, gc_mark_atom_key_thing, cx);
|
||||
js_MarkAtomState(&rt->atomState, gcflags, gc_mark_atom_key_thing, cx);
|
||||
js_MarkWatchPoints(rt);
|
||||
js_MarkScriptFilenames(rt, keepAtoms);
|
||||
js_MarkScriptFilenames(rt, gcflags);
|
||||
js_MarkNativeIteratorStates(cx);
|
||||
|
||||
iter = NULL;
|
||||
|
@ -2691,11 +2630,6 @@ restart:
|
|||
js_GCMarkSharpMap(cx, &acx->sharpObjectMap);
|
||||
|
||||
acx->cachedIterObj = NULL;
|
||||
|
||||
/*
|
||||
* Mark objects with close hooks that some thread is actively closing.
|
||||
*/
|
||||
MarkCloseList(cx, acx->gcObjectsToClose);
|
||||
}
|
||||
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
|
@ -2712,11 +2646,14 @@ restart:
|
|||
* Close phase: search and mark part.
|
||||
*
|
||||
* We find all unreachable objects with close hooks and move them to a
|
||||
* private work-list to execute the close hooks after the GC cycle
|
||||
* completes. To ensure liveness during the sweep phase we mark all
|
||||
* objects we are about to close.
|
||||
* private work-list to execute the close hooks after the sweep phase.
|
||||
* To ensure liveness during the sweep phase we mark all objects we are
|
||||
* about to close.
|
||||
*/
|
||||
FindAndMarkObjectsToClose(cx, gckind);
|
||||
#ifdef __GNUC__ /* suppress a bogus gcc warning */
|
||||
objectsToClose.startIndex = 0;
|
||||
#endif
|
||||
FindAndMarkObjectsToClose(cx, &objectsToClose);
|
||||
|
||||
JS_ASSERT(!cx->insideGCMarkCallback);
|
||||
if (rt->gcCallback) {
|
||||
|
@ -2737,7 +2674,7 @@ restart:
|
|||
* so that any attempt to allocate a GC-thing from a finalizer will fail,
|
||||
* rather than nest badly and leave the unmarked newborn to be swept.
|
||||
*/
|
||||
js_SweepAtomState(&rt->atomState, keepAtoms);
|
||||
js_SweepAtomState(&rt->atomState);
|
||||
js_SweepScopeProperties(rt);
|
||||
|
||||
/*
|
||||
|
@ -2791,7 +2728,7 @@ restart:
|
|||
* triggering a call to rt->destroyScriptHook, the hook can still access
|
||||
* script's filename. See bug 323267.
|
||||
*/
|
||||
js_SweepScriptFilenames(rt, keepAtoms);
|
||||
js_SweepScriptFilenames(rt);
|
||||
|
||||
/*
|
||||
* Free phase.
|
||||
|
@ -2863,13 +2800,36 @@ restart:
|
|||
}
|
||||
#endif
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
/*
|
||||
* We want to restart GC if any of the finalizers called js_RemoveRoot or
|
||||
* js_UnlockGCThingRT.
|
||||
*/
|
||||
shouldRestart = rt->gcPoke;
|
||||
|
||||
/*
|
||||
* We want to restart GC if js_GC was called recursively or if any of the
|
||||
* finalizers called js_RemoveRoot or js_UnlockGCThingRT.
|
||||
* Close phase: execution part.
|
||||
*
|
||||
* The GC allocator works when called during execution of a close hook
|
||||
* but if it exhausts the malloc heap, it will fail without trying a
|
||||
* last-ditch GC.
|
||||
*/
|
||||
if (rt->gcLevel > 1 || rt->gcPoke) {
|
||||
if (objectsToClose.count != 0) {
|
||||
ExecuteCloseHooks(cx, &objectsToClose);
|
||||
|
||||
/*
|
||||
* On the last destroy context restart GC to collect just closed
|
||||
* objects. This does not cause infinite loops with close hooks
|
||||
* creating more closeable objects since we do not allow installing
|
||||
* close hooks during the shutdown of runtime.
|
||||
*
|
||||
* See bug 340889 and bug 341675.
|
||||
*/
|
||||
if (gcflags & GC_LAST_CONTEXT)
|
||||
shouldRestart = JS_TRUE;
|
||||
}
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
if (rt->gcLevel > 1 || shouldRestart) {
|
||||
rt->gcLevel = 1;
|
||||
rt->gcPoke = JS_FALSE;
|
||||
JS_UNLOCK_GC(rt);
|
||||
|
@ -2891,17 +2851,18 @@ restart:
|
|||
* Unlock unless we have GC_LAST_DITCH which requires locked GC on return
|
||||
* after a successful GC cycle.
|
||||
*/
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
JS_UNLOCK_GC(rt);
|
||||
#endif
|
||||
|
||||
checkBranchCallback = gckind == GC_LAST_DITCH &&
|
||||
checkBranchCallback = (gcflags & GC_LAST_DITCH) &&
|
||||
JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
|
||||
cx->branchCallback;
|
||||
|
||||
/* Execute JSGC_END callback and branch callback outside the lock. */
|
||||
if (rt->gcCallback || checkBranchCallback) {
|
||||
if (gckind == GC_LAST_DITCH)
|
||||
|
||||
/* Execute JSGC_END and branch callbacks outside the lock. */
|
||||
if (gcflags & GC_LAST_DITCH)
|
||||
JS_UNLOCK_GC(rt);
|
||||
if (rt->gcCallback)
|
||||
(void) rt->gcCallback(cx, JSGC_END);
|
||||
|
@ -2913,19 +2874,10 @@ restart:
|
|||
/* Keep GC unlocked when canceled. */
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (gckind == GC_LAST_DITCH)
|
||||
if (gcflags & GC_LAST_DITCH)
|
||||
JS_LOCK_GC(rt);
|
||||
}
|
||||
|
||||
/*
|
||||
* See FindAndMarkObjectsToClose for comments about the check for the last
|
||||
* ditch GC here.
|
||||
*/
|
||||
if (gckind != GC_LAST_DITCH && !RunCloseHooks(cx)) {
|
||||
METER(rt->gcStats.retryhalt++);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,20 +140,8 @@ typedef struct JSPtrTable {
|
|||
extern JSBool
|
||||
js_RegisterCloseableIterator(JSContext *cx, JSObject *obj);
|
||||
|
||||
typedef struct JSCloseLink JSCloseLink;
|
||||
|
||||
/*
|
||||
* Structure to track an object of extended class with a non-null close hook.
|
||||
*/
|
||||
typedef struct JSGCCloseListItem JSGCCloseListItem;
|
||||
|
||||
struct JSGCCloseListItem {
|
||||
JSGCCloseListItem *next;
|
||||
JSObject *object;
|
||||
};
|
||||
|
||||
extern JSBool
|
||||
js_RegisterObjectWithCloseHook(JSContext *cx, JSObject *obj);
|
||||
js_AddObjectToCloseTable(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* The private JSGCThing struct, which describes a gcFreeList element.
|
||||
|
@ -218,35 +206,29 @@ extern void
|
|||
js_MarkStackFrame(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
/*
|
||||
* Kinds of js_GC invocation.
|
||||
* Flags to modify how a GC marks and sweeps:
|
||||
* GC_KEEP_ATOMS Don't sweep unmarked atoms, they may be in use by the
|
||||
* compiler, or by an API function that calls js_Atomize,
|
||||
* when the GC is called from js_NewGCThing, due to a
|
||||
* malloc failure or the runtime GC-thing limit.
|
||||
* GC_LAST_CONTEXT Called from js_DestroyContext for last JSContext in a
|
||||
* JSRuntime, when it is imperative that rt->gcPoke gets
|
||||
* cleared early in js_GC, if it is set.
|
||||
* GC_LAST_DITCH Called from js_NewGCThing as a last-ditch GC attempt.
|
||||
* See comments before js_GC definition for details.
|
||||
*/
|
||||
typedef enum JSGCInvocationKind {
|
||||
|
||||
/* Normal invocation. */
|
||||
GC_NORMAL,
|
||||
|
||||
/*
|
||||
* Called from js_DestroyContext for last JSContext in a JSRuntime, when
|
||||
* it is imperative that rt->gcPoke gets cleared early in js_GC.
|
||||
*/
|
||||
GC_LAST_CONTEXT,
|
||||
|
||||
/*
|
||||
* Called from js_NewGCThing as a last-ditch GC attempt. See comments
|
||||
* before js_GC definition for details.
|
||||
*/
|
||||
GC_LAST_DITCH
|
||||
|
||||
} JSGCInvocationKind;
|
||||
#define GC_KEEP_ATOMS 0x1
|
||||
#define GC_LAST_CONTEXT 0x2
|
||||
#define GC_LAST_DITCH 0x4
|
||||
|
||||
extern void
|
||||
js_ForceGC(JSContext *cx, JSGCInvocationKind gckind);
|
||||
js_ForceGC(JSContext *cx, uintN gcflags);
|
||||
|
||||
/*
|
||||
* Return false when the branch callback cancels GC and true otherwise.
|
||||
*/
|
||||
extern JSBool
|
||||
js_GC(JSContext *cx, JSGCInvocationKind gckind);
|
||||
js_GC(JSContext *cx, uintN gcflags);
|
||||
|
||||
/* Call this after succesful malloc of memory for GC-related things. */
|
||||
extern void
|
||||
|
@ -285,9 +267,6 @@ typedef struct JSGCStats {
|
|||
uint32 afree; /* thing arenas freed so far */
|
||||
uint32 stackseg; /* total extraordinary stack segments scanned */
|
||||
uint32 segslots; /* total stack segment jsval slots scanned */
|
||||
uint32 nclose; /* number of objects with close hooks */
|
||||
uint32 maxnclose; /* max number of objects with close hooks */
|
||||
uint32 maxcloselater; /* max number of close hooks scheduled to run */
|
||||
} JSGCStats;
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
|
|
|
@ -624,7 +624,7 @@ typedef struct JSGenerator {
|
|||
jsval stack[1];
|
||||
} JSGenerator;
|
||||
|
||||
static JSBool
|
||||
static void
|
||||
generator_closehook(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSGenerator *gen;
|
||||
|
@ -632,10 +632,17 @@ generator_closehook(JSContext *cx, JSObject *obj)
|
|||
const jsid id = ATOM_TO_JSID(cx->runtime->atomState.closeAtom);
|
||||
|
||||
gen = (JSGenerator *) JS_GetPrivate(cx, obj);
|
||||
if (!gen || !JS_GetMethodById(cx, obj, id, &obj, &fval))
|
||||
return JS_TRUE;
|
||||
if (!gen)
|
||||
return;
|
||||
|
||||
return js_InternalCall(cx, obj, fval, 0, NULL, &rval);
|
||||
/*
|
||||
* Ignore errors until after we call the close method, then force prompt
|
||||
* error reporting, since GC is infallible.
|
||||
*/
|
||||
if (JS_GetMethodById(cx, obj, id, &obj, &fval))
|
||||
js_InternalCall(cx, obj, fval, 0, NULL, &rval);
|
||||
if (cx->throwing && !js_ReportUncaughtException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -2245,7 +2245,7 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
|||
/* If obj needs to be closed before being finalized, remember it. */
|
||||
if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
|
||||
((JSExtendedClass *)clasp)->close &&
|
||||
!js_RegisterObjectWithCloseHook(cx, obj)) {
|
||||
!js_AddObjectToCloseTable(cx, obj)) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
|
|
|
@ -257,20 +257,13 @@ typedef JSBool
|
|||
* Finalize obj, which the garbage collector has determined to be unreachable
|
||||
* from other live objects or from GC roots. Obviously, finalizers must never
|
||||
* store a reference to obj.
|
||||
*
|
||||
* This is also the type of the JSExtendedClass.close hook, which is stubbed
|
||||
* with NULL if not needed.
|
||||
*/
|
||||
typedef void
|
||||
(* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Close hook for obj where obj has extended class. When non-null, it is
|
||||
* executed after the garbage collector has determined the object to be
|
||||
* unreachable from other live objects or from GC roots. The hook can execute
|
||||
* arbitrary code. GC runs the hook only once even if it makes the object
|
||||
* reachable again.
|
||||
*/
|
||||
typedef JSBool
|
||||
(* JS_DLL_CALLBACK JSCloseOp)(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer
|
||||
* to extend and reduce the set of string types finalized by the GC.
|
||||
|
|
|
@ -1195,15 +1195,29 @@ js_MarkScriptFilename(const char *filename)
|
|||
sfe->mark = JS_TRUE;
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(intN)
|
||||
js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
|
||||
{
|
||||
ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
|
||||
|
||||
sfe->mark = JS_TRUE;
|
||||
return HT_ENUMERATE_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
|
||||
js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags)
|
||||
{
|
||||
JSCList *head, *link;
|
||||
ScriptFilenamePrefix *sfp;
|
||||
|
||||
if (!rt->scriptFilenameTable || keepAtoms)
|
||||
if (!rt->scriptFilenameTable)
|
||||
return;
|
||||
|
||||
if (gcflags & GC_KEEP_ATOMS) {
|
||||
JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
|
||||
js_script_filename_marker,
|
||||
rt);
|
||||
}
|
||||
for (head = &rt->scriptFilenamePrefixes, link = head->next;
|
||||
link != head;
|
||||
link = link->next) {
|
||||
|
@ -1224,9 +1238,9 @@ js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
|
|||
}
|
||||
|
||||
void
|
||||
js_SweepScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
|
||||
js_SweepScriptFilenames(JSRuntime *rt)
|
||||
{
|
||||
if (!rt->scriptFilenameTable || keepAtoms)
|
||||
if (!rt->scriptFilenameTable)
|
||||
return;
|
||||
|
||||
JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
|
||||
|
|
|
@ -139,10 +139,10 @@ extern void
|
|||
js_MarkScriptFilename(const char *filename);
|
||||
|
||||
extern void
|
||||
js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms);
|
||||
js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags);
|
||||
|
||||
extern void
|
||||
js_SweepScriptFilenames(JSRuntime *rt, JSBool keepAtoms);
|
||||
js_SweepScriptFilenames(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* Two successively less primitive ways to make a new JSScript. The first
|
||||
|
|
Загрузка…
Ссылка в новой задаче