diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 639ccd6c5c1..620591fd7ec 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -780,6 +780,7 @@ JS_BeginRequest(JSContext *cx) /* Indicate that a request is running. */ rt->requestCount++; + cx->thread->contextsInRequests++; cx->requestDepth = 1; cx->outstandingRequests++; return; @@ -799,12 +800,14 @@ JS_EndRequest(JSContext *cx) JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); JS_ASSERT(cx->requestDepth > 0); JS_ASSERT(cx->outstandingRequests > 0); + JS_ASSERT(cx->thread->contextsInRequests > 0); if (cx->requestDepth == 1) { LeaveTrace(cx); /* for GC safety */ /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ rt = cx->runtime; AutoLockGC lock(rt); + cx->requestDepth = 0; cx->outstandingRequests--; @@ -813,11 +816,11 @@ JS_EndRequest(JSContext *cx) /* Give the GC a chance to run if this was the last request running. */ JS_ASSERT(rt->requestCount > 0); rt->requestCount--; + cx->thread->contextsInRequests--; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); return; } - cx->requestDepth--; cx->outstandingRequests--; #endif diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index cb9f86bf451..d905d040e67 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1247,48 +1247,6 @@ js_NextActiveContext(JSRuntime *rt, JSContext *cx) #endif } -#ifdef JS_THREADSAFE - -uint32 -js_CountThreadRequests(JSContext *cx) -{ - JSCList *head, *link; - uint32 nrequests; - - JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); - head = &cx->thread->contextList; - nrequests = 0; - for (link = head->next; link != head; link = link->next) { - JSContext *acx = CX_FROM_THREAD_LINKS(link); - JS_ASSERT(acx->thread == cx->thread); - if (acx->requestDepth) - nrequests++; - } - return nrequests; -} - -/* - * 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) -{ - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcRunning); - } -} - -#endif - static JSDHashNumber resolving_HashKey(JSDHashTable *table, const void *ptr) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 2ece93fa204..288f4545d98 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1076,6 +1076,12 @@ struct JSThread { */ bool gcWaiting; + /* + * Number of JSContext instances that are in requests on this thread. For + * such instances JSContext::requestDepth > 0 holds. + */ + uint32 contextsInRequests; + /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */ JSThreadData data; }; @@ -2705,29 +2711,6 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); extern JS_FRIEND_API(JSContext *) js_NextActiveContext(JSRuntime *, JSContext *); -#ifdef JS_THREADSAFE - -/* - * Count the number of contexts entered requests on the current thread. - */ -extern uint32 -js_CountThreadRequests(JSContext *cx); - -/* - * This is a helper for code at can potentially run outside JS request to - * ensure that the GC is not running when the function returns. - * - * This function must be called with the GC lock held. - */ -extern void -js_WaitForGC(JSRuntime *rt); - -#else /* !JS_THREADSAFE */ - -# define js_WaitForGC(rt) ((void) 0) - -#endif - /* * JSClass.resolve and watchpoint recursion damping machinery. */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 0b027c9b306..23d75fca14b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2941,6 +2941,27 @@ GC(JSContext *cx GCTIMER_PARAM) } #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->gcState to avoid + * an expensive call when the GC is not running. + */ +void +js_WaitForGC(JSRuntime *rt) +{ + if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { + do { + JS_AWAIT_GC_DONE(rt); + } while (rt->gcRunning); + } +} + /* * GC is running on another thread. Temporarily suspend all requests running * on the current thread and wait until the GC is done. @@ -2952,7 +2973,7 @@ LetOtherGCFinish(JSContext *cx) JS_ASSERT(rt->gcThread); JS_ASSERT(cx->thread != rt->gcThread); - size_t requestDebit = js_CountThreadRequests(cx); + size_t requestDebit = cx->thread->contextsInRequests; JS_ASSERT(requestDebit <= rt->requestCount); #ifdef JS_TRACER JS_ASSERT_IF(requestDebit == 0, !JS_ON_TRACE(cx)); @@ -3034,7 +3055,7 @@ BeginGCSession(JSContext *cx) * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on * rt->requestCount transitions to 0. */ - size_t requestDebit = js_CountThreadRequests(cx); + size_t requestDebit = cx->thread->contextsInRequests; JS_ASSERT_IF(cx->requestDepth != 0, requestDebit >= 1); JS_ASSERT(requestDebit <= rt->requestCount); if (requestDebit != rt->requestCount) { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 9077e24ba0e..29319d8e611 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -227,6 +227,22 @@ extern bool js_SetProtoOrParentCheckingForCycles(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj); +#ifdef JS_THREADSAFE +/* + * This is a helper for code at can potentially run outside JS request to + * ensure that the GC is not running when the function returns. + * + * This function must be called with the GC lock held. + */ +extern void +js_WaitForGC(JSRuntime *rt); + +#else /* !JS_THREADSAFE */ + +# define js_WaitForGC(rt) ((void) 0) + +#endif + extern void js_CallGCMarker(JSTracer *trc, void *thing, uint32 kind);