bug 730234 - remove GC locking from activities, operation callbacks-related code and for code that accesses the JSContext list. r=luke
authorIgor Bukanov <igor@mir2.org>
Fri, 24 Feb 2012 12:03:28 +0100
changeset 88580 dcb6daea6aefb9cb1d70cf5c99a4a253564a2f4f
parent 88579 1352b8b4848b1efcba97f14756a45228fd1794d6
child 88581 2175db811fadf2e7105bf1d9cdbf17e0acb03bee
push id591
push usertim.taubert@gmx.de
push dateMon, 12 Mar 2012 08:42:51 +0000
treeherderfx-team@c6f26a8dcd08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs730234
milestone13.0a1
bug 730234 - remove GC locking from activities, operation callbacks-related code and for code that accesses the JSContext list. r=luke
dom/workers/WorkerPrivate.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/Debugger.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2920,17 +2920,17 @@ WorkerPrivate::Dispatch(WorkerRunnable* 
       }
     }
 
     if (!aQueue->Push(event)) {
       return false;
     }
 
     if (aQueue == &mControlQueue && mJSContext) {
-      JS_TriggerOperationCallback(mJSContext);
+      JS_TriggerOperationCallback(JS_GetRuntime(mJSContext));
     }
 
     mCondVar.Notify();
   }
 
   event.forget();
   return true;
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1009,18 +1009,16 @@ static void
 StartRequest(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
 
     if (rt->requestDepth) {
         rt->requestDepth++;
     } else {
-        AutoLockGC lock(rt);
-
         /* Indicate that a request is running. */
         rt->requestDepth = 1;
 
         if (rt->activityCallback)
             rt->activityCallback(rt->activityCallbackArg, true);
     }
 }
 
@@ -1029,20 +1027,16 @@ StopRequest(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
     JS_ASSERT(rt->requestDepth != 0);
     if (rt->requestDepth != 1) {
         rt->requestDepth--;
     } else {
         rt->conservativeGC.updateForRequestEnd(rt->suspendCount);
-
-        /* Lock before clearing to interlock with ClaimScope, in jslock.c. */
-        AutoLockGC lock(rt);
-
         rt->requestDepth = 0;
 
         if (rt->activityCallback)
             rt->activityCallback(rt->activityCallbackArg, false);
     }
 }
 #endif /* JS_THREADSAFE */
 
