author | Gregor Wagner <anygregor@gmail.com> |
Wed, 23 Jun 2010 11:37:35 -0500 | |
changeset 47494 | fbf9ceffee0609bae569b8fc4c9d576492861510 |
parent 47493 | 03876a6eba5a5046c5cf8c60aa1e0e47c05ddf27 |
child 47495 | dd2e6dc2699ec59e90b528185429c68813f6eecf |
push id | 14372 |
push user | rsayre@mozilla.com |
push date | Wed, 14 Jul 2010 13:02:13 +0000 |
treeherder | mozilla-central@c26c255bade9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | igor |
bugs | 519949 |
milestone | 1.9.3a6pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/jsapi.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsapi.h | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.h | file | annotate | diff | comparison | revisions | |
js/src/jsgc.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsinterp.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsxml.cpp | file | annotate | diff | comparison | revisions |
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1920,44 +1920,16 @@ JS_RemoveGCThingRoot(JSContext *cx, void } JS_PUBLIC_API(void) JS_ClearNewbornRoots(JSContext *cx) { JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); } -JS_PUBLIC_API(JSBool) -JS_EnterLocalRootScope(JSContext *cx) -{ - CHECK_REQUEST(cx); - return js_EnterLocalRootScope(cx); -} - -JS_PUBLIC_API(void) -JS_LeaveLocalRootScope(JSContext *cx) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScope(cx); -} - -JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScopeWithResult(cx, rval); -} - -JS_PUBLIC_API(void) -JS_ForgetLocalRoot(JSContext *cx, void *thing) -{ - CHECK_REQUEST(cx); - js_ForgetLocalRoot(cx, (jsval) thing); -} - #ifdef DEBUG JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data) { js_DumpNamedRoots(rt, dump, data);
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -989,126 +989,24 @@ extern JS_FRIEND_API(JSBool) js_RemoveRoot(JSRuntime *rt, void *rp); /* * This symbol may be used by embedders to detect the change from the old * JS_AddRoot(JSContext *, void *) APIs to the new ones above. */ #define JS_TYPED_ROOTING_API -/* - * The last GC thing of each type (object, string, double, external string - * types) created on a given context is kept alive until another thing of the - * same type is created, using a newborn root in the context. These newborn - * roots help native code protect newly-created GC-things from GC invocations - * activated before those things can be rooted using local or global roots. - * - * However, the newborn roots can also entrain great gobs of garbage, so the - * JS_GC entry point clears them for the context on which GC is being forced. - * Embeddings may need to do likewise for all contexts. - * - * See the scoped local root API immediately below for a better way to manage - * newborns in cases where native hooks (functions, getters, setters, etc.) - * create many GC-things, potentially without connecting them to predefined - * local roots such as *rval or argv[i] in an active native function. Using - * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type - * newborn roots, until control flow unwinds and leaves the outermost nesting - * local root scope. - */ extern JS_PUBLIC_API(void) JS_ClearNewbornRoots(JSContext *cx); -/* - * Scoped local root management allows native functions, getter/setters, etc. - * to avoid worrying about the newborn root pigeon-holes, overloading local - * roots allocated in argv and *rval, or ending up having to call JS_Add*Root - * and JS_Remove*Root to manage global roots temporarily. - * - * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around - * the body of the native hook causes the engine to allocate a local root for - * each newborn created in between the two API calls, using a local root stack - * associated with cx. For example: - * - * JSBool - * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) - * { - * JSBool ok; - * - * if (!JS_EnterLocalRootScope(cx)) - * return JS_FALSE; - * ok = my_GetPropertyBody(cx, obj, id, vp); - * JS_LeaveLocalRootScope(cx); - * return ok; - * } - * - * NB: JS_LeaveLocalRootScope must be called once for every prior successful - * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must - * not make the matching JS_LeaveLocalRootScope call. - * - * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave - * a local root scope that protects a result or return value, by effectively - * pushing it in the caller's local root scope. - * - * In case a native hook allocates many objects or other GC-things, but the - * native protects some of those GC-things by storing them as property values - * in an object that is itself protected, the hook can call JS_ForgetLocalRoot - * to free the local root automatically pushed for the now-protected GC-thing. - * - * JS_ForgetLocalRoot works on any GC-thing allocated in the current local - * root scope, but it's more time-efficient when called on references to more - * recently created GC-things. Calling it successively on other than the most - * recently allocated GC-thing will tend to average the time inefficiency, and - * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too - * many local roots if you can root as you go (build a tree of objects from - * the top down, forgetting each latest-allocated GC-thing immediately upon - * linking it to its parent). - */ -extern JS_PUBLIC_API(JSBool) -JS_EnterLocalRootScope(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScope(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - -extern JS_PUBLIC_API(void) -JS_ForgetLocalRoot(JSContext *cx, void *thing); - -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoLocalRootScope { - public: - JSAutoLocalRootScope(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) - : mContext(cx) { - JS_GUARD_OBJECT_NOTIFIER_INIT; - JS_EnterLocalRootScope(mContext); - } - ~JSAutoLocalRootScope() { - JS_LeaveLocalRootScope(mContext); - } - - void forget(void *thing) { - JS_ForgetLocalRoot(mContext, thing); - } - - protected: - JSContext *mContext; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif +/* Obsolete rooting APIs. */ +#define JS_EnterLocalRootScope(cx) (true) +#define JS_LeaveLocalRootScope(cx) ((void) 0) +#define JS_LeaveLocalRootScopeWithResult(cx, rval) ((void) 0) +#define JS_ForgetLocalRoot(cx, thing) ((void) 0) #ifdef DEBUG extern JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data); #endif
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -99,19 +99,16 @@ using namespace js; static const size_t ARENA_HEADER_SIZE_HACK = 40; static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK; static void FreeContext(JSContext *cx); -static void -MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs); - #ifdef DEBUG JS_REQUIRES_STACK bool CallStack::contains(const JSStackFrame *fp) const { JS_ASSERT(inContext()); JSStackFrame *start; JSStackFrame *stop; if (isSuspended()) { @@ -533,17 +530,16 @@ JSThreadData::init() void JSThreadData::finish() { #ifdef DEBUG /* All GC-related things must be already removed at this point. */ JS_ASSERT(gcFreeLists.isEmpty()); for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) JS_ASSERT(!scriptsToGC[i]); - JS_ASSERT(!localRootStack); JS_ASSERT(!conservativeGC.isEnabled()); #endif if (dtoaState) js_DestroyDtoaState(dtoaState); js_FinishGSNCache(&gsnCache); propertyCache.~PropertyCache(); @@ -555,24 +551,22 @@ JSThreadData::finish() void JSThreadData::mark(JSTracer *trc) { stackSpace.mark(trc); #ifdef JS_TRACER traceMonitor.mark(trc); #endif - if (localRootStack) - MarkLocalRoots(trc, localRootStack); } void JSThreadData::purge(JSContext *cx) { - purgeGCFreeLists(); + gcFreeLists.purge(); js_PurgeGSNCache(&gsnCache); /* FIXME: bug 506341. */ propertyCache.purge(cx); #ifdef JS_TRACER /* @@ -587,27 +581,16 @@ JSThreadData::purge(JSContext *cx) js_DestroyScriptsToGC(cx, this); /* Purge cached native iterators. */ memset(cachedNativeIterators, 0, sizeof(cachedNativeIterators)); dtoaCache.s = NULL; } -void -JSThreadData::purgeGCFreeLists() -{ - if (!localRootStack) { - gcFreeLists.purge(); - } else { - JS_ASSERT(gcFreeLists.isEmpty()); - localRootStack->gcFreeLists.purge(); - } -} - #ifdef JS_THREADSAFE static JSThread * NewThread(void *id) { JS_ASSERT(js_CurrentThreadId() == id); JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread)); if (!thread) @@ -746,17 +729,17 @@ js_PurgeThreads(JSContext *cx) if (JS_CLIST_IS_EMPTY(&thread->contextList)) { JS_ASSERT(cx->thread != thread); js_DestroyScriptsToGC(cx, &thread->data); /* * The following is potentially suboptimal as it also zeros the * caches in data, but the code simplicity wins here. */ - thread->data.purgeGCFreeLists(); + thread->data.gcFreeLists.purge(); DestroyThread(thread); e.removeFront(); } else { thread->data.purge(cx); thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT; } } #else @@ -1357,246 +1340,16 @@ js_StopResolving(JSContext *cx, JSResolv * compressing or shrinking the table as needed. */ if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) JS_DHashTableRawRemove(table, &entry->hdr); else JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); } -JSBool -js_EnterLocalRootScope(JSContext *cx) -{ - JSThreadData *td = JS_THREAD_DATA(cx); - JSLocalRootStack *lrs = td->localRootStack; - if (!lrs) { - lrs = (JSLocalRootStack *) js_malloc(sizeof *lrs); - if (!lrs) { - js_ReportOutOfMemory(cx); - return false; - } - lrs->scopeMark = JSLRS_NULL_MARK; - lrs->rootCount = 0; - lrs->topChunk = &lrs->firstChunk; - lrs->firstChunk.down = NULL; - td->gcFreeLists.moveTo(&lrs->gcFreeLists); - td->localRootStack = lrs; - } - - /* Push lrs->scopeMark to save it for restore when leaving. */ - int mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); - if (mark < 0) - return JS_FALSE; - lrs->scopeMark = (uint32) mark; - return true; -} - -void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - JSLocalRootStack *lrs; - uint32 mark, m, n; - JSLocalRootChunk *lrc; - - /* Defend against buggy native callers. */ - lrs = JS_THREAD_DATA(cx)->localRootStack; - JS_ASSERT(lrs && lrs->rootCount != 0); - if (!lrs || lrs->rootCount == 0) - return; - - mark = lrs->scopeMark; - JS_ASSERT(mark != JSLRS_NULL_MARK); - if (mark == JSLRS_NULL_MARK) - return; - - /* Free any chunks being popped by this leave operation. */ - m = mark >> JSLRS_CHUNK_SHIFT; - n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT; - while (n > m) { - lrc = lrs->topChunk; - JS_ASSERT(lrc != &lrs->firstChunk); - lrs->topChunk = lrc->down; - js_free(lrc); - --n; - } - - /* - * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push - * it on the caller's scope, or store it in lastInternalResult if we are - * leaving the outermost scope. We don't need to allocate a new lrc - * because we can overwrite the old mark's slot with rval. - */ - lrc = lrs->topChunk; - m = mark & JSLRS_CHUNK_MASK; - lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); - if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) { - if (mark == 0) { - cx->weakRoots.lastInternalResult = rval; - } else { - /* - * Increment m to avoid the "else if (m == 0)" case below. If - * rval is not a GC-thing, that case would take care of freeing - * any chunk that contained only the old mark. Since rval *is* - * a GC-thing here, we want to reuse that old mark's slot. - */ - lrc->roots[m++] = rval; - ++mark; - } - } - lrs->rootCount = (uint32) mark; - - /* - * Free the stack eagerly, risking malloc churn. The alternative would - * require an lrs->entryCount member, maintained by Enter and Leave, and - * tested by the GC in addition to the cx->localRootStack non-null test. - * - * That approach would risk hoarding 264 bytes (net) per context. Right - * now it seems better to give fresh (dirty in CPU write-back cache, and - * the data is no longer needed) memory back to the malloc heap. - */ - if (mark == 0) { - JSThreadData *td = JS_THREAD_DATA(cx); - JS_ASSERT(td->gcFreeLists.isEmpty()); - lrs->gcFreeLists.moveTo(&td->gcFreeLists); - td->localRootStack = NULL; - js_free(lrs); - } else if (m == 0) { - lrs->topChunk = lrc->down; - js_free(lrc); - } -} - -void -js_ForgetLocalRoot(JSContext *cx, jsval v) -{ - JSLocalRootStack *lrs; - uint32 i, j, m, n, mark; - JSLocalRootChunk *lrc, *lrc2; - jsval top; - - lrs = JS_THREAD_DATA(cx)->localRootStack; - JS_ASSERT(lrs && lrs->rootCount); - if (!lrs || lrs->rootCount == 0) - return; - - /* Prepare to pop the top-most value from the stack. */ - n = lrs->rootCount - 1; - m = n & JSLRS_CHUNK_MASK; - lrc = lrs->topChunk; - top = lrc->roots[m]; - - /* Be paranoid about calls on an empty scope. */ - mark = lrs->scopeMark; - JS_ASSERT(mark < n); - if (mark >= n) - return; - - /* If v was not the last root pushed in the top scope, find it. */ - if (top != v) { - /* Search downward in case v was recently pushed. */ - i = n; - j = m; - lrc2 = lrc; - while (--i > mark) { - if (j == 0) - lrc2 = lrc2->down; - j = i & JSLRS_CHUNK_MASK; - if (lrc2->roots[j] == v) - break; - } - - /* If we didn't find v in this scope, assert and bail out. */ - JS_ASSERT(i != mark); - if (i == mark) - return; - - /* Swap top and v so common tail code can pop v. */ - lrc2->roots[j] = top; - } - - /* Pop the last value from the stack. */ - lrc->roots[m] = JSVAL_NULL; - lrs->rootCount = n; - if (m == 0) { - JS_ASSERT(n != 0); - JS_ASSERT(lrc != &lrs->firstChunk); - lrs->topChunk = lrc->down; - cx->free(lrc); - } -} - -int -js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) -{ - uint32 n, m; - JSLocalRootChunk *lrc; - - n = lrs->rootCount; - m = n & JSLRS_CHUNK_MASK; - if (n == 0 || m != 0) { - /* - * At start of first chunk, or not at start of a non-first top chunk. - * Check for lrs->rootCount overflow. - */ - if ((uint32)(n + 1) == 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_LOCAL_ROOTS); - return -1; - } - lrc = lrs->topChunk; - JS_ASSERT(n != 0 || lrc == &lrs->firstChunk); - } else { - /* - * After lrs->firstChunk, trying to index at a power-of-two chunk - * boundary: need a new chunk. - */ - lrc = (JSLocalRootChunk *) js_malloc(sizeof *lrc); - if (!lrc) { - js_ReportOutOfMemory(cx); - return -1; - } - lrc->down = lrs->topChunk; - lrs->topChunk = lrc; - } - lrs->rootCount = n + 1; - lrc->roots[m] = v; - return (int) n; -} - -static void -MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs) -{ - uint32 n, m, mark; - JSLocalRootChunk *lrc; - jsval v; - - n = lrs->rootCount; - if (n == 0) - return; - - mark = lrs->scopeMark; - lrc = lrs->topChunk; - do { - while (--n > mark) { - m = n & JSLRS_CHUNK_MASK; - v = lrc->roots[m]; - JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL); - JS_SET_TRACING_INDEX(trc, "local_root", n); - js_CallValueTracerIfGCThing(trc, v); - if (m == 0) - lrc = lrc->down; - } - m = n & JSLRS_CHUNK_MASK; - mark = JSVAL_TO_INT(lrc->roots[m]); - if (m == 0) - lrc = lrc->down; - } while (n != 0); - JS_ASSERT(!lrc); -} - static void ReportError(JSContext *cx, const char *message, JSErrorReport *reportp, JSErrorCallback callback, void *userRef) { /* * Check the error report, and set a JavaScript-catchable exception * if the error is defined to have an associated exception. If an * exception is thrown, then the JSREPORT_EXCEPTION flag will be set @@ -2433,17 +2186,17 @@ JSContext::checkMallocGCPressure(void *p /* * Empty the GC free lists to trigger a last-ditch GC when any GC * thing is allocated later on this thread. This makes unnecessary * to check for the memory pressure on the fast path of the GC * allocator. We cannot touch the free lists on other threads as * their manipulation is not thread-safe. */ - JS_THREAD_DATA(this)->purgeGCFreeLists(); + JS_THREAD_DATA(this)->gcFreeLists.purge(); js_TriggerGC(this, true); } } } bool JSContext::isConstructing() {
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -952,39 +952,16 @@ struct JSEvalCacheMeter { struct JSFunctionMeter { int32 FUNCTION_KIND_METER_LIST(identity); }; # undef identity #endif -struct JSLocalRootChunk; - -#define JSLRS_CHUNK_SHIFT 8 -#define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) -#define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) - -struct JSLocalRootChunk { - jsval roots[JSLRS_CHUNK_SIZE]; - JSLocalRootChunk *down; -}; - -struct JSLocalRootStack { - uint32 scopeMark; - uint32 rootCount; - JSLocalRootChunk *topChunk; - JSLocalRootChunk firstChunk; - - /* See comments in js_NewFinalizableGCThing. */ - JSGCFreeLists gcFreeLists; -}; - -const uint32 JSLRS_NULL_MARK = uint32(-1); - #define NATIVE_ITER_CACHE_LOG2 8 #define NATIVE_ITER_CACHE_MASK JS_BITMASK(NATIVE_ITER_CACHE_LOG2) #define NATIVE_ITER_CACHE_SIZE JS_BIT(NATIVE_ITER_CACHE_LOG2) struct JSPendingProxyOperation { JSPendingProxyOperation *next; JSObject *object; }; @@ -1006,19 +983,16 @@ struct JSThreadData { * The GSN cache is per thread since even multi-cx-per-thread embeddings * do not interleave js_GetSrcNote calls. */ JSGSNCache gsnCache; /* Property cache for faster call/get/set invocation. */ js::PropertyCache propertyCache; - /* Optional stack of heap-allocated scoped local GC roots. */ - JSLocalRootStack *localRootStack; - #ifdef JS_TRACER /* Trace-tree JIT recorder/interpreter state. */ js::TraceMonitor traceMonitor; #endif /* Lock-free hashed lists of scripts created by eval to garbage-collect. */ JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; @@ -1050,17 +1024,16 @@ struct JSThreadData { JSPendingProxyOperation *pendingProxyOperation; js::ConservativeGCThreadData conservativeGC; bool init(); void finish(); void mark(JSTracer *trc); void purge(JSContext *cx); - void purgeGCFreeLists(); }; #ifdef JS_THREADSAFE /* * Structure uniquely representing a thread. It holds thread-private data * that can be accessed without a global lock. */ @@ -2763,39 +2736,16 @@ extern JSBool js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry **entryp); extern void js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry *entry, uint32 generation); /* - * Local root set management. - * - * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing - * pointers cast to (jsval). This relies on JSObject's tag being zero, but - * on the up side it lets us push int-jsval-encoded scopeMark values on the - * local root stack. - */ -extern JSBool -js_EnterLocalRootScope(JSContext *cx); - -#define js_LeaveLocalRootScope(cx) \ - js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL) - -extern void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - -extern void -js_ForgetLocalRoot(JSContext *cx, jsval v); - -extern int -js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v); - -/* * Report an exception, which is currently realized as a printf-style format * string and its arguments. */ typedef enum JSErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "js.msg" #undef MSG_DEF
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1742,26 +1742,16 @@ IsGCThresholdReached(JSRuntime *rt) /* * Since the initial value of the gcLastBytes parameter is not equal to * zero (see the js_InitGC function) the return value is false when * the gcBytes value is close to zero at the JS engine start. */ return rt->isGCMallocLimitReached() || rt->gcBytes >= rt->gcTriggerBytes; } -static inline JSGCFreeLists * -GetGCFreeLists(JSContext *cx) -{ - JSThreadData *td = JS_THREAD_DATA(cx); - if (!td->localRootStack) - return &td->gcFreeLists; - JS_ASSERT(td->gcFreeLists.isEmpty()); - return &td->localRootStack->gcFreeLists; -} - static void LastDitchGC(JSContext *cx) { JS_ASSERT(!JS_ON_TRACE(cx)); /* The last ditch GC preserves weak roots and all atoms. */ AutoPreserveWeakRoots save(cx); AutoKeepAtoms keep(cx->runtime); @@ -1774,17 +1764,17 @@ LastDitchGC(JSContext *cx) * operating systems. For the gory details, see bug 162779. */ js_GC(cx, GC_LOCK_HELD); } static JSGCThing * RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) { - JS_ASSERT(!GetGCFreeLists(cx)->finalizables[thingKind]); + JS_ASSERT(!JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind]); JSRuntime *rt = cx->runtime; JSGCArenaList *arenaList; JSGCArena *a; { AutoLockGC lock(rt); JS_ASSERT(!rt->gcRunning); if (rt->gcRunning) { @@ -1801,17 +1791,17 @@ RefillFinalizableFreeList(JSContext *cx, METER(cx->runtime->gcStats.arenaStats[thingKind].retry++); canGC = false; /* * The JSGC_END callback can legitimately allocate new GC * things and populate the free list. If that happens, just * return that list head. */ - JSGCThing *freeList = GetGCFreeLists(cx)->finalizables[thingKind]; + JSGCThing *freeList = JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind]; if (freeList) return freeList; } while ((a = arenaList->cursor) != NULL) { JSGCArenaInfo *ainfo = a->getInfo(); arenaList->cursor = ainfo->prev; JSGCThing *freeList = ainfo->freeList; @@ -1873,78 +1863,39 @@ js_NewFinalizableGCThing(JSContext *cx, /* Updates of metering counters here may not be thread-safe. */ METER(cx->runtime->gcStats.arenaStats[thingKind].alloc++); JSGCThing **freeListp = JS_THREAD_DATA(cx)->gcFreeLists.finalizables + thingKind; JSGCThing *thing = *freeListp; if (thing) { - JS_ASSERT(!JS_THREAD_DATA(cx)->localRootStack); *freeListp = thing->link; cx->weakRoots.finalizableNewborns[thingKind] = thing; CheckGCFreeListLink(thing); METER(cx->runtime->gcStats.arenaStats[thingKind].localalloc++); return thing; } - /* - * To avoid for the local roots on each GC allocation when the local roots - * are not active we move the GC free lists from JSThreadData to lrs in - * JS_EnterLocalRootScope(). This way with inactive local roots we only - * check for non-null lrs only when we exhaust the free list. - */ - JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack; - for (;;) { - if (lrs) { - freeListp = lrs->gcFreeLists.finalizables + thingKind; - thing = *freeListp; - if (thing) { - *freeListp = thing->link; - METER(cx->runtime->gcStats.arenaStats[thingKind].localalloc++); - break; - } - } - - thing = RefillFinalizableFreeList(cx, thingKind); - if (thing) { - /* - * See comments in RefillFinalizableFreeList about a possibility - * of *freeListp == thing. - */ - JS_ASSERT(!*freeListp || *freeListp == thing); - *freeListp = thing->link; - break; - } - + thing = RefillFinalizableFreeList(cx, thingKind); + if (!thing) { js_ReportOutOfMemory(cx); return NULL; } + /* + * See comments in RefillFinalizableFreeList about a possibility + * of *freeListp == thing. + */ + JS_ASSERT(!*freeListp || *freeListp == thing); + *freeListp = thing->link; + CheckGCFreeListLink(thing); - if (lrs) { - /* - * If we're in a local root scope, don't set newborn[type] at all, to - * avoid entraining garbage from it for an unbounded amount of time - * on this context. A caller will leave the local root scope and pop - * this reference, allowing thing to be GC'd if it has no other refs. - * See JS_EnterLocalRootScope and related APIs. - */ - if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { - JS_ASSERT(thing->link == *freeListp); - *freeListp = thing; - return NULL; - } - } else { - /* - * No local root scope, so we're stuck with the old, fragile model of - * depending on a pigeon-hole newborn per type per context. - */ - cx->weakRoots.finalizableNewborns[thingKind] = thing; - } + + cx->weakRoots.finalizableNewborns[thingKind] = thing; return thing; } static JSGCThing * TurnUsedArenaIntoDoubleList(JSGCArena *a) { JSGCThing *head; @@ -1996,34 +1947,34 @@ TurnUsedArenaIntoDoubleList(JSGCArena *a } *tailp = NULL; return head; } static JSGCThing * RefillDoubleFreeList(JSContext *cx) { - JS_ASSERT(!GetGCFreeLists(cx)->doubles); + JS_ASSERT(!JS_THREAD_DATA(cx)->gcFreeLists.doubles); JSRuntime *rt = cx->runtime; JS_ASSERT(!rt->gcRunning); JS_LOCK_GC(rt); bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; bool doGC = canGC && IsGCThresholdReached(rt); JSGCArena *a; for (;;) { if (doGC) { LastDitchGC(cx); METER(rt->gcStats.doubleArenaStats.retry++); canGC = false; /* See comments in RefillFinalizableFreeList. */ - JSGCThing *freeList = GetGCFreeLists(cx)->doubles; + JSGCThing *freeList = JS_THREAD_DATA(cx)->gcFreeLists.doubles; if (freeList) { JS_UNLOCK_GC(rt); return freeList; } } /* * Loop until we find arena with some free doubles. We turn arenas @@ -2064,57 +2015,44 @@ js_NewDoubleInRootedValue(JSContext *cx, { /* Updates of metering counters here are not thread-safe. */ METER(cx->runtime->gcStats.doubleArenaStats.alloc++); JSGCThing **freeListp = &JS_THREAD_DATA(cx)->gcFreeLists.doubles; JSGCThing *thing = *freeListp; if (thing) { METER(cx->runtime->gcStats.doubleArenaStats.localalloc++); - JS_ASSERT(!JS_THREAD_DATA(cx)->localRootStack); CheckGCFreeListLink(thing); *freeListp = thing->link; jsdouble *dp = &thing->asDouble; *dp = d; *vp = DOUBLE_TO_JSVAL(dp); return true; } - JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack; - for (;;) { - if (lrs) { - freeListp = &lrs->gcFreeLists.doubles; - thing = *freeListp; - if (thing) { - METER(cx->runtime->gcStats.doubleArenaStats.localalloc++); - break; - } - } - thing = RefillDoubleFreeList(cx); - if (thing) { - JS_ASSERT(!*freeListp || *freeListp == thing); - break; - } - + thing = RefillDoubleFreeList(cx); + if (!thing) { if (!JS_ON_TRACE(cx)) { /* Trace code handle this on its own. */ js_ReportOutOfMemory(cx); METER(cx->runtime->gcStats.doubleArenaStats.fail++); } return false; } + JS_ASSERT(!*freeListp || *freeListp == thing); + CheckGCFreeListLink(thing); *freeListp = thing->link; jsdouble *dp = reinterpret_cast<jsdouble *>(thing); *dp = d; *vp = DOUBLE_TO_JSVAL(dp); - return !lrs || js_PushLocalRoot(cx, lrs, *vp) >= 0; + return true; } jsdouble * js_NewWeaklyRootedDouble(JSContext *cx, jsdouble d) { jsval v; if (!js_NewDoubleInRootedValue(cx, d, &v)) return NULL;
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -691,32 +691,24 @@ js_InternalInvoke(JSContext *cx, JSObjec args.getvp()[0] = fval; args.getvp()[1] = OBJECT_TO_JSVAL(obj); memcpy(args.getvp() + 2, argv, argc * sizeof(jsval)); if (!js_Invoke(cx, args, flags)) return JS_FALSE; /* - * Store *rval in the a scoped local root if a scope is open, else in - * the lastInternalResult pigeon-hole GC root, solely so users of - * js_InternalInvoke and its direct and indirect (js_ValueToString for - * example) callers do not need to manage roots for local, temporary - * references to such results. + * Store *rval in the lastInternalResult pigeon-hole GC root, solely + * so users of js_InternalInvoke and its direct and indirect + * (js_ValueToString for example) callers do not need to manage roots + * for local, temporary references to such results. */ *rval = *args.getvp(); - if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) { - JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack; - if (lrs) { - if (js_PushLocalRoot(cx, lrs, *rval) < 0) - return JS_FALSE; - } else { - cx->weakRoots.lastInternalResult = *rval; - } - } + if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) + cx->weakRoots.lastInternalResult = *rval; return JS_TRUE; } JSBool js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) {
--- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -86,16 +86,32 @@ using namespace js; * * TODO * - XXXbe patrol * - Fuse objects and their JSXML* private data into single GC-things * - fix function::foo vs. x.(foo == 42) collision using proper namespacing * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! */ +static inline bool +js_EnterLocalRootScope(JSContext *cx) +{ + return true; +} + +static inline void +js_LeaveLocalRootScope(JSContext *cx) +{ +} + +static inline void +js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) +{ +} + #ifdef XML_METERING static struct { jsrefcount qname; jsrefcount xmlnamespace; jsrefcount xml; jsrefcount xmlobj; } xml_stats; @@ -2939,18 +2955,16 @@ DeepCopy(JSContext *cx, JSXML *xml, JSOb static JSBool DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, uintN flags) { uint32 j, n; JSXML *kid2; JSString *str; - JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack); - n = from->length; if (!to->setCapacity(cx, n)) return JS_FALSE; JSXMLArrayCursor cursor(from); j = 0; while (JSXML *kid = (JSXML *) cursor.getNext()) { if ((flags & XSF_IGNORE_COMMENTS) && @@ -2996,19 +3010,16 @@ static JSXML * DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) { JSXML *copy; JSObject *qn; JSBool ok; uint32 i, n; JSObject *ns, *ns2; - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack); - JS_CHECK_RECURSION(cx, return NULL); copy = js_NewXML(cx, JSXMLClass(xml->xml_class)); if (!copy) return NULL; qn = xml->name; if (qn) { qn = NewXMLQName(cx, GetURI(qn), GetPrefix(qn), GetLocalName(qn)); @@ -4375,19 +4386,16 @@ bad: /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ static JSBool ResolveValue(JSContext *cx, JSXML *list, JSXML **result) { JSXML *target, *base; JSObject *targetprop; jsval id, tv; - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack); - if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { if (!js_GetXMLObject(cx, list)) return JS_FALSE; *result = list; return JS_TRUE; } target = list->xml_target;