From 865dcaf560bd9825f6e5bb340282773108f5daf2 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 13 Mar 2009 12:36:21 +0100 Subject: [PATCH] bug 437325 - JSThread is no longer shared between runtimes. r=brendan --- js/src/jsapi.cpp | 62 +---- js/src/jscntxt.cpp | 469 +++++++++++++++++++++------------- js/src/jscntxt.h | 141 +++++----- js/src/jsdbgapi.cpp | 2 +- js/src/jsfun.cpp | 4 + js/src/jsgc.cpp | 110 +++----- js/src/jsgc.h | 6 + js/src/jsinterp.cpp | 7 +- js/src/jsinterp.h | 7 +- js/src/jsprvtd.h | 2 + js/src/jsscript.cpp | 21 +- js/src/jstracer.cpp | 8 +- js/src/jstracer.h | 4 +- js/src/xpconnect/src/xpcjsruntime.cpp | 17 -- 14 files changed, 434 insertions(+), 426 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index c8920b4ccc..751ed2ce95 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -787,8 +787,6 @@ JS_NewRuntime(uint32 maxbytes) if (!js_InitDeflatedStringCache(rt)) goto bad; #ifdef JS_THREADSAFE - if (!js_InitThreadPrivateIndex(js_ThreadDestructorCB)) - goto bad; rt->gcLock = JS_NEW_LOCK(); if (!rt->gcLock) goto bad; @@ -817,10 +815,8 @@ JS_NewRuntime(uint32 maxbytes) #endif if (!js_InitPropertyTree(rt)) goto bad; - -#if !defined JS_THREADSAFE && defined JS_TRACER - js_InitJIT(&rt->traceMonitor); -#endif + if (!js_InitThreads(rt)) + goto bad; return rt; @@ -849,10 +845,7 @@ JS_DestroyRuntime(JSRuntime *rt) } #endif -#if !defined JS_THREADSAFE && defined JS_TRACER - js_FinishJIT(&rt->traceMonitor); -#endif - + js_FinishThreads(rt); js_FreeRuntimeScriptState(rt); js_FinishAtomState(rt); @@ -884,8 +877,6 @@ JS_DestroyRuntime(JSRuntime *rt) JS_DESTROY_CONDVAR(rt->titleSharingDone); if (rt->debuggerLock) JS_DESTROY_LOCK(rt->debuggerLock); -#else - GSN_CACHE_CLEAR(&rt->gsnCache); #endif js_FinishPropertyTree(rt); free(rt); @@ -902,7 +893,6 @@ JS_ShutDown(void) js_FinishDtoa(); #ifdef JS_THREADSAFE - js_CleanupThreadPrivateData(); /* Fixes bug 464828. */ js_CleanupLocks(); #endif PRMJ_NowShutdown(); @@ -926,7 +916,7 @@ JS_BeginRequest(JSContext *cx) #ifdef JS_THREADSAFE JSRuntime *rt; - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); if (!cx->requestDepth) { JS_ASSERT(cx->gcLocalFreeLists == &js_GCEmptyFreeListSet); @@ -934,7 +924,6 @@ JS_BeginRequest(JSContext *cx) rt = cx->runtime; JS_LOCK_GC(rt); - /* NB: we use cx->thread here, not js_GetCurrentThread(). */ if (rt->gcThread != cx->thread) { while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); @@ -961,6 +950,7 @@ JS_EndRequest(JSContext *cx) JSBool shared; CHECK_REQUEST(cx); + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); JS_ASSERT(cx->requestDepth > 0); JS_ASSERT(cx->outstandingRequests > 0); if (cx->requestDepth == 1) { @@ -2850,19 +2840,6 @@ JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) { CHECK_REQUEST(cx); JS_ASSERT(obj != proto); -#ifdef DEBUG - /* - * FIXME: bug 408416. The cycle-detection required for script-writeable - * __proto__ lives in js_SetProtoOrParent over in jsobj.c, also known as - * js_ObjectOps.setProto. This hook must detect cycles, to prevent scripts - * from ilooping SpiderMonkey trivially. But the overhead of detecting - * cycles is high enough, and the threat from JS-API-calling C++ code is - * low enough, that it's not worth burdening the non-DEBUG callers. Same - * goes for JS_SetParent, below. - */ - if (obj->map->ops->setProto) - return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto); -#else if (OBJ_IS_NATIVE(obj)) { JS_LOCK_OBJ(cx, obj); if (!js_GetMutableScope(cx, obj)) { @@ -2873,7 +2850,6 @@ JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; } -#endif OBJ_SET_PROTO(cx, obj, proto); return JS_TRUE; } @@ -2894,11 +2870,6 @@ JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) { CHECK_REQUEST(cx); JS_ASSERT(obj != parent); -#ifdef DEBUG - /* FIXME: bug 408416, see JS_SetPrototype just above. */ - if (obj->map->ops->setParent) - return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent); -#endif OBJ_SET_PARENT(cx, obj, parent); return JS_TRUE; } @@ -5958,25 +5929,17 @@ JS_SetContextThread(JSContext *cx) #ifdef JS_THREADSAFE JS_ASSERT(cx->requestDepth == 0); if (cx->thread) { - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); return cx->thread->id; } - JSRuntime *rt = cx->runtime; - JSThread *thread = js_GetCurrentThread(rt); - if (!thread) { + if (!js_InitContextThread(cx)) { js_ReportOutOfMemory(cx); return -1; } - /* - * We must not race with a GC that accesses cx->thread for all threads, - * see bug 476934. - */ - JS_LOCK_GC(rt); - js_WaitForGC(rt); - js_InitContextThread(cx, thread); - JS_UNLOCK_GC(rt); + /* Here the GC lock is still held after js_InitContextThread took it. */ + JS_UNLOCK_GC(cx->runtime); #endif return 0; } @@ -5993,8 +5956,8 @@ JS_ClearContextThread(JSContext *cx) JS_ASSERT(cx->requestDepth == 0); if (!cx->thread) return 0; + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); jsword old = cx->thread->id; - JS_ASSERT(old == js_CurrentThreadId()); /* * We must not race with a GC that accesses cx->thread for all threads, @@ -6003,9 +5966,8 @@ JS_ClearContextThread(JSContext *cx) JSRuntime *rt = cx->runtime; JS_LOCK_GC(rt); js_WaitForGC(rt); - JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); - cx->thread = NULL; - JS_UNLOCK_GC(cx->runtime); + js_ClearContextThread(cx); + JS_UNLOCK_GC(rt); return old; #else return 0; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 56d16381c8..0b896869b5 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -69,141 +69,231 @@ #include "jstracer.h" #ifdef JS_THREADSAFE -#include "prtypes.h" -/* - * The index for JSThread info, returned by PR_NewThreadPrivateIndex. The - * index value is visible and shared by all threads, but the data associated - * with it is private to each thread. - */ -static PRUintn threadTPIndex; -static JSBool tpIndexInited = JS_FALSE; - -JS_BEGIN_EXTERN_C -JSBool -js_InitThreadPrivateIndex(void (*ptr)(void *)) -{ - PRStatus status; +struct JSThreadsHashEntry : JSDHashEntryHdr { + JSThread *thread; +}; - if (tpIndexInited) - return JS_TRUE; +#endif - status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr); +static void +FreeContext(JSContext *cx); - if (status == PR_SUCCESS) - tpIndexInited = JS_TRUE; - return status == PR_SUCCESS; +static void +InitThreadData(JSThreadData *data) +{ +#ifdef DEBUG + /* The data must be already zeroed. */ + for (size_t i = 0; i != sizeof(*data); ++i) + JS_ASSERT(reinterpret_cast(data)[i] == 0); +#endif +#ifdef JS_TRACER + js_InitJIT(&data->traceMonitor); +#endif } -JS_END_EXTERN_C -JS_BEGIN_EXTERN_C -JSBool -js_CleanupThreadPrivateData() +static void +FinishThreadData(JSThreadData *data) { - if (!tpIndexInited) - return JS_TRUE; - return PR_SetThreadPrivate(threadTPIndex, NULL) == PR_SUCCESS; +#ifdef DEBUG + /* All GC-related things must be already removed at this point. */ + for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) + JS_ASSERT(!data->scriptsToGC[i]); +#endif + + js_FinishGSNCache(&data->gsnCache); + js_FinishPropertyCache(&data->propertyCache); +#if defined JS_TRACER + js_FinishJIT(&data->traceMonitor); +#endif } -JS_END_EXTERN_C -/* - * Callback function to delete a JSThread info when the thread that owns it - * is destroyed. - */ -void -js_ThreadDestructorCB(void *ptr) +static void +PurgeThreadData(JSContext *cx, JSThreadData *data) { - JSThread *thread = (JSThread *)ptr; +# ifdef JS_TRACER + js_PurgeTraceMonitor(cx, &data->traceMonitor); +# endif + + /* Destroy eval'ed scripts. */ + js_DestroyScriptsToGC(cx, data); + + js_PurgeGSNCache(&data->gsnCache); + js_PurgePropertyCache(cx, &data->propertyCache); +} + +#ifdef JS_THREADSAFE +static JSThread * +NewThread(jsword id) +{ + JS_ASSERT(js_CurrentThreadId() == id); + JSThread *thread = (JSThread *) calloc(1, sizeof(JSThread)); if (!thread) - return; + return NULL; + JS_INIT_CLIST(&thread->contextList); + thread->id = id; + InitThreadData(&thread->data); + return thread; +} - /* - * Check that this thread properly called either JS_DestroyContext or - * JS_ClearContextThread on each JSContext it created or used. - */ +static void +DestroyThread(JSThread *thread) +{ + /* The thread must have zero contexts. */ JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); - GSN_CACHE_CLEAR(&thread->gsnCache); -#if defined JS_TRACER - js_FinishJIT(&thread->traceMonitor); -#endif + FinishThreadData(&thread->data); free(thread); } -/* - * Get current thread-local JSThread info, creating one if it doesn't exist. - * Each thread has a unique JSThread pointer. - * - * Since we are dealing with thread-local data, no lock is needed. - * - * Return a pointer to the thread local info, NULL if the system runs out - * of memory, or it failed to set thread private data (neither case is very - * likely; both are probably due to out-of-memory). It is up to the caller - * to report an error, if possible. - */ -JSThread * -js_GetCurrentThread(JSRuntime *rt) +JSBool +js_InitContextThread(JSContext *cx) { + JSRuntime *rt; + jsword id = js_CurrentThreadId(); + const void *key = (const void *) id; + JSThreadsHashEntry *entry; JSThread *thread; - thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex); - if (!thread) { - thread = (JSThread *) malloc(sizeof(JSThread)); + JS_ASSERT(!cx->thread); + + rt = cx->runtime; + JS_LOCK_GC(rt); + + /* + * We must not race with a GC that accesses cx->thread for JSContext + * instances on all threads, see bug 476934. + */ + js_WaitForGC(rt); + entry = (JSThreadsHashEntry *) + JS_DHashTableOperate(&rt->threads, key, JS_DHASH_LOOKUP); + if (JS_DHASH_ENTRY_IS_BUSY(entry)) { + thread = entry->thread; + JS_ASSERT(thread->id == id); + } else { + JS_UNLOCK_GC(rt); + thread = NewThread(id); if (!thread) - return NULL; -#ifdef DEBUG - memset(thread, JS_FREE_PATTERN, sizeof(JSThread)); -#endif - if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) { - free(thread); - return NULL; + return false; + JS_LOCK_GC(rt); + js_WaitForGC(rt); + entry = (JSThreadsHashEntry *) + JS_DHashTableOperate(&rt->threads, key, JS_DHASH_ADD); + if (!entry) { + JS_UNLOCK_GC(rt); + DestroyThread(thread); + return false; } - JS_INIT_CLIST(&thread->contextList); - thread->id = js_CurrentThreadId(); - thread->gcMallocBytes = 0; -#ifdef JS_TRACER - memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor)); - js_InitJIT(&thread->traceMonitor); -#endif - memset(thread->scriptsToGC, 0, sizeof thread->scriptsToGC); - - /* - * js_InitContextThread initializes the remaining fields as necessary. - */ + /* Another thread cannot initialize entry->thread. */ + JS_ASSERT(!entry->thread); + entry->thread = thread; } - return thread; + + JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); + cx->thread = thread; + return true; } -/* - * Sets current thread as owning thread of a context by assigning the - * thread-private info to the context. - */ void -js_InitContextThread(JSContext *cx, JSThread *thread) +js_ClearContextThread(JSContext *cx) { - JS_ASSERT(CURRENT_THREAD_IS_ME(thread)); - JS_ASSERT(!cx->thread); - JS_ASSERT(cx->requestDepth == 0); + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); + cx->thread = NULL; +} + +static JSBool +thread_matchEntry(JSDHashTable *table, + const JSDHashEntryHdr *hdr, + const void *key) +{ + const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr; + + return entry->thread->id == (jsword) key; +} + +static const JSDHashTableOps threads_ops = { + JS_DHashAllocTable, + JS_DHashFreeTable, + JS_DHashVoidPtrKeyStub, + thread_matchEntry, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +static JSDHashOperator +thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, + void * /* arg */) +{ + JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr; + JSThread *thread = entry->thread; + + JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); + DestroyThread(thread); + return JS_DHASH_REMOVE; +} + +static JSDHashOperator +thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, + void *arg) +{ + JSContext* cx = (JSContext *) arg; + JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread; - /* - * Clear caches on each transition from 0 to 1 context active on the - * current thread. See bug 425828. - */ if (JS_CLIST_IS_EMPTY(&thread->contextList)) { - memset(&thread->gsnCache, 0, sizeof thread->gsnCache); - memset(&thread->propertyCache, 0, sizeof thread->propertyCache); -#ifdef DEBUG - memset(&thread->evalCacheMeter, 0, sizeof thread->evalCacheMeter); -#endif + JS_ASSERT(cx->thread != thread); + js_DestroyScriptsToGC(cx, &thread->data); + DestroyThread(thread); + return JS_DHASH_REMOVE; } - - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); - cx->thread = thread; + PurgeThreadData(cx, &thread->data); + return JS_DHASH_NEXT; } #endif /* JS_THREADSAFE */ +JSBool +js_InitThreads(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL, + sizeof(JSThreadsHashEntry), 4)) { + rt->threads.ops = NULL; + return false; + } +#else + InitThreadData(&rt->threadData); +#endif + return true; +} + +void +js_FinishThreads(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + if (!rt->threads.ops) + return; + JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL); + JS_DHashTableFinish(&rt->threads); + rt->threads.ops = NULL; +#else + FinishThreadData(&rt->threadData); +#endif +} + +void +js_PurgeThreads(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx); +#else + PurgeThreadData(cx, &cx->runtime->threadData); +#endif +} + /* * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version * associated with scripts, so in addition to storing them in cx->options we @@ -260,12 +350,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JSContext *cx; JSBool ok, first; JSContextCallback cxCallback; -#ifdef JS_THREADSAFE - JSThread *thread = js_GetCurrentThread(rt); - - if (!thread) - return NULL; -#endif /* * We need to initialize the new context fully before adding it to the @@ -284,7 +368,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; #ifdef JS_THREADSAFE cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet; - js_InitContextThread(cx, thread); #endif JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0); JS_ASSERT(cx->version == JSVERSION_DEFAULT); @@ -299,12 +382,18 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) js_InitRegExpStatics(cx); JS_ASSERT(cx->resolveFlags == 0); - JS_LOCK_GC(rt); +#ifdef JS_THREADSAFE + if (!js_InitContextThread(cx)) { + FreeContext(cx); + return NULL; + } +#endif + + /* + * Here the GC lock is still held after js_InitContextThread took it and + * the GC is not running on another thread. + */ for (;;) { - /* - * Ensure that we don't race with the GC on other threads, bug 478336. - */ - js_WaitForGC(rt); if (rt->state == JSRTS_UP) { JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList)); first = JS_FALSE; @@ -317,6 +406,15 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) break; } JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); + + /* + * During the above wait after we are notified about the state change + * but before we wake up, another thread could enter the GC from + * js_DestroyContext, bug 478336. So we must wait here to ensure that + * when we exit the loop with the first flag set to true, that GC is + * finished. + */ + js_WaitForGC(rt); } JS_APPEND_LINK(&cx->link, &rt->contextList); JS_UNLOCK_GC(rt); @@ -403,7 +501,7 @@ DumpEvalCacheMeter(JSContext *cx) EVAL_CACHE_METER_LIST(frob) #undef frob }; - JSEvalCacheMeter *ecm = &JS_CACHE_LOCUS(cx)->evalCacheMeter; + JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter; static AutoFile fp; if (!fp) { @@ -438,9 +536,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JSRuntime *rt; JSContextCallback cxCallback; JSBool last; - JSArgumentFormatMap *map; - JSLocalRootStack *lrs; - JSLocalRootChunk *lrc; #ifdef JS_THREADSAFE JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); @@ -477,73 +572,99 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) last = (rt->contextList.next == &rt->contextList); if (last) rt->state = JSRTS_LANDING; - JS_UNLOCK_GC(rt); + if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC +#ifdef JS_THREADSAFE + || cx->requestDepth != 0 +#endif + ) { + JS_UNLOCK_GC(rt); - if (last) { + if (last) { #ifdef JS_THREADSAFE - /* - * If cx is not in a request already, begin one now so that we wait - * for any racing GC started on a not-last context to finish, before - * we plow ahead and unpin atoms. Note that even though we begin a - * request here if necessary, we end all requests on cx below before - * forcing a final GC. This lets any not-last context destruction - * racing in another thread try to force or maybe run the GC, but by - * that point, rt->state will not be JSRTS_UP, and that GC attempt - * will return early. - */ - if (cx->requestDepth == 0) - JS_BeginRequest(cx); + /* + * If cx is not in a request already, begin one now so that we wait + * for any racing GC started on a not-last context to finish, before + * we plow ahead and unpin atoms. Note that even though we begin a + * request here if necessary, we end all requests on cx below before + * forcing a final GC. This lets any not-last context destruction + * racing in another thread try to force or maybe run the GC, but by + * that point, rt->state will not be JSRTS_UP, and that GC attempt + * will return early. + */ + if (cx->requestDepth == 0) + JS_BeginRequest(cx); #endif - /* Unlock and clear GC things held by runtime pointers. */ - js_FinishRuntimeNumberState(cx); - js_FinishRuntimeStringState(cx); + /* Unlock and clear GC things held by runtime pointers. */ + js_FinishRuntimeNumberState(cx); + js_FinishRuntimeStringState(cx); - /* Unpin all common atoms before final GC. */ - js_FinishCommonAtoms(cx); + /* Unpin all common atoms before final GC. */ + js_FinishCommonAtoms(cx); - /* Clear debugging state to remove GC roots. */ - JS_ClearAllTraps(cx); - JS_ClearAllWatchPoints(cx); - } + /* Clear debugging state to remove GC roots. */ + JS_ClearAllTraps(cx); + JS_ClearAllWatchPoints(cx); + } - /* Remove more GC roots in regExpStatics, then collect garbage. */ - JS_ClearRegExpRoots(cx); + /* Remove more GC roots in regExpStatics, then collect garbage. */ + JS_ClearRegExpRoots(cx); #ifdef JS_THREADSAFE - /* - * Destroying a context implicitly calls JS_EndRequest(). Also, we must - * end our request here in case we are "last" -- in that event, another - * js_DestroyContext that was not last might be waiting in the GC for our - * request to end. We'll let it run below, just before we do the truly - * final GC and then free atom state. - */ - while (cx->requestDepth != 0) - JS_EndRequest(cx); + /* + * Destroying a context implicitly calls JS_EndRequest(). Also, we must + * end our request here in case we are "last" -- in that event, another + * js_DestroyContext that was not last might be waiting in the GC for our + * request to end. We'll let it run below, just before we do the truly + * final GC and then free atom state. + */ + while (cx->requestDepth != 0) + JS_EndRequest(cx); #endif - if (last) { - js_GC(cx, GC_LAST_CONTEXT); - DUMP_EVAL_CACHE_METER(cx); + if (last) { + js_GC(cx, GC_LAST_CONTEXT); + DUMP_EVAL_CACHE_METER(cx); - /* - * Free the script filename table if it exists and is empty. Do this - * after the last GC to avoid finalizers tripping on free memory. - */ - if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) - js_FinishRuntimeScriptState(rt); + /* + * Free the script filename table if it exists and is empty. Do this + * after the last GC to avoid finalizers tripping on free memory. + */ + if (rt->scriptFilenameTable && + rt->scriptFilenameTable->nentries == 0) { + js_FinishRuntimeScriptState(rt); + } - /* Take the runtime down, now that it has no contexts or atoms. */ - JS_LOCK_GC(rt); - rt->state = JSRTS_DOWN; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); - JS_UNLOCK_GC(rt); - } else { - if (mode == JSDCM_FORCE_GC) - js_GC(cx, GC_NORMAL); - else if (mode == JSDCM_MAYBE_GC) - JS_MaybeGC(cx); + /* Take the runtime down, now that it has no contexts or atoms. */ + JS_LOCK_GC(rt); + rt->state = JSRTS_DOWN; + JS_NOTIFY_ALL_CONDVAR(rt->stateChange); + } else { + if (mode == JSDCM_FORCE_GC) + js_GC(cx, GC_NORMAL); + else if (mode == JSDCM_MAYBE_GC) + JS_MaybeGC(cx); + JS_LOCK_GC(rt); + js_WaitForGC(rt); + } } +#ifdef JS_THREADSAFE + js_ClearContextThread(cx); +#endif + JS_UNLOCK_GC(rt); + FreeContext(cx); +} + +static void +FreeContext(JSContext *cx) +{ + JSArgumentFormatMap *map; + JSLocalRootStack *lrs; + JSLocalRootChunk *lrc; + +#ifdef JS_THREADSAFE + JS_ASSERT(!cx->thread); +#endif /* Free the stuff hanging off of cx. */ js_FreeRegExpStatics(cx); @@ -577,16 +698,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JS_free(cx, lrs); } -#ifdef JS_THREADSAFE - /* - * Since cx is not on rt->contextList, it cannot be accessed by the GC - * running on another thread. Thus, compared with JS_ClearContextThread, - * we can safely unlink cx from from JSThread.contextList without taking - * the GC lock. - */ - JS_REMOVE_LINK(&cx->threadLinks); -#endif - /* Finally, free cx itself. */ free(cx); } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 74797da0e2..8a615143dd 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -72,25 +72,20 @@ typedef struct JSGSNCache { uint32 hits; uint32 misses; uint32 fills; - uint32 clears; + uint32 purges; # define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt) #else # define GSN_CACHE_METER(cache,cnt) /* nothing */ #endif } JSGSNCache; -#define GSN_CACHE_CLEAR(cache) \ - JS_BEGIN_MACRO \ - (cache)->code = NULL; \ - if ((cache)->table.ops) { \ - JS_DHashTableFinish(&(cache)->table); \ - (cache)->table.ops = NULL; \ - } \ - GSN_CACHE_METER(cache, clears); \ - JS_END_MACRO +#define js_FinishGSNCache(cache) js_PurgeGSNCache(cache) + +extern void +js_PurgeGSNCache(JSGSNCache *cache); /* These helper macros take a cx as parameter and operate on its GSN cache. */ -#define JS_CLEAR_GSN_CACHE(cx) GSN_CACHE_CLEAR(&JS_GSN_CACHE(cx)) +#define JS_PURGE_GSN_CACHE(cx) js_PurgeGSNCache(&JS_GSN_CACHE(cx)) #define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt) typedef struct InterpState InterpState; @@ -125,7 +120,7 @@ struct GlobalState { * JS_THREADSAFE) has an associated trace monitor that keeps track of loop * frequencies for all JavaScript code loaded into that runtime. */ -typedef struct JSTraceMonitor { +struct JSTraceMonitor { /* * Flag set when running (or recording) JIT-compiled code. This prevents * both interpreter activation and last-ditch garbage collection when up @@ -176,7 +171,7 @@ typedef struct JSTraceMonitor { /* Keep a list of recorders we need to abort on cache flush. */ CLS(TraceRecorder) abortStack; -} JSTraceMonitor; +}; typedef struct InterpStruct InterpStruct; @@ -206,11 +201,31 @@ typedef struct JSEvalCacheMeter { } JSEvalCacheMeter; # undef ID -# define DECLARE_EVAL_CACHE_METER JSEvalCacheMeter evalCacheMeter; -#else -# define DECLARE_EVAL_CACHE_METER /* nothing */ #endif +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. */ + JSPropertyCache propertyCache; + +#ifdef JS_TRACER + /* Trace-tree JIT recorder/interpreter state. */ + JSTraceMonitor traceMonitor; +#endif + + /* Lock-free hashed lists of scripts created by eval to garbage-collect. */ + JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; + +#ifdef JS_EVAL_CACHE_METERING + JSEvalCacheMeter evalCacheMeter; +#endif +}; + #ifdef JS_THREADSAFE /* @@ -230,39 +245,24 @@ struct JSThread { */ uint32 gcMallocBytes; - /* - * Store the GSN cache in struct JSThread, not struct JSContext, both to - * save space and to simplify cleanup in js_GC. Any embedding (Firefox - * or another Gecko application) that uses many contexts per thread is - * unlikely to interleave js_GetSrcNote-intensive loops in the decompiler - * among two or more contexts running script in one thread. - */ - JSGSNCache gsnCache; - - /* Property cache for faster call/get/set invocation. */ - JSPropertyCache propertyCache; - -#ifdef JS_TRACER - /* Trace-tree JIT recorder/interpreter state. */ - JSTraceMonitor traceMonitor; -#endif - - /* Lock-free hashed lists of scripts created by eval to garbage-collect. */ - JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; - - DECLARE_EVAL_CACHE_METER + JSThreadData data; }; -#define JS_CACHE_LOCUS(cx) ((cx)->thread) +#define JS_THREAD_DATA(cx) (&(cx)->thread->data) -extern void -js_ThreadDestructorCB(void *ptr); +/* + * The function takes the GC lock and does not release in successful return. + * On error (out of memory) the function releases the lock but delegates + * the error reporting to the caller. + */ +extern JSBool +js_InitContextThread(JSContext *cx); +/* + * On entrance the GC lock must be held and it will be held on exit. + */ extern void -js_InitContextThread(JSContext *cx, JSThread *thread); - -extern JSThread * -js_GetCurrentThread(JSRuntime *rt); +js_ClearContextThread(JSContext *cx); #endif /* JS_THREADSAFE */ @@ -474,6 +474,8 @@ struct JSRuntime { * case too. */ PRLock *debuggerLock; + + JSDHashTable threads; #endif /* JS_THREADSAFE */ uint32 debuggerMutations; @@ -523,26 +525,9 @@ struct JSRuntime { JSNativeEnumerator *nativeEnumerators; #ifndef JS_THREADSAFE - /* - * For thread-unsafe embeddings, the GSN cache lives in the runtime and - * not each context, since we expect it to be filled once when decompiling - * a longer script, then hit repeatedly as js_GetSrcNote is called during - * the decompiler activation that filled it. - */ - JSGSNCache gsnCache; + JSThreadData threadData; - /* Property cache for faster call/get/set invocation. */ - JSPropertyCache propertyCache; - - /* Trace-tree JIT recorder/interpreter state. */ - JSTraceMonitor traceMonitor; - - /* Lock-free hashed lists of scripts created by eval to garbage-collect. */ - JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; - - DECLARE_EVAL_CACHE_METER - -#define JS_CACHE_LOCUS(cx) ((cx)->runtime) +#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData) #endif /* @@ -652,13 +637,13 @@ struct JSRuntime { }; /* Common macros to access thread-local caches in JSThread or JSRuntime. */ -#define JS_GSN_CACHE(cx) (JS_CACHE_LOCUS(cx)->gsnCache) -#define JS_PROPERTY_CACHE(cx) (JS_CACHE_LOCUS(cx)->propertyCache) -#define JS_TRACE_MONITOR(cx) (JS_CACHE_LOCUS(cx)->traceMonitor) -#define JS_SCRIPTS_TO_GC(cx) (JS_CACHE_LOCUS(cx)->scriptsToGC) +#define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache) +#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) +#define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor) +#define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC) #ifdef JS_EVAL_CACHE_METERING -# define EVAL_CACHE_METER(x) (JS_CACHE_LOCUS(cx)->evalCacheMeter.x++) +# define EVAL_CACHE_METER(x) (JS_THREAD_DATA(cx)->evalCacheMeter.x++) #else # define EVAL_CACHE_METER(x) ((void) 0) #endif @@ -1138,22 +1123,14 @@ class JSAutoResolveFlags #define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \ JSVERSION_NUMBER(cx) >= JSVERSION_1_6) -/* - * Initialize a library-wide thread private data index, and remember that it - * has already been done, so that it happens only once ever. Returns true on - * success. - */ extern JSBool -js_InitThreadPrivateIndex(void (*ptr)(void *)); +js_InitThreads(JSRuntime *rt); -/* - * Clean up thread-private data on the current thread. NSPR automatically - * cleans up thread-private data for every thread except the main thread - * (see bug 383977) on shutdown. Thus, this function should be called for - * exactly those threads that survive JS_ShutDown, including the main thread. - */ -extern JSBool -js_CleanupThreadPrivateData(); +extern void +js_FinishThreads(JSRuntime *rt); + +extern void +js_PurgeThreads(JSContext *cx); /* * Ensures the JSOPTION_XML and JSOPTION_ANONFUNFIX bits of cx->options are diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 49add1c74e..da5102680f 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -123,7 +123,7 @@ js_UntrapScriptCode(JSContext *cx, JSScript *script) if (!code) break; memcpy(code, script->code, nbytes); - JS_CLEAR_GSN_CACHE(cx); + JS_PURGE_GSN_CACHE(cx); } code[trap->pc - script->code] = trap->op; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index fcfc9c1a35..6a43973c23 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2712,4 +2712,8 @@ js_FreezeLocalNames(JSContext *cx, JSFunction *fun) if (array) fun->u.i.names.array = array; } +#ifdef DEBUG + if (n > MAX_ARRAY_LOCALS) + JS_DHashMarkTableImmutable(&fun->u.i.names.map->names); +#endif } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index c248ffb00e..ab8f2fbf60 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3109,23 +3109,23 @@ js_TraceContext(JSTracer *trc, JSContext *acx) js_TraceRegExpStatics(trc, acx); } +#ifdef JS_TRACER void -js_TraceTraceMonitor(JSTracer *trc, JSTraceMonitor *tm) +js_PurgeTraceMonitor(JSContext *cx, JSTraceMonitor *tm) { - if (IS_GC_MARKING_TRACER(trc)) { - tm->reservedDoublePoolPtr = tm->reservedDoublePool; + tm->reservedDoublePoolPtr = tm->reservedDoublePool; - tm->needFlush = JS_TRUE; + tm->needFlush = JS_TRUE; - /* Keep the reserved objects. */ - for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) { - uint8 *flagp = GetGCThingFlags(obj); - JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT); - JS_ASSERT(*flagp != GCF_FINAL); - *flagp |= GCF_MARK; - } + /* Keep the reserved objects. */ + for (JSObject *obj = tm->reservedObjects; obj; obj = JSVAL_TO_OBJECT(obj->fslots[0])) { + uint8 *flagp = GetGCThingFlags(obj); + JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT); + JS_ASSERT(*flagp != GCF_FINAL); + *flagp |= GCF_MARK; } } +#endif JS_REQUIRES_STACK void js_TraceRuntime(JSTracer *trc, JSBool allAtoms) @@ -3152,17 +3152,6 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms) if (rt->builtinFunctions[i]) JS_CALL_OBJECT_TRACER(trc, rt->builtinFunctions[i], "builtin function"); } - -#ifdef JS_THREADSAFE - /* Trace the loop table(s) which can contain pointers to code objects. */ - while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (!acx->thread) - continue; - js_TraceTraceMonitor(trc, &acx->thread->traceMonitor); - } -#else - js_TraceTraceMonitor(trc, &rt->traceMonitor); -#endif #endif } @@ -3241,15 +3230,18 @@ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr) STOBJ_SET_DELEGATE(pobj); } -static void -DestroyScriptsToGC(JSContext *cx, JSScript **listp) +void +js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data) { - JSScript *script; + JSScript **listp, *script; - while ((script = *listp) != NULL) { - *listp = script->u.nextToGC; - script->u.nextToGC = NULL; - js_DestroyScript(cx, script); + for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) { + listp = &data->scriptsToGC[i]; + while ((script = *listp) != NULL) { + *listp = script->u.nextToGC; + script->u.nextToGC = NULL; + js_DestroyScript(cx, script); + } } } @@ -3281,7 +3273,14 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx)); rt = cx->runtime; + #ifdef JS_THREADSAFE + /* + * We allow js_GC calls outside a request but the context must be bound + * to the current thread. + */ + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + /* Avoid deadlock. */ JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); #endif @@ -3357,11 +3356,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) /* * If we're in one or more requests (possibly on more than one context) * running on the current thread, indicate, temporarily, that all these - * requests are inactive. If cx->thread is NULL, then cx is not using - * the request model, and does not contribute to rt->requestCount. + * requests are inactive. */ requestDebit = 0; - if (cx->thread) { + { JSCList *head, *link; /* @@ -3375,17 +3373,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) if (acx->requestDepth) requestDebit++; } - } else { - /* - * We assert, but check anyway, in case someone is misusing the API. - * Avoiding the loop over all of rt's contexts is a win in the event - * that the GC runs only on request-less contexts with null threads, - * in a special thread such as might be used by the UI/DOM/Layout - * "mozilla" or "main" thread in Mozilla-the-browser. - */ - JS_ASSERT(cx->requestDepth == 0); - if (cx->requestDepth) - requestDebit = 1; } if (requestDebit) { JS_ASSERT(requestDebit <= rt->requestCount); @@ -3495,43 +3482,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) } #endif - /* Clear property and JIT oracle caches (only for cx->thread if JS_THREADSAFE). */ - js_FlushPropertyCache(cx); -#ifdef JS_TRACER - js_FlushJITOracle(cx); -#endif - - /* Destroy eval'ed scripts. */ - for (i = 0; i < JS_ARRAY_LENGTH(JS_SCRIPTS_TO_GC(cx)); i++) - DestroyScriptsToGC(cx, &JS_SCRIPTS_TO_GC(cx)[i]); - -#ifdef JS_THREADSAFE - /* - * Clear thread-based caches. To avoid redundant clearing we unroll the - * current thread's step. - * - * In case a JSScript wrapped within an object was finalized, we null - * acx->thread->gsnCache.script and finish the cache's hashtable. Note - * that js_DestroyScript, called from script_finalize, will have already - * cleared cx->thread->gsnCache above during finalization, so we don't - * have to here. - */ - iter = NULL; - while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (!acx->thread || acx->thread == cx->thread) - continue; - GSN_CACHE_CLEAR(&acx->thread->gsnCache); - js_FlushPropertyCache(acx); #ifdef JS_TRACER - js_FlushJITOracle(acx); -#endif - for (i = 0; i < JS_ARRAY_LENGTH(acx->thread->scriptsToGC); i++) - DestroyScriptsToGC(cx, &acx->thread->scriptsToGC[i]); - } -#else - /* The thread-unsafe case just has to clear the runtime's GSN cache. */ - GSN_CACHE_CLEAR(&rt->gsnCache); + js_PurgeJITOracle(); #endif + js_PurgeThreads(cx); restart: rt->gcNumber++; diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 1487f964d6..dcff53b02a 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -247,6 +247,9 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms); extern JS_REQUIRES_STACK JS_FRIEND_API(void) js_TraceContext(JSTracer *trc, JSContext *acx); +extern void +js_PurgeTraceMonitor(JSContext *cx, JSTraceMonitor *tm); + /* * Kinds of js_GC invocation. */ @@ -340,6 +343,9 @@ extern const JSGCFreeListSet js_GCEmptyFreeListSet; extern void js_RevokeGCLocalFreeLists(JSContext *cx); +extern void +js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data); + struct JSWeakRoots { /* Most recently created things by type, members of the GC's root set. */ void *newborn[GCX_NTYPES]; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index df1e092eb7..573da3b39b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -425,11 +425,8 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, JS_STATIC_ASSERT(PCVAL_NULL == 0); void -js_FlushPropertyCache(JSContext *cx) +js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache) { - JSPropertyCache *cache; - - cache = &JS_PROPERTY_CACHE(cx); if (cache->empty) { ASSERT_CACHE_IS_EMPTY(cache); return; @@ -497,7 +494,7 @@ js_FlushPropertyCache(JSContext *cx) } void -js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script) +js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script) { JSPropertyCache *cache; JSPropCacheEntry *entry; diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 115530a83f..12b650f7c6 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -353,11 +353,14 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp, JSPropCacheEntry **entryp); +/* The property cache does not need a destructor. */ +#define js_FinishPropertyCache(cache) ((void) 0) + extern void -js_FlushPropertyCache(JSContext *cx); +js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache); extern void -js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script); +js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script); extern void js_DisablePropertyCache(JSContext *cx); diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 0243f0254d..36f70a5260 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -101,10 +101,12 @@ typedef struct JSPropCacheEntry JSPropCacheEntry; typedef struct JSSharpObjectMap JSSharpObjectMap; typedef struct JSTempValueRooter JSTempValueRooter; typedef struct JSThread JSThread; +typedef struct JSThreadData JSThreadData; typedef struct JSToken JSToken; typedef struct JSTokenPos JSTokenPos; typedef struct JSTokenPtr JSTokenPtr; typedef struct JSTokenStream JSTokenStream; +typedef struct JSTraceMonitor JSTraceMonitor; typedef struct JSTreeContext JSTreeContext; typedef struct JSTryNote JSTryNote; typedef struct JSWeakRoots JSWeakRoots; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 386b0dec8f..058bf4f78c 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1578,13 +1578,13 @@ js_DestroyScript(JSContext *cx, JSScript *script) JSPRINCIPALS_DROP(cx, script->principals); if (JS_GSN_CACHE(cx).code == script->code) - JS_CLEAR_GSN_CACHE(cx); + JS_PURGE_GSN_CACHE(cx); /* * The GC flushes all property caches, so no need to purge just the * entries for this script. * - * JS_THREADSAFE note: js_FlushPropertyCacheForScript flushes only the + * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the * current thread's property cache, so a script not owned by a function * or object, which hands off lifetime management for that script to the * GC, must be used by only one thread over its lifetime. @@ -1605,9 +1605,9 @@ js_DestroyScript(JSContext *cx, JSScript *script) #ifdef CHECK_SCRIPT_OWNER JS_ASSERT(script->owner == cx->thread); #endif - js_FlushPropertyCacheForScript(cx, script); + js_PurgePropertyCacheForScript(cx, script); #ifdef JS_TRACER - js_FlushScriptFragments(cx, script); + js_PurgeScriptFragments(cx, script); #endif } } @@ -1676,6 +1676,17 @@ typedef struct GSNCacheEntry { #define GSN_CACHE_THRESHOLD 100 +void +js_PurgeGSNCache(JSGSNCache *cache) +{ + cache->code = NULL; + if (cache->table.ops) { + JS_DHashTableFinish(&cache->table); + cache->table.ops = NULL; + } + GSN_CACHE_METER(cache, purges); +} + jssrcnote * js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) { @@ -1713,7 +1724,7 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) if (JS_GSN_CACHE(cx).code != script->code && script->length >= GSN_CACHE_THRESHOLD) { - JS_CLEAR_GSN_CACHE(cx); + JS_PURGE_GSN_CACHE(cx); nsrcnotes = 0; for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 63330fb9a9..ca41351b54 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4629,19 +4629,17 @@ TraceRecorder::popAbortStack() } void -js_FlushJITOracle(JSContext* cx) +js_PurgeJITOracle() { - if (!TRACING_ENABLED(cx)) - return; oracle.clear(); } JS_REQUIRES_STACK void -js_FlushScriptFragments(JSContext* cx, JSScript* script) +js_PurgeScriptFragments(JSContext* cx, JSScript* script) { if (!TRACING_ENABLED(cx)) return; - debug_only_v(printf("Flushing fragments for JSScript %p.\n", (void*)script);) + debug_only_v(printf("Purging fragments for JSScript %p.\n", (void*)script);) JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { for (VMFragment **f = &(tm->vmfragments[i]); *f; ) { diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 0cbcb567bb..043de83904 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -639,13 +639,13 @@ extern void js_FinishJIT(JSTraceMonitor *tm); extern void -js_FlushScriptFragments(JSContext* cx, JSScript* script); +js_PurgeScriptFragments(JSContext* cx, JSScript* script); extern void js_FlushJITCache(JSContext* cx); extern void -js_FlushJITOracle(JSContext* cx); +js_PurgeJITOracle(); extern JSObject * js_GetBuiltinFunction(JSContext *cx, uintN index); diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index e2a236cd10..23917fb5c5 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1090,23 +1090,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) // these jsids filled in later when we have a JSContext to work with. mStrIDs[0] = 0; - // Call XPCPerThreadData::GetData to initialize - // XPCPerThreadData::gTLSIndex before initializing - // JSRuntime::threadTPIndex in JS_NewRuntime. - // - // XPConnect uses a thread local storage (XPCPerThreadData) indexed by - // XPCPerThreadData::gTLSIndex, and SpiderMonkey GC uses a thread local - // storage indexed by JSRuntime::threadTPIndex. - // - // The destructor for XPCPerThreadData::gTLSIndex may access - // thread local storage indexed by JSRuntime::threadTPIndex. - // Thus, the destructor for JSRuntime::threadTPIndex must be called - // later than the one for XPCPerThreadData::gTLSIndex. - // - // We rely on the implementation of NSPR that calls destructors at - // the same order of calling PR_NewThreadPrivateIndex. - XPCPerThreadData::GetData(nsnull); - mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L); // pref ? if(mJSRuntime) { -- 2.11.4.GIT