@@ -1302,24 +1296,22 @@ SetOptionsCommon(JSContext *cx, unsigned
     cx->setCompileOptions(newcopts);
     cx->updateJITEnabled();
     return oldopts;
 }
 
 JS_PUBLIC_API(uint32_t)
 JS_SetOptions(JSContext *cx, uint32_t options)
 {
-    AutoLockGC lock(cx->runtime);
     return SetOptionsCommon(cx, options);
 }
 
 JS_PUBLIC_API(uint32_t)
 JS_ToggleOptions(JSContext *cx, uint32_t options)
 {
-    AutoLockGC lock(cx->runtime);
     unsigned oldopts = cx->allOptions();
     unsigned newopts = oldopts ^ options;
     return SetOptionsCommon(cx, newopts);
 }
 
 JS_PUBLIC_API(void)
 JS_SetJitHardening(JSRuntime *rt, JSBool enabled)
 {
@@ -2891,17 +2883,16 @@ JS_IsAboutToBeFinalized(void *thing)
     return IsAboutToBeFinalized(t);
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
 {
     switch (key) {
       case JSGC_MAX_BYTES: {
-        AutoLockGC lock(rt);
         JS_ASSERT(value >= rt->gcBytes);
         rt->gcMaxBytes = value;
         break;
       }
       case JSGC_MAX_MALLOC_BYTES:
         rt->setGCMaxMallocBytes(value);
         break;
       case JSGC_SLICE_TIME_BUDGET:
@@ -5518,30 +5509,18 @@ JS_SetOperationCallback(JSContext *cx, J
 
 JS_PUBLIC_API(JSOperationCallback)
 JS_GetOperationCallback(JSContext *cx)
 {
     return cx->operationCallback;
 }
 
 JS_PUBLIC_API(void)
-JS_TriggerOperationCallback(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
-    AutoLockGC lock(cx->runtime);
-#endif
-    cx->runtime->triggerOperationCallback();
-}
-
-JS_PUBLIC_API(void)
-JS_TriggerRuntimeOperationCallback(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
-    AutoLockGC lock(rt);
-#endif
+JS_TriggerOperationCallback(JSRuntime *rt)
+{
     rt->triggerOperationCallback();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx)
 {
     StackFrame *fp = cx->maybefp();
     while (fp && fp->isDummyFrame())
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4499,42 +4499,35 @@ Call(JSContext *cx, jsval thisv, JSObjec
 
 } /* namespace JS */
 
 JS_BEGIN_EXTERN_C
 #endif /* __cplusplus */
 
 /*
  * These functions allow setting an operation callback that will be called
- * from the thread the context is associated with some time after any thread
- * triggered the callback using JS_TriggerOperationCallback(cx).
+ * from the JS thread some time after any thread triggered the callback using
+ * JS_TriggerOperationCallback(rt).
  *
- * In a threadsafe build the engine internally triggers operation callbacks
- * under certain circumstances (i.e. GC and title transfer) to force the
- * context to yield its current request, which the engine always
- * automatically does immediately prior to calling the callback function.
- * The embedding should thus not rely on callbacks being triggered through
- * the external API only.
+ * To schedule the GC and for other activities the engine internally triggers
+ * operation callbacks. The embedding should thus not rely on callbacks being
+ * triggered through the external API only.
  *
  * Important note: Additional callbacks can occur inside the callback handler
  * if it re-enters the JS engine. The embedding must ensure that the callback
  * is disconnected before attempting such re-entry.
  */
-
 extern JS_PUBLIC_API(JSOperationCallback)
 JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback);
 
 extern JS_PUBLIC_API(JSOperationCallback)
 JS_GetOperationCallback(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
-JS_TriggerOperationCallback(JSContext *cx);
-
-extern JS_PUBLIC_API(void)
-JS_TriggerRuntimeOperationCallback(JSRuntime *rt);
+JS_TriggerOperationCallback(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx);
 
 /*
  * Saving and restoring frame chains.
  *
  * These two functions are used to set aside cx's call stack while that stack
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -113,17 +113,17 @@ JSRuntime::sizeOfExcludingThis(JSMallocS
 
     if (stackCommitted)
         *stackCommitted = stackSpace.sizeOfCommitted();
 
     if (gcMarkerSize)
         *gcMarkerSize = gcMarker.sizeOfExcludingThis(mallocSizeOf);
 }
 
-JS_FRIEND_API(void)
+void
 JSRuntime::triggerOperationCallback()
 {
     /*
      * Use JS_ATOMIC_SET in the hope that it ensures the write will become
      * immediately visible to other processors polling the flag.
      */
     JS_ATOMIC_SET(&interrupt, 1);
 }
@@ -245,59 +245,58 @@ js_DestroyContext(JSContext *cx, JSDestr
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
              * return true.
              */
             DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY);
             JS_ASSERT(callbackStatus);
         }
     }
 
-    JS_LOCK_GC(rt);
     JS_REMOVE_LINK(&cx->link);
     bool last = !rt->hasContexts();
-    if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC) {
+    if (last) {
         JS_ASSERT(!rt->gcRunning);
 
 #ifdef JS_THREADSAFE
-        rt->gcHelperThread.waitBackgroundSweepEnd();
+        {
+            AutoLockGC lock(rt);
+            rt->gcHelperThread.waitBackgroundSweepEnd();
+        }
 #endif
-        JS_UNLOCK_GC(rt);
-
-        if (last) {
-            /*
-             * Dump remaining type inference results first. This printing
-             * depends on atoms still existing.
-             */
-            {
-                AutoLockGC lock(rt);
-                for (CompartmentsIter c(rt); !c.done(); c.next())
-                    c->types.print(cx, false);
-            }
-
-            /* Unpin all common atoms before final GC. */
-            js_FinishCommonAtoms(cx);
+        
+        /*
+         * Dump remaining type inference results first. This printing
+         * depends on atoms still existing.
+         */
+        for (CompartmentsIter c(rt); !c.done(); c.next())
+            c->types.print(cx, false);
 
-            /* Clear debugging state to remove GC roots. */
-            for (CompartmentsIter c(rt); !c.done(); c.next())
-                c->clearTraps(cx);
-            JS_ClearAllWatchPoints(cx);
-
-            GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT);
+        /* Unpin all common atoms before final GC. */
+        js_FinishCommonAtoms(cx);
+        
+        /* Clear debugging state to remove GC roots. */
+        for (CompartmentsIter c(rt); !c.done(); c.next())
+            c->clearTraps(cx);
+        JS_ClearAllWatchPoints(cx);
+        
+        GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT);
+    } else if (mode == JSDCM_FORCE_GC) {
+        JS_ASSERT(!rt->gcRunning);
+        GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT);
+    } else if (mode == JSDCM_MAYBE_GC) {
+        JS_ASSERT(!rt->gcRunning);
+        JS_MaybeGC(cx);
+    }
 
-        } else if (mode == JSDCM_FORCE_GC) {
-            GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT);
-        } else if (mode == JSDCM_MAYBE_GC) {
-            JS_MaybeGC(cx);
-        }
-        JS_LOCK_GC(rt);
+#ifdef JS_THREADSAFE
+    {
+        AutoLockGC lock(rt);
+        rt->gcHelperThread.waitBackgroundSweepEnd();
     }
