Bug 341821: removing the previous commit, the patch was broken.

This commit is contained in:
igor.bukanov%gmail.com 2006-07-28 13:23:09 +00:00
Родитель b7c80c717d
Коммит 63d75e09ea
11 изменённых файлов: 239 добавлений и 305 удалений

Просмотреть файл

@ -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)

Просмотреть файл

@ -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