diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index acaaeb5b258..ffc80b4a18f 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -5794,56 +5794,21 @@ JS_PUBLIC_API(jsword) JS_SetContextThread(JSContext *cx) { #ifdef JS_THREADSAFE - JS_ASSERT(cx->requestDepth == 0); - if (cx->thread) { - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); - return cx->thread->id; - } - - JSRuntime *rt = cx->runtime; - JSThread *thread = js_GetCurrentThread(rt); - if (!thread) { - js_ReportOutOfMemory(cx); + jsword old = JS_THREAD_ID(cx); + if (!js_SetContextThread(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); -#endif + return old; +#else return 0; +#endif } JS_PUBLIC_API(jsword) JS_ClearContextThread(JSContext *cx) { #ifdef JS_THREADSAFE - /* - * This must be called outside a request and, if cx is associated with a - * thread, this must be called only from that thread. If not, this is a - * harmless no-op. - */ - JS_ASSERT(cx->requestDepth == 0); - if (!cx->thread) - return 0; - jsword old = cx->thread->id; - JS_ASSERT(old == js_CurrentThreadId()); - - /* - * We must not race with a GC that accesses cx->thread for all threads, - * see bug 476934. - */ - 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); + jsword old = JS_THREAD_ID(cx); + js_ClearContextThread(cx); return old; #else return 0; diff --git a/mozilla/js/src/jscntxt.c b/mozilla/js/src/jscntxt.c index 8b9bdd3d5f7..69e7995f6d8 100644 --- a/mozilla/js/src/jscntxt.c +++ b/mozilla/js/src/jscntxt.c @@ -146,7 +146,7 @@ js_GetCurrentThread(JSRuntime *rt) thread->gcMallocBytes = 0; /* - * js_InitContextThread initializes the remaining fields as necessary. + * js_SetContextThread initializes the remaining fields as necessary. */ } return thread; @@ -154,14 +154,18 @@ js_GetCurrentThread(JSRuntime *rt) /* * Sets current thread as owning thread of a context by assigning the - * thread-private info to the context. + * thread-private info to the context. If the current thread doesn't have + * private JSThread info, create one. */ -void -js_InitContextThread(JSContext *cx, JSThread *thread) +JSBool +js_SetContextThread(JSContext *cx) { - JS_ASSERT(CURRENT_THREAD_IS_ME(thread)); - JS_ASSERT(!cx->thread); - JS_ASSERT(cx->requestDepth == 0); + JSThread *thread = js_GetCurrentThread(cx->runtime); + + if (!thread) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } /* * Clear gcFreeLists and caches on each transition from 0 to 1 context @@ -173,8 +177,31 @@ js_InitContextThread(JSContext *cx, JSThread *thread) memset(&thread->propertyCache, 0, sizeof(thread->propertyCache)); } - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); + /* Assert that the previous cx->thread called JS_ClearContextThread(). */ + JS_ASSERT(!cx->thread || cx->thread == thread); + if (!cx->thread) + JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); cx->thread = thread; + return JS_TRUE; +} + +/* Remove the owning thread info of a context. */ +void +js_ClearContextThread(JSContext *cx) +{ + /* + * If cx is associated with a thread, this must be called only from that + * thread. If not, this is a harmless no-op. + */ + JS_ASSERT(cx->thread == js_GetCurrentThread(cx->runtime) || !cx->thread); + JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); +#ifdef DEBUG + if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) { + memset(cx->thread->gcFreeLists, JS_FREE_PATTERN, + sizeof(cx->thread->gcFreeLists)); + } +#endif + cx->thread = NULL; } #endif /* JS_THREADSAFE */ @@ -202,55 +229,33 @@ 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 - * runtime list. After that it can be accessed from another thread via - * js_ContextIterator. - */ - cx = (JSContext *) calloc(1, sizeof *cx); + cx = (JSContext *) malloc(sizeof *cx); if (!cx) return NULL; + memset(cx, 0, sizeof *cx); cx->runtime = rt; JS_ClearOperationCallback(cx); cx->debugHooks = &rt->globalDebugHooks; #if JS_STACK_GROWTH_DIRECTION > 0 - cx->stackLimit = (jsuword) -1; + cx->stackLimit = (jsuword)-1; #endif cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; #ifdef JS_THREADSAFE - js_InitContextThread(cx, thread); + JS_INIT_CLIST(&cx->threadLinks); + js_SetContextThread(cx); #endif - JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0); - JS_ASSERT(cx->version == JSVERSION_DEFAULT); - JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), - &cx->scriptStackQuota); - JS_INIT_ARENA_POOL(&cx->tempPool, "temp", 1024, sizeof(jsdouble), - &cx->scriptStackQuota); - - js_InitRegExpStatics(cx); JS_LOCK_GC(rt); for (;;) { - /* - * Ensure that we don't race with the GC on other threads, bug 478336. - */ - js_WaitForGC(rt); + first = (rt->contextList.next == &rt->contextList); if (rt->state == JSRTS_UP) { - JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList)); - first = JS_FALSE; + JS_ASSERT(!first); break; } if (rt->state == JSRTS_DOWN) { - JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList)); - first = JS_TRUE; + JS_ASSERT(first); rt->state = JSRTS_LAUNCHING; break; } @@ -259,6 +264,21 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_APPEND_LINK(&cx->links, &rt->contextList); JS_UNLOCK_GC(rt); + /* + * First we do the infallible, every-time per-context initializations. + * Should a later, fallible initialization (js_InitRegExpStatics, e.g., + * or the stuff under 'if (first)' below) fail, at least the version + * and arena-pools will be valid and safe to use (say, from the last GC + * done by js_DestroyContext). + */ + cx->version = JSVERSION_DEFAULT; + JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), + &cx->scriptStackQuota); + JS_INIT_ARENA_POOL(&cx->tempPool, "temp", 1024, sizeof(jsdouble), + &cx->scriptStackQuota); + + js_InitRegExpStatics(cx); + /* * If cx is the first context on this runtime, initialize well-known atoms, * keywords, numbers, and strings. If one of these steps should fail, the @@ -316,9 +336,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JSLocalRootStack *lrs; JSLocalRootChunk *lrc; -#ifdef JS_THREADSAFE - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); -#endif rt = cx->runtime; if (mode != JSDCM_NEW_FAILED) { @@ -336,16 +353,9 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) } } + /* Remove cx from context list first. */ JS_LOCK_GC(rt); JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); -#ifdef JS_THREADSAFE - /* - * Typically we are called outside a request, so ensure that the GC is not - * running before removing the context from rt->contextList, see bug 477021. - */ - if (cx->requestDepth == 0) - js_WaitForGC(rt); -#endif JS_REMOVE_LINK(&cx->links); last = (rt->contextList.next == &rt->contextList); if (last) @@ -354,7 +364,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) 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 @@ -390,6 +400,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) * 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. + * + * At this point, cx must be inaccessible to other threads. It's off the + * rt->contextList, and it should not be reachable via any object private + * data structure. */ while (cx->requestDepth != 0) JS_EndRequest(cx); @@ -449,13 +463,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) } #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); + js_ClearContextThread(cx); #endif /* Finally, free cx itself. */ diff --git a/mozilla/js/src/jscntxt.h b/mozilla/js/src/jscntxt.h index 991f76b65c1..0c9805e958a 100644 --- a/mozilla/js/src/jscntxt.h +++ b/mozilla/js/src/jscntxt.h @@ -134,8 +134,11 @@ struct JSThread { extern void JS_DLL_CALLBACK js_ThreadDestructorCB(void *ptr); +extern JSBool +js_SetContextThread(JSContext *cx); + extern void -js_InitContextThread(JSContext *cx, JSThread *thread); +js_ClearContextThread(JSContext *cx); extern JSThread * js_GetCurrentThread(JSRuntime *rt); diff --git a/mozilla/js/src/jsgc.c b/mozilla/js/src/jsgc.c index d39a5c27b45..38325cb63fd 100644 --- a/mozilla/js/src/jsgc.c +++ b/mozilla/js/src/jsgc.c @@ -1454,9 +1454,22 @@ js_AddRootRT(JSRuntime *rt, void *rp, const char *name) * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * rt->gcLock across the mark phase (including the root hashtable mark). + * + * If the GC is running and we're called on another thread, wait for this + * GC activation to finish. We can safely wait here (in the case where we + * are called within a request on another thread's context) without fear + * of deadlock because the GC doesn't set rt->gcRunning until after it has + * waited for all active requests to end. */ JS_LOCK_GC(rt); - js_WaitForGC(rt); +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); + if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { + do { + JS_AWAIT_GC_DONE(rt); + } while (rt->gcLevel > 0); + } +#endif rhe = (JSGCRootHashEntry *) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD); if (rhe) { @@ -1478,7 +1491,14 @@ js_RemoveRoot(JSRuntime *rt, void *rp) * Same synchronization drill as above in js_AddRoot. */ JS_LOCK_GC(rt); - js_WaitForGC(rt); +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); + if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { + do { + JS_AWAIT_GC_DONE(rt); + } while (rt->gcLevel > 0); + } +#endif (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); rt->gcPoke = JS_TRUE; JS_UNLOCK_GC(rt); @@ -3529,31 +3549,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) } } -#ifdef JS_THREADSAFE - -/* - * If the GC is running and we're called on another thread, wait for this GC - * activation to finish. We can safely wait here without fear of deadlock (in - * the case where we are called within a request on another thread's context) - * because the GC doesn't set rt->gcRunning until after it has waited for all - * active requests to end. - * - * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid - * expensive calls when the GC is not running. - */ -void -js_WaitForGC(JSRuntime *rt) -{ - JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcRunning); - } -} - -#endif - void js_UpdateMallocCounter(JSContext *cx, size_t nbytes) { diff --git a/mozilla/js/src/jsgc.h b/mozilla/js/src/jsgc.h index 12fddb23464..46351d09dde 100644 --- a/mozilla/js/src/jsgc.h +++ b/mozilla/js/src/jsgc.h @@ -296,20 +296,6 @@ typedef enum JSGCInvocationKind { extern void js_GC(JSContext *cx, JSGCInvocationKind gckind); - -/* - * This function must be called with the GC lock held. It is a helper for code - * that can potentially run outside JS request to ensure that the GC is not - * running when the function returns. - */ -#ifdef JS_THREADSAFE -extern void -js_WaitForGC(JSRuntime *rt); -#else -# define js_WaitForGC(rt) ((void) 0) -#endif - - /* Call this after succesful malloc of memory for GC-related things. */ extern void js_UpdateMallocCounter(JSContext *cx, size_t nbytes);