-#ifdef JS_THREADSAFE
-    rt->gcHelperThread.waitBackgroundSweepEnd();
 #endif
-    JS_UNLOCK_GC(rt);
     Foreground::delete_(cx);
 }
 
 namespace js {
 
 bool
 AutoResolving::alreadyStartedSlow() const
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -681,17 +681,17 @@ struct JSRuntime : js::RuntimeFriendFiel
      * to recove some memory or to report an error. Failures in malloc and
      * calloc are signaled by p == null and p == reinterpret_cast<void *>(1).
      * Other values of p mean a realloc failure.
      *
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 
-    JS_FRIEND_API(void) triggerOperationCallback();
+    void triggerOperationCallback();
 
     void setJitHardening(bool enabled);
     bool getJitHardening() const {
         return jitHardening;
     }
 
     void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
                              size_t *regexpCode, size_t *stackCommitted, size_t *gcMarker);
@@ -1242,16 +1242,50 @@ class AutoXMLRooter : private AutoGCRoot
 #ifdef JS_THREADSAFE
 # define JS_LOCK_GC(rt)    PR_Lock((rt)->gcLock)
 # define JS_UNLOCK_GC(rt)  PR_Unlock((rt)->gcLock)
 #else
 # define JS_LOCK_GC(rt)
 # define JS_UNLOCK_GC(rt)
 #endif
 
+class AutoLockGC
+{
+  public:
+    explicit AutoLockGC(JSRuntime *rt = NULL
+                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : runtime(rt)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        if (rt)
+            JS_LOCK_GC(rt);
+    }
+
+    ~AutoLockGC()
+    {
+        if (runtime)
+            JS_UNLOCK_GC(runtime);
+    }
+
+    bool locked() const {
+        return !!runtime;
+    }
+
+    void lock(JSRuntime *rt) {
+        JS_ASSERT(rt);
+        JS_ASSERT(!runtime);
+        runtime = rt;
+        JS_LOCK_GC(rt);
+    }
+
+  private:
+    JSRuntime *runtime;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 class AutoUnlockGC {
   private:
     JSRuntime *rt;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoUnlockGC(JSRuntime *rt
                           JS_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -604,39 +604,16 @@ js::DumpHeapComplete(JSRuntime *rt, FILE
     dtrc.visited.finish();
     fflush(dtrc.output);
 }
 
 #endif
 
 namespace js {
 
-/* static */ void
-AutoLockGC::LockGC(JSRuntime *rt)
-{
-    JS_ASSERT(rt);
-    JS_LOCK_GC(rt);
-}
-
-/* static */ void
-AutoLockGC::UnlockGC(JSRuntime *rt)
-{
-    JS_ASSERT(rt);
-    JS_UNLOCK_GC(rt);
-}
-
-void
-AutoLockGC::lock(JSRuntime *rt)
-{
-    JS_ASSERT(rt);
-    JS_ASSERT(!runtime);
-    runtime = rt;
-    JS_LOCK_GC(rt);
-}
-
 JS_FRIEND_API(const JSStructuredCloneCallbacks *)
 GetContextStructuredCloneCallbacks(JSContext *cx)
 {
     return cx->runtime->structuredCloneCallbacks;
 }
 
 JS_FRIEND_API(JSVersion)
 VersionSetXML(JSVersion version, bool enable)
@@ -669,22 +646,16 @@ GetOwnerThread(const JSContext *cx)
 }
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx)
 {
     return cx->outstandingRequests;
 }
 
-JS_FRIEND_API(PRLock *)
-GetRuntimeGCLock(const JSRuntime *rt)
-{
-    return rt->gcLock;
-}
-
 AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : context(cx)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
     JSRuntime *rt = context->runtime;
     JS_ASSERT(rt->requestDepth >= 1);
@@ -721,22 +692,16 @@ SetActivityCallback(JSRuntime *rt, Activ
 }
 
 JS_FRIEND_API(bool)
 IsContextRunningJS(JSContext *cx)
 {
     return !cx->stack.empty();
 }
 
-JS_FRIEND_API(void)
-TriggerOperationCallback(JSRuntime *rt)
-{
-    rt->triggerOperationCallback();
-}
-
 JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt)
 {
     return rt->compartments;
 }
 
 JS_FRIEND_API(size_t)
 SizeOfJSContext()
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -182,18 +182,16 @@ extern JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs);
 
 #endif
 
 JS_END_EXTERN_C
 
 #ifdef __cplusplus
 
-struct PRLock;
-
 namespace js {
 
 struct ContextFriendFields {
     JSRuntime *const    runtime;
 
     ContextFriendFields(JSRuntime *rt)
       : runtime(rt) { }
 
@@ -568,19 +566,16 @@ GetPCCountScriptContents(JSContext *cx, 
 
 #ifdef JS_THREADSAFE
 JS_FRIEND_API(void *)
 GetOwnerThread(const JSContext *cx);
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx);
 
-JS_FRIEND_API(PRLock *)
-GetRuntimeGCLock(const JSRuntime *rt);
-
 class JS_FRIEND_API(AutoSkipConservativeScan)
 {
   public:
     AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoSkipConservativeScan();
 
   private:
     JSContext *context;
@@ -595,72 +590,36 @@ JS_FRIEND_API(bool)
 HasUnrootedGlobal(const JSContext *cx);
 
 typedef void
 (* ActivityCallback)(void *arg, JSBool active);
 
 /*
  * Sets a callback that is run whenever the runtime goes idle - the
  * last active request ceases - and begins activity - when it was
- * idle and a request begins. Note: The callback is called under the
- * GC lock.
+ * idle and a request begins.
  */
 JS_FRIEND_API(void)
 SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg);
 
-class JS_FRIEND_API(AutoLockGC)
-{
-  public:
-    explicit AutoLockGC(JSRuntime *rt = NULL
-                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : runtime(rt)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        if (rt)
-            LockGC(rt);
-    }
-
-    ~AutoLockGC()
-    {
-        if (runtime)
-            UnlockGC(runtime);
-    }
-
-    bool locked() const {
-        return !!runtime;
-    }
-    void lock(JSRuntime *rt);
-
-  private:
-    static void LockGC(JSRuntime *rt);
-    static void UnlockGC(JSRuntime *rt);
-
-    JSRuntime *runtime;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 extern JS_FRIEND_API(const JSStructuredCloneCallbacks *)
 GetContextStructuredCloneCallbacks(JSContext *cx);
 
 extern JS_FRIEND_API(JSVersion)
 VersionSetXML(JSVersion version, bool enable);
 
 extern JS_FRIEND_API(bool)
 CanCallContextDebugHandler(JSContext *cx);
 
 extern JS_FRIEND_API(JSTrapStatus)
 CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval);
 
 extern JS_FRIEND_API(bool)
 IsContextRunningJS(JSContext *cx);
 
-/* Must be called with GC lock taken. */
-extern JS_FRIEND_API(void)
-TriggerOperationCallback(JSRuntime *rt);
-
 class SystemAllocPolicy;
 typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
 extern JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt);
 
 extern JS_FRIEND_API(size_t)
 SizeOfJSContext();
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1303,53 +1303,30 @@ js_AddGCThingRoot(JSContext *cx, void **
     if (!ok)
         JS_ReportOutOfMemory(cx);
     return ok;
 }
 
 JS_FRIEND_API(JSBool)
 js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name)
 {
-    /*
-     * Due to the long-standing, but now removed, use of rt->gcLock across the
-     * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking
-     * 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).
-     */
-    AutoLockGC lock(rt);
-
     return !!rt->gcRootsHash.put((void *)vp,
                                  RootInfo(name, JS_GC_ROOT_VALUE_PTR));
 }
 
 JS_FRIEND_API(JSBool)
 js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
 {
-    /*
-     * Due to the long-standing, but now removed, use of rt->gcLock across the
-     * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking
-     * 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).
-     */
-    AutoLockGC lock(rt);
-
     return !!rt->gcRootsHash.put((void *)rp,
                                  RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
 }
 
 JS_FRIEND_API(JSBool)
 js_RemoveRoot(JSRuntime *rt, void *rp)
 {
-    /*
-     * Due to the JS_RemoveRootRT API, we may be called outside of a request.
-     * Same synchronization drill as above in js_AddRoot.
-     */
-    AutoLockGC lock(rt);
     rt->gcRootsHash.remove(rp);
     rt->gcPoke = JS_TRUE;
     return JS_TRUE;
 }
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
@@ -1399,17 +1376,16 @@ js_DumpNamedRoots(JSRuntime *rt,
     }
 }
 
 #endif /* DEBUG */
 
 uint32_t
 js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
 {
-    AutoLockGC lock(rt);
     int ct = 0;
     for (RootEnum e(rt->gcRootsHash); !e.empty(); e.popFront()) {
         RootEntry &entry = e.front();
 
         ct++;
         int mapflags = map(entry.key, entry.value.type, entry.value.name, data);
 
         if (mapflags & JS_MAP_GCROOT_REMOVE)
@@ -1790,35 +1766,31 @@ js_GetGCThingTraceKind(void *thing)
 }
 
 JSBool
 js_LockGCThingRT(JSRuntime *rt, void *thing)
 {
     if (!thing)
         return true;
 
-    AutoLockGC lock(rt);
     if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) {
         p->value++;
         return true;
     }
 
     return false;
 }
 
 void
 js_UnlockGCThingRT(JSRuntime *rt, void *thing)
 {
     if (!thing)
         return;
 
-    AutoLockGC lock(rt);
-    GCLocks::Ptr p = rt->gcLocksHash.lookup(thing);
-
-    if (p) {
+    if (GCLocks::Ptr p = rt->gcLocksHash.lookup(thing)) {
         rt->gcPoke = true;
         if (--p->value == 0)
             rt->gcLocksHash.remove(p);
     }
 }
 
 namespace js {
 
@@ -2412,16 +2384,17 @@ MarkRuntime(JSTracer *trc, bool useSaved
             gcmarker->startBufferingGrayRoots();
             (*op)(trc, rt->gcGrayRootsData);
             gcmarker->endBufferingGrayRoots();
         } else {
             (*op)(trc, rt->gcGrayRootsData);
         }
     }
 }
+
 void
 TriggerGC(JSRuntime *rt, gcreason::Reason reason)
 {
     JS_ASSERT(rt->onOwnerThread());
 
     if (rt->gcRunning || rt->gcIsNeeded)
         return;
 
@@ -2458,17 +2431,17 @@ TriggerCompartmentGC(JSCompartment *comp
 
     /*
      * Trigger the GC when it is safe to call an operation callback on any
      * thread.
      */
     rt->gcIsNeeded = true;
     rt->gcTriggerCompartment = comp;
     rt->gcTriggerReason = reason;
-    comp->rt->triggerOperationCallback();
+    rt->triggerOperationCallback();
 }
 
 void
 MaybeGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
 
@@ -4511,34 +4484,32 @@ ReleaseScriptPCCounters(JSContext *cx)
     cx->delete_(rt->scriptPCCounters);
     rt->scriptPCCounters = NULL;
 }
 
 JS_FRIEND_API(void)
 StartPCCountProfiling(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
 
     if (rt->profilingScripts)
         return;
 
     if (rt->scriptPCCounters)
         ReleaseScriptPCCounters(cx);
 
     ReleaseAllJITCode(cx);
 
     rt->profilingScripts = true;
 }
 
 JS_FRIEND_API(void)
 StopPCCountProfiling(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
 
     if (!rt->profilingScripts)
         return;
     JS_ASSERT(!rt->scriptPCCounters);
 
     ReleaseAllJITCode(cx);
 
     ScriptOpcodeCountsVector *vec = cx->new_<ScriptOpcodeCountsVector>(SystemAllocPolicy());
@@ -4561,17 +4532,16 @@ StopPCCountProfiling(JSContext *cx)
     rt->profilingScripts = false;
     rt->scriptPCCounters = vec;
 }
 
 JS_FRIEND_API(void)
 PurgePCCounts(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
 
     if (!rt->scriptPCCounters)
         return;
     JS_ASSERT(!rt->profilingScripts);
 
     ReleaseScriptPCCounters(cx);
 }
 
@@ -4580,16 +4550,20 @@ PurgePCCounts(JSContext *cx)
 JS_PUBLIC_API(void)
 JS_IterateCompartments(JSRuntime *rt, void *data,
                        JSIterateCompartmentCallback compartmentCallback)
 {
     JS_ASSERT(!rt->gcRunning);
 
     AutoLockGC lock(rt);
     AutoHeapSession session(rt);
+#ifdef JS_THREADSAFE
+    rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
+#endif
+    AutoUnlockGC unlock(rt);
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         (*compartmentCallback)(rt, data, c);
 }
 
 #if JS_HAS_XML_SUPPORT
 extern size_t sE4XObjectsCreated;
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2174,27 +2174,16 @@ TypeCompartment::nukeTypes(JSContext *cx
      * inconsistent state.
      */
     JS_ASSERT(pendingNukeTypes);
     if (pendingRecompiles) {
         cx->free_(pendingRecompiles);
         pendingRecompiles = NULL;
     }
 
-    /*
-     * We may or may not be under the GC. In either case don't allocate, and
-     * acquire the GC lock so we can update inferenceEnabled for all contexts.
-     */
-
-#ifdef JS_THREADSAFE
-    AutoLockGC maybeLock;
-    if (!cx->runtime->gcRunning)
-        maybeLock.lock(cx->runtime);
-#endif
-
     inferenceEnabled = false;
 
     /* Update the cached inferenceEnabled bit in all contexts. */
     for (ContextIter acx(cx->runtime); !acx.done(); acx.next())
         acx->setCompartment(acx->compartment);
 
 #ifdef JS_METHODJIT
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3129,17 +3129,17 @@ CancelExecution(JSRuntime *rt)
 {
     gCanceled = true;
     if (gExitCode == 0)
         gExitCode = EXITCODE_TIMEOUT;
 #ifdef JS_THREADSAFE
     if (gWorkerThreadPool)
         js::workers::terminateAll(gWorkerThreadPool);
 #endif
-    JS_TriggerRuntimeOperationCallback(rt);
+    JS_TriggerOperationCallback(rt);
 
     static const char msg[] = "Script runs for too long, terminating.\n";
 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
     /* It is not safe to call fputs from signals. */
     /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
     ssize_t dummy = write(2, msg, sizeof(msg) - 1);
     (void)dummy;
 #else
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -822,17 +822,17 @@ class Worker MOZ_FINAL : public WorkerPa
             return false;
         return events.push(event);
     }
 
     void setTerminateFlag() {
         AutoLock hold(lock);
         terminateFlag = true;
         if (current)
-            JS_TriggerOperationCallback(context);
+            JS_TriggerOperationCallback(runtime);
     }
 
     void notifyTerminating() {
         setTerminateFlag();
         WorkerParent::notifyTerminating();
     }
 
     void processOneEvent();
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -424,17 +424,16 @@ Breakpoint::nextInSite()
 
 Debugger::Debugger(JSContext *cx, JSObject *dbg)
   : object(dbg), uncaughtExceptionHook(NULL), enabled(true),
     frames(cx), scripts(cx), objects(cx), environments(cx)
 {
     assertSameCompartment(cx, dbg);
 
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
     JS_APPEND_LINK(&link, &rt->debuggerList);
     JS_INIT_CLIST(&breakpoints);
 }
 
 Debugger::~Debugger()
 {
     JS_ASSERT(debuggees.empty());
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -921,51 +921,64 @@ XPCJSRuntime::FinalizeCallback(JSContext
                 xpc_NotifyAll(self->GetMapLock());
             }
 
             break;
         }
     }
 }
 
+class AutoLockWatchdog {
+    XPCJSRuntime* const mRuntime;
+
+  public:
+    AutoLockWatchdog(XPCJSRuntime* aRuntime)
+      : mRuntime(aRuntime) {
+        PR_Lock(mRuntime->mWatchdogLock);
+    }
+
+    ~AutoLockWatchdog() {
+        PR_Unlock(mRuntime->mWatchdogLock);
+    }
+};
+
 //static
 void
 XPCJSRuntime::WatchdogMain(void *arg)
 {
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
 
     // Lock lasts until we return
-    js::AutoLockGC lock(self->mJSRuntime);
+    AutoLockWatchdog lock(self);
 
     PRIntervalTime sleepInterval;
     while (self->mWatchdogThread) {
         // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
         if (self->mLastActiveTime == -1 || PR_Now() - self->mLastActiveTime <= PRTime(2*PR_USEC_PER_SEC))
             sleepInterval = PR_TicksPerSecond();
         else {
             sleepInterval = PR_INTERVAL_NO_TIMEOUT;
             self->mWatchdogHibernating = true;
         }
-#ifdef DEBUG
-        PRStatus status =
-#endif
-            PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval);
-        JS_ASSERT(status == PR_SUCCESS);
-        js::TriggerOperationCallback(self->mJSRuntime);
+        MOZ_ALWAYS_TRUE(PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval) == PR_SUCCESS);
+        JS_TriggerOperationCallback(self->mJSRuntime);
     }
 
     /* Wake up the main thread waiting for the watchdog to terminate. */
     PR_NotifyCondVar(self->mWatchdogWakeup);
 }
 
 //static
 void
 XPCJSRuntime::ActivityCallback(void *arg, JSBool active)
 {
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
+
+    AutoLockWatchdog lock(self);
+    
     if (active) {
         self->mLastActiveTime = -1;
         if (self->mWatchdogHibernating) {
             self->mWatchdogHibernating = false;
             PR_NotifyCondVar(self->mWatchdogWakeup);
         }
     } else {
         self->mLastActiveTime = PR_Now();
@@ -1053,24 +1066,25 @@ XPCJSRuntime::GetJSCycleCollectionContex
 XPCJSRuntime::~XPCJSRuntime()
 {
     if (mWatchdogWakeup) {
         // If the watchdog thread is running, tell it to terminate waking it
         // up if necessary and wait until it signals that it finished. As we
         // must release the lock before calling PR_DestroyCondVar, we use an
         // extra block here.
         {
-            js::AutoLockGC lock(mJSRuntime);
+            AutoLockWatchdog lock(this);
             if (mWatchdogThread) {
                 mWatchdogThread = nsnull;
                 PR_NotifyCondVar(mWatchdogWakeup);
                 PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
             }
         }
         PR_DestroyCondVar(mWatchdogWakeup);
+        PR_DestroyLock(mWatchdogLock);
         mWatchdogWakeup = nsnull;
     }
 
     if (mJSCycleCollectionContext)
         JS_DestroyContextNoGC(mJSCycleCollectionContext);
 
 #ifdef XPC_DUMP_AT_SHUTDOWN
     {
@@ -2024,16 +2038,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
    mThreadRunningGC(nsnull),
    mWrappedJSToReleaseArray(),
    mNativesToReleaseArray(),
    mDoingFinalization(false),
    mVariantRoots(nsnull),
    mWrappedJSRoots(nsnull),
    mObjectHolderRoots(nsnull),
+   mWatchdogLock(nsnull),
    mWatchdogWakeup(nsnull),
    mWatchdogThread(nsnull),
    mWatchdogHibernating(false),
    mLastActiveTime(-1)
 {
 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
     DEBUG_WrappedNativeHashtable =
         JS_NewDHashTable(JS_DHashGetStubOps(), nsnull,
@@ -2048,72 +2063,72 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
 
     // these jsids filled in later when we have a JSContext to work with.
     mStrIDs[0] = JSID_VOID;
 
     mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L); // pref ?
     if (!mJSRuntime)
         NS_RUNTIMEABORT("JS_NewRuntime failed.");
 
-    {
-        // Unconstrain the runtime's threshold on nominal heap size, to avoid
-        // triggering GC too often if operating continuously near an arbitrary
-        // finite threshold (0xffffffff is infinity for uint32_t parameters).
-        // This leaves the maximum-JS_malloc-bytes threshold still in effect
-        // to cause period, and we hope hygienic, last-ditch GCs from within
-        // the GC's allocator.
-        JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
+    // Unconstrain the runtime's threshold on nominal heap size, to avoid
+    // triggering GC too often if operating continuously near an arbitrary
+    // finite threshold (0xffffffff is infinity for uint32_t parameters).
+    // This leaves the maximum-JS_malloc-bytes threshold still in effect
+    // to cause period, and we hope hygienic, last-ditch GCs from within
+    // the GC's allocator.
+    JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
 #ifdef MOZ_ASAN
-        // ASan requires more stack space due to redzones
-        JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024);
+    // ASan requires more stack space due to redzones
+    JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024);
 #else  
-        JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024);
+    JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024);
 #endif
-        JS_SetContextCallback(mJSRuntime, ContextCallback);
-        JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
-        JS_SetGCCallback(mJSRuntime, GCCallback);
-        JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback);
-        JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
-        JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
-        JS_SetWrapObjectCallbacks(mJSRuntime,
-                                  xpc::WrapperFactory::Rewrap,
-                                  xpc::WrapperFactory::PrepareForWrapping);
-        js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
-
+    JS_SetContextCallback(mJSRuntime, ContextCallback);
+    JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
+    JS_SetGCCallback(mJSRuntime, GCCallback);
+    JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback);
+    JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
+    JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
+    JS_SetWrapObjectCallbacks(mJSRuntime,
+                              xpc::WrapperFactory::Rewrap,
+                              xpc::WrapperFactory::PrepareForWrapping);
+    js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
 #ifdef MOZ_CRASHREPORTER
-        JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
+    JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
 #endif
-        JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
-        mWatchdogWakeup = PR_NewCondVar(js::GetRuntimeGCLock(mJSRuntime));
-        if (!mWatchdogWakeup)
-            NS_RUNTIMEABORT("PR_NewCondVar failed.");
-
-        js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
-
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
-        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
-        NS_RegisterMemoryMultiReporter(new JSMemoryMultiReporter);
-        NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter);
-    }
+    JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
+    js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
+        
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
+    NS_RegisterMemoryMultiReporter(new JSMemoryMultiReporter);
+    NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter);
 
     if (!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
                            sizeof(ObjectHolder), 512))
         mJSHolders.ops = nsnull;
 
     mCompartmentMap.Init();
 
     // Install a JavaScript 'debugger' keyword handler in debug builds only
 #ifdef DEBUG
-    if (mJSRuntime && !JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler)
+    if (!JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler)
         xpc_InstallJSDebuggerKeywordHandler(mJSRuntime);
 #endif
 
-    if (mWatchdogWakeup) {
-        js::AutoLockGC lock(mJSRuntime);
+    mWatchdogLock = PR_NewLock();
+    if (!mWatchdogLock)
+        NS_RUNTIMEABORT("PR_NewLock failed.");
+    mWatchdogWakeup = PR_NewCondVar(mWatchdogLock);
+    if (!mWatchdogWakeup)
+        NS_RUNTIMEABORT("PR_NewCondVar failed.");
+
+    {
+        AutoLockWatchdog lock(this);
 
         mWatchdogThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
                                           PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
                                           PR_UNJOINABLE_THREAD, 0);
         if (!mWatchdogThread)
             NS_RUNTIMEABORT("PR_CreateThread failed!");
     }
 }
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -832,21 +832,24 @@ private:
     PRThread* mThreadRunningGC;
     nsTArray<nsXPCWrappedJS*> mWrappedJSToReleaseArray;
     nsTArray<nsISupports*> mNativesToReleaseArray;
     JSBool mDoingFinalization;
     XPCRootSetElem *mVariantRoots;
     XPCRootSetElem *mWrappedJSRoots;
     XPCRootSetElem *mObjectHolderRoots;
     JSDHashTable mJSHolders;
+    PRLock *mWatchdogLock;
     PRCondVar *mWatchdogWakeup;
     PRThread *mWatchdogThread;
     nsTArray<JSGCCallback> extraGCCallbacks;
     bool mWatchdogHibernating;
     PRTime mLastActiveTime; // -1 if active NOW
+
+    friend class AutoLockWatchdog;
 };
 
 /***************************************************************************/
 /***************************************************************************/
 // XPCContext is mostly a dumb class to hold JSContext specific data and
 // maps that let us find wrappers created for the given JSContext.
 
 // no virtuals