Bug 580096 - 'move the cycle collector off the main thread'. r=peterv+jst
authorBen Turner <bent.mozilla@gmail.com>
Thu, 11 Nov 2010 14:52:30 -0800
changeset 57794 b07a1861acf2539b81bda452854497266c89555a
parent 57793 d3f5185f03927e69dc7c29322294a00588ebc14c
child 57795 638acb1aac5272a8315539d821b64555f5fb88c0
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewerspeterv
bugs580096
milestone2.0b8pre
Bug 580096 - 'move the cycle collector off the main thread'. r=peterv+jst
content/xbl/src/nsBindingManager.cpp
content/xbl/src/nsXBLBinding.cpp
dom/base/nsGlobalWindow.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpccallcontext.cpp
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcconvert.cpp
js/src/xpconnect/src/xpcinlines.h
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcquickstubs.cpp
js/src/xpconnect/src/xpcquickstubs.h
js/src/xpconnect/src/xpcvariant.cpp
js/src/xpconnect/src/xpcwrappedjs.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
xpcom/build/nsXPComInit.cpp
xpcom/glue/Makefile.in
xpcom/glue/nsCycleCollectionParticipant.cpp
xpcom/glue/nsCycleCollectionParticipant.h
xpcom/glue/nsCycleCollectorUtils.cpp
xpcom/glue/nsCycleCollectorUtils.h
xpcom/glue/nsISupportsImpl.h
xpcom/glue/nsThreadIDs.h
xpcom/glue/nsThreadUtils.cpp
xpcom/glue/nsThreadUtils.h
xpcom/glue/objs.mk
xpcom/threads/nsThreadManager.cpp
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -399,18 +399,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 static PLDHashOperator
 DocumentInfoHashtableTraverser(nsIURI* key,
                                nsXBLDocumentInfo* di,
                                void* userArg)
 {
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(userArg);
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mDocumentTable value");
-  nsCOMPtr<nsISupports> iface = do_QueryObject(di);
-  cb->NoteXPCOMChild(iface);
+  cb->NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(di));
   return PL_DHASH_NEXT;
 }
 
 static PLDHashOperator
 LoadingDocHashtableTraverser(nsIURI* key,
                              nsIStreamListener* sl,
                              void* userArg)
 {
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -311,18 +311,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NA
   if (tmp->mContent) {
     nsXBLBinding::UninstallAnonymousContent(tmp->mContent->GetOwnerDoc(),
                                             tmp->mContent);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContent)
   // XXX What about mNextBinding and mInsertionPointTable?
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLBinding)
-  nsCOMPtr<nsISupports> iface = do_QueryObject(tmp->mPrototypeBinding->XBLDocumentInfo());
-  cb.NoteXPCOMChild(iface);
+  cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(
+                      tmp->mPrototypeBinding->XBLDocumentInfo()));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNextBinding, nsXBLBinding)
   if (tmp->mInsertionPointTable)
     tmp->mInsertionPointTable->EnumerateRead(TraverseKey, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -937,19 +937,19 @@ nsGlobalWindow::ShutDown()
 // static
 void
 nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow)
 {
   if (aWindow->mCachedXBLPrototypeHandlers.IsInitialized() &&
       aWindow->mCachedXBLPrototypeHandlers.Count() > 0) {
     aWindow->mCachedXBLPrototypeHandlers.Clear();
 
-    nsCOMPtr<nsISupports> supports;
+    nsISupports* supports;
     aWindow->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                            getter_AddRefs(supports));
+                            reinterpret_cast<void**>(&supports));
     NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!");
 
     nsContentUtils::DropJSObjects(supports);
   }
 }
 
 void
 nsGlobalWindow::MaybeForgiveSpamCount()
@@ -6387,19 +6387,19 @@ nsGlobalWindow::CacheXBLPrototypeHandler
   if (!mCachedXBLPrototypeHandlers.Count()) {
     // Can't use macros to get the participant because nsGlobalChromeWindow also
     // runs through this code. Use QueryInterface to get the correct objects.
     nsXPCOMCycleCollectionParticipant* participant;
     CallQueryInterface(this, &participant);
     NS_ASSERTION(participant,
                  "Failed to QI to nsXPCOMCycleCollectionParticipant!");
 
-    nsCOMPtr<nsISupports> thisSupports;
+    nsISupports* thisSupports;
     QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                   getter_AddRefs(thisSupports));
+                   reinterpret_cast<void**>(&thisSupports));
     NS_ASSERTION(thisSupports, "Failed to QI to nsCycleCollectionISupports!");
 
     nsresult rv = nsContentUtils::HoldJSObjects(thisSupports, participant);
     if (NS_FAILED(rv)) {
       NS_ERROR("nsContentUtils::HoldJSObjects failed!");
       return;
     }
   }
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -451,25 +451,25 @@ NoteJSRoot(JSTracer *trc, void *thing, u
     }
 }
 #endif
 
 nsresult 
 nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                   bool explainLiveExpectedGarbage)
 {
-    NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishCollection?");
+    NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?");
     mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER);
     if (!mCycleCollectionContext->IsValid()) {
         mCycleCollectionContext = nsnull;
-        return PR_FALSE;
+        return NS_ERROR_FAILURE;
     }
 
 #ifdef DEBUG_CC
-    NS_ASSERTION(!mJSRoots.ops, "Didn't call FinishCollection?");
+    NS_ASSERTION(!mJSRoots.ops, "Didn't call FinishCycleCollection?");
 
     if(explainLiveExpectedGarbage)
     {
         // Being called from nsCycleCollector::ExplainLiveExpectedGarbage.
 
         // Record all objects held by the JS runtime. This avoids doing a
         // complete GC if we're just tracing to explain (from
         // ExplainLiveExpectedGarbage), which makes the results of cycle
@@ -490,21 +490,26 @@ nsXPConnect::BeginCycleCollection(nsCycl
 #endif
 
     GetRuntime()->AddXPConnectRoots(mCycleCollectionContext->GetJSContext(), cb);
 
     return NS_OK;
 }
 
 nsresult 
-nsXPConnect::FinishCycleCollection()
+nsXPConnect::FinishTraverse()
 {
     if (mCycleCollectionContext)
         mCycleCollectionContext = nsnull;
+    return NS_OK;
+}
 
+nsresult 
+nsXPConnect::FinishCycleCollection()
+{
 #ifdef DEBUG_CC
     if(mJSRoots.ops)
     {
         PL_DHashTableFinish(&mJSRoots);
         mJSRoots.ops = nsnull;
     }
 #endif
 
--- a/js/src/xpconnect/src/xpccallcontext.cpp
+++ b/js/src/xpconnect/src/xpccallcontext.cpp
@@ -191,17 +191,17 @@ XPCCallContext::Init(XPCContext::LangTyp
         mWrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(mJSContext, obj,
                                                                 funobj,
                                                                 &mCurrentJSObject,
                                                                 &mTearOff);
     if(mWrapper)
     {
         DEBUG_CheckWrapperThreadSafety(mWrapper);
 
-        mFlattenedJSObject = mWrapper->GetFlatJSObject();
+        mFlattenedJSObject = mWrapper->GetFlatJSObjectAndMark();
 
         if(mTearOff)
         {
             mCurrentJSObject = mTearOff->GetJSObject();
             mScriptableInfo = nsnull;
         }
         else
         {
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -2820,22 +2820,23 @@ nsXPCComponents_Utils::LookupMethod()
         return NS_ERROR_XPC_BAD_CONVERT_JS;
 
     jsval funval;
     JSFunction *oldfunction;
 
     {
         JSAutoEnterCompartment ac;
 
-        if (!ac.enter(inner_cc, wrapper->GetFlatJSObject())) {
+        if (!ac.enter(inner_cc, wrapper->GetFlatJSObjectAndMark())) {
             return NS_ERROR_UNEXPECTED;
         }
 
         // get (and perhaps lazily create) the member's cloned function
-        if(!member->NewFunctionObject(inner_cc, iface, wrapper->GetFlatJSObject(),
+        if(!member->NewFunctionObject(inner_cc, iface,
+                                      wrapper->GetFlatJSObjectAndMark(),
                                       &funval))
             return NS_ERROR_XPC_BAD_CONVERT_JS;
 
         oldfunction = JS_ValueToFunction(inner_cc, funval);
         NS_ASSERTION(oldfunction, "Function is not a function");
     }
 
     // Stick the function in the return value. This roots it.
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -1324,17 +1324,17 @@ XPCConvert::NativeInterface2JSObject(XPC
         *pErr = rv;
 
     // If creating the wrapped native failed, then return early.
     if(NS_FAILED(rv) || !wrapper)
         return JS_FALSE;
 
     // If we're not creating security wrappers, we can return the
     // XPCWrappedNative as-is here.
-    flat = wrapper->GetFlatJSObject();
+    flat = wrapper->GetFlatJSObjectAndMark();
     jsval v = OBJECT_TO_JSVAL(flat);
     if(!XPCPerThreadData::IsMainThread(lccx.GetJSContext()) ||
        !allowNativeWrapper)
     {
         *d = v;
         if(dest)
             *dest = strongWrapper.forget().get();
         return JS_TRUE;
--- a/js/src/xpconnect/src/xpcinlines.h
+++ b/js/src/xpconnect/src/xpcinlines.h
@@ -55,29 +55,29 @@ xpc::PtrAndPrincipalHashKey::KeyEquals(c
 
   nsIScriptSecurityManager *ssm = nsXPConnect::gScriptSecurityManager;
   return !ssm || NS_SUCCEEDED(ssm->CheckSameOriginURI(mURI, aKey->mURI, PR_FALSE));
 }
 
 inline void
 XPCJSRuntime::AddVariantRoot(XPCTraceableVariant* variant)
 {
-    variant->AddToRootSet(GetJSRuntime(), &mVariantRoots);
+    variant->AddToRootSet(GetMapLock(), &mVariantRoots);
 }
 
 inline void
 XPCJSRuntime::AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS)
 {
-    wrappedJS->AddToRootSet(GetJSRuntime(), &mWrappedJSRoots);
+    wrappedJS->AddToRootSet(GetMapLock(), &mWrappedJSRoots);
 }
 
 inline void
 XPCJSRuntime::AddObjectHolderRoot(XPCJSObjectHolder* holder)
 {
-    holder->AddToRootSet(GetJSRuntime(), &mObjectHolderRoots);
+    holder->AddToRootSet(GetMapLock(), &mObjectHolderRoots);
 }
 
 /***************************************************************************/
 
 inline JSBool
 XPCCallContext::IsValid() const
 {
     return mState != INIT_FAILED;
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -340,20 +340,25 @@ void XPCJSRuntime::TraceJS(JSTracer* trc
                              XPCPerThreadData::IterateThreads(&iterp)))
             {
                 // Trace those AutoMarkingPtr lists!
                 thread->TraceJS(trc);
             }
         }
     }
 
-    // XPCJSObjectHolders don't participate in cycle collection, so always trace
-    // them here.
-    for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot())
-        static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+    {
+        XPCAutoLock lock(self->mMapLock);
+
+        // XPCJSObjectHolders don't participate in cycle collection, so always
+        // trace them here.
+        XPCRootSetElem *e;
+        for(e = self->mObjectHolderRoots; e; e = e->GetNextRoot())
+            static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+    }
 
     // Mark these roots as gray so the CC can walk them later.
     js::GCMarker *gcmarker = NULL;
     if (IS_GC_MARKING_TRACER(trc)) {
         gcmarker = static_cast<js::GCMarker *>(trc);
         JS_ASSERT(gcmarker->getMarkColor() == XPC_GC_COLOR_BLACK);
         gcmarker->setMarkColor(XPC_GC_COLOR_GRAY);
     }
@@ -394,16 +399,18 @@ void XPCJSRuntime::TraceXPConnectRoots(J
 {
     JSContext *iter = nsnull, *acx;
     while ((acx = JS_ContextIterator(GetJSRuntime(), &iter))) {
         JS_ASSERT(JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL));
         if (acx->globalObject)
             JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object");
     }
 
+    XPCAutoLock lock(mMapLock);
+
     XPCWrappedNativeScope::TraceJS(trc, this);
 
     for(XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
         static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
 
     for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
 
@@ -1432,37 +1439,39 @@ XPCJSRuntime::DebugDump(PRInt16 depth)
 
         XPC_LOG_OUTDENT();
 #endif
 }
 
 /***************************************************************************/
 
 void
-XPCRootSetElem::AddToRootSet(JSRuntime* rt, XPCRootSetElem** listHead)
+XPCRootSetElem::AddToRootSet(XPCLock *lock, XPCRootSetElem **listHead)
 {
     NS_ASSERTION(!mSelfp, "Must be not linked");
 
-    AutoLockJSGC lock(rt);
+    XPCAutoLock autoLock(lock);
+
     mSelfp = listHead;
     mNext = *listHead;
     if(mNext)
     {
         NS_ASSERTION(mNext->mSelfp == listHead, "Must be list start");
         mNext->mSelfp = &mNext;
     }
     *listHead = this;
 }
 
 void
-XPCRootSetElem::RemoveFromRootSet(JSRuntime* rt)
+XPCRootSetElem::RemoveFromRootSet(XPCLock *lock)
 {
     NS_ASSERTION(mSelfp, "Must be linked");
 
-    AutoLockJSGC lock(rt);
+    XPCAutoLock autoLock(lock);
+
     NS_ASSERTION(*mSelfp == this, "Link invariant");
     *mSelfp = mNext;
     if(mNext)
         mNext->mSelfp = mSelfp;
 #ifdef DEBUG
     mSelfp = nsnull;
     mNext = nsnull;
 #endif
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -561,16 +561,17 @@ public:
     NS_IMETHOD Unlink(void *p);
     NS_IMETHOD Unroot(void *p);
     NS_IMETHOD Traverse(void *p,
                         nsCycleCollectionTraversalCallback &cb);
     
     // nsCycleCollectionLanguageRuntime
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                           bool explainExpectedLiveGarbage);
+    virtual nsresult FinishTraverse();
     virtual nsresult FinishCycleCollection();
     virtual nsCycleCollectionParticipant *ToParticipant(void *p);
     virtual void Collect();
 #ifdef DEBUG_CC
     virtual void PrintAllReferencesTo(void *p);
 #endif
 
     unsigned GetOutstandingRequests(JSContext* cx);
@@ -649,18 +650,18 @@ public:
 
     ~XPCRootSetElem()
     {
         NS_ASSERTION(!mNext, "Must be unlinked");
         NS_ASSERTION(!mSelfp, "Must be unlinked");
     }
 
     inline XPCRootSetElem* GetNextRoot() { return mNext; }
-    void AddToRootSet(JSRuntime* rt, XPCRootSetElem** listHead);
-    void RemoveFromRootSet(JSRuntime* rt);
+    void AddToRootSet(XPCLock *lock, XPCRootSetElem **listHead);
+    void RemoveFromRootSet(XPCLock *lock);
 
 private:
     XPCRootSetElem *mNext;
     XPCRootSetElem **mSelfp;
 };
 
 /***************************************************************************/
 
@@ -2528,17 +2529,23 @@ public:
         {return GetProto() ? GetProto()->GetScope() :
          (XPCWrappedNativeScope*)
          (XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);}
 
     nsISupports*
     GetIdentityObject() const {return mIdentity;}
 
     JSObject*
-    GetFlatJSObject() const {return mFlatJSObject;}
+    GetFlatJSObjectAndMark() const
+        {if(mFlatJSObject && mFlatJSObject != INVALID_OBJECT)
+             mFlatJSObject->markIfUnmarked();
+         return mFlatJSObject;}
+
+    JSObject*
+    GetFlatJSObjectNoMark() const {return mFlatJSObject;}
 
     XPCLock*
     GetLock() const {return IsValid() && HasProto() ?
                                 GetProto()->GetLock() : nsnull;}
 
     XPCNativeSet*
     GetSet() const {XPCAutoLock al(GetLock()); return mSet;}
 
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -803,24 +803,26 @@ getNative(nsISupports *idobj,
     nsresult rv = idobj->QueryInterface(iid, ppThis);
     *pThisRef = static_cast<nsISupports*>(*ppThis);
     if(NS_SUCCEEDED(rv))
         *vp = OBJECT_TO_JSVAL(obj);
     return rv;
 }
 
 inline nsresult
-getNativeFromWrapper(XPCWrappedNative *wrapper,
+getNativeFromWrapper(JSContext *cx,
+                     XPCWrappedNative *wrapper,
                      const nsIID &iid,
                      void **ppThis,
                      nsISupports **pThisRef,
                      jsval *vp)
 {
     return getNative(wrapper->GetIdentityObject(), wrapper->GetOffsets(),
-                     wrapper->GetFlatJSObject(), iid, ppThis, pThisRef, vp);
+                     wrapper->GetFlatJSObjectAndMark(), iid, ppThis, pThisRef,
+                     vp);
 }
 
 
 nsresult
 getWrapper(JSContext *cx,
            JSObject *obj,
            JSObject *callee,
            XPCWrappedNative **wrapper,
@@ -851,17 +853,18 @@ castNative(JSContext *cx,
            const nsIID &iid,
            void **ppThis,
            nsISupports **pThisRef,
            jsval *vp,
            XPCLazyCallContext *lccx)
 {
     if(wrapper)
     {
-        nsresult rv = getNativeFromWrapper(wrapper, iid, ppThis, pThisRef, vp);
+        nsresult rv = getNativeFromWrapper(cx,wrapper, iid, ppThis, pThisRef,
+                                           vp);
 
         if(lccx && NS_SUCCEEDED(rv))
             lccx->SetWrapper(wrapper, tearoff);
 
         if(rv != NS_ERROR_NO_INTERFACE)
             return rv;
     }
     else if(cur)
--- a/js/src/xpconnect/src/xpcquickstubs.h
+++ b/js/src/xpconnect/src/xpcquickstubs.h
@@ -522,17 +522,17 @@ castNativeFromWrapper(JSContext *cx,
         if (NS_FAILED(*rv))
             return nsnull;
     }
 
     nsISupports *native;
     if(wrapper)
     {
         native = wrapper->GetIdentityObject();
-        cur = wrapper->GetFlatJSObject();
+        cur = wrapper->GetFlatJSObjectAndMark();
     }
     else
     {
         native = cur ?
                  static_cast<nsISupports*>(xpc_GetJSPrivate(cur)) :
                  nsnull;
     }
 
--- a/js/src/xpconnect/src/xpcvariant.cpp
+++ b/js/src/xpconnect/src/xpcvariant.cpp
@@ -84,17 +84,17 @@ XPCTraceableVariant::~XPCTraceableVarian
     NS_ASSERTION(JSVAL_IS_GCTHING(mJSVal), "Must be traceable or unlinked");
 
     // If mJSVal is JSVAL_STRING, we don't need to clean anything up;
     // simply removing the string from the root set is good.
     if(!JSVAL_IS_STRING(mJSVal))
         nsVariant::Cleanup(&mData);
 
     if (!JSVAL_IS_NULL(mJSVal))
-        RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetJSRuntime());
+        RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
     NS_ASSERTION(JSVAL_IS_TRACEABLE(mJSVal), "Must be traceable");
     JS_SET_TRACING_DETAILS(trc, PrintTraceName, this, 0);
     JS_CallTracer(trc, JSVAL_TO_TRACEABLE(mJSVal), JSVAL_TRACE_KIND(mJSVal));
 }
@@ -121,17 +121,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XP
     // so Cleanup() won't try to delete it
     if(JSVAL_IS_STRING(tmp->mJSVal))
         tmp->mData.u.wstr.mWStringValue = nsnull;
     nsVariant::Cleanup(&tmp->mData);
 
     if(JSVAL_IS_TRACEABLE(tmp->mJSVal))
     {
         XPCTraceableVariant *v = static_cast<XPCTraceableVariant*>(tmp);
-        v->RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetJSRuntime());
+        v->RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
     }
     tmp->mJSVal = JSVAL_NULL;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // static 
 XPCVariant* XPCVariant::newVariant(XPCCallContext& ccx, jsval aJSVal)
 {
     XPCVariant* variant;
--- a/js/src/xpconnect/src/xpcwrappedjs.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjs.cpp
@@ -106,17 +106,17 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsXP
                 if(map)
                 {
                     XPCAutoLock lock(rt->GetMapLock());
                     map->Remove(tmp);
                 }
             }
 
             if(tmp->mRefCnt > 1)
-                tmp->RemoveFromRootSet(rt->GetJSRuntime());
+                tmp->RemoveFromRootSet(rt->GetMapLock());
         }
 
         tmp->mJSObj = nsnull;
     }
 NS_IMPL_CYCLE_COLLECTION_ROOT_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
     tmp->Unlink();
@@ -155,17 +155,16 @@ nsXPCWrappedJS::QueryInterface(REFNSIID 
 
     if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
         *aInstancePtr = & NS_CYCLE_COLLECTION_NAME(nsXPCWrappedJS);
         return NS_OK;
     }
 
     if(aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)))
     {
-        NS_ADDREF(this);
         *aInstancePtr =
             NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
         return NS_OK;
     }
 
     if(!IsValid())
         return NS_ERROR_UNEXPECTED;
 
@@ -240,17 +239,17 @@ do_decrement:
     if(0 == cnt)
     {
         delete this;   // also unlinks us from chain
         return 0;
     }
     if(1 == cnt)
     {
         if(IsValid())
-            RemoveFromRootSet(rt->GetJSRuntime());
+            RemoveFromRootSet(rt->GetMapLock());
 
         // If we are not the root wrapper or if we are not being used from a
         // weak reference, then this extra ref is not needed and we can let
         // ourself be deleted.
         // Note: HasWeakReferences() could only return true for the root.
         if(!HasWeakReferences())
             goto do_decrement;
     }
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -608,19 +608,19 @@ FinishCreate(XPCCallContext& ccx,
              XPCNativeInterface* Interface,
              nsWrapperCache *cache,
              XPCWrappedNative* wrapper,
              XPCWrappedNative** resultWrapper)
 {
 #if DEBUG_xpc_leaks
     {
         char* s = wrapper->ToString(ccx);
-        NS_ASSERTION(wrapper->GetFlatJSObject(), "eh?");
+        NS_ASSERTION(wrapper->IsValid(), "eh?");
         printf("Created wrapped native %s, flat JSObject is %p\n",
-               s, (void*)wrapper->GetFlatJSObject());
+               s, (void*)wrapper->GetFlatJSObjectNoMark());
         if(s)
             JS_smprintf_free(s);
     }
 #endif
 
     XPCLock* mapLock = Scope->GetRuntime()->GetMapLock();
     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
 
@@ -649,32 +649,31 @@ FinishCreate(XPCCallContext& ccx,
 
     if(wrapperToKill)
     {
         // Second reference will be released by the FlatJSObject's finializer.
         wrapperToKill->Release();
     }
     else if(wrapper)
     {
-        JSObject *flat = wrapper->GetFlatJSObject();
+        JSObject *flat = wrapper->GetFlatJSObjectAndMark();
         NS_ASSERTION(!cache || !cache->GetWrapper() ||
                      flat == cache->GetWrapper(),
                      "This object has a cached wrapper that's different from "
                      "the JSObject held by its native wrapper?");
 
         if(cache && !cache->GetWrapper())
             cache->SetWrapper(flat);
 
         // Our newly created wrapper is the one that we just added to the table.
         // All is well. Call PostCreate as necessary.
         XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
         if(si && si->GetFlags().WantPostCreate())
         {
-            nsresult rv = si->GetCallback()->
-                     PostCreate(wrapper, ccx, wrapper->GetFlatJSObject());
+            nsresult rv = si->GetCallback()->PostCreate(wrapper, ccx, flat);
             if(NS_FAILED(rv))
             {
                 // PostCreate failed and that's Very Bad. We'll remove it from
                 // the map and mark it as invalid, but the PostCreate function
                 // may have handed the partially-constructed-and-now-invalid
                 // wrapper to someone before failing. Or, perhaps worse, the
                 // PostCreate call could have triggered code that reentered
                 // XPConnect and tried to wrap the same object. In that case
@@ -1508,17 +1507,17 @@ XPCWrappedNative::ReparentWrapperIfFound
     }
     else
     {
         rv = XPCWrappedNative::GetUsedOnly(ccx, aCOMObj, aOldScope, iface,
                                            getter_AddRefs(wrapper));
         if(NS_FAILED(rv))
             return rv;
 
-        flat = wrapper->GetFlatJSObject();
+        flat = wrapper->GetFlatJSObjectAndMark();
     }
 
     if(!flat)
     {
         *aWrapper = nsnull;
         return NS_OK;
     }
 
@@ -1962,17 +1961,17 @@ XPCWrappedNative::InitTearOff(XPCCallCon
         // The code in this block also does a check for the double wrapped
         // nsIPropertyBag case.
 
         nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(obj));
         if(wrappedJS)
         {
             JSObject* jso = nsnull;
             if(NS_SUCCEEDED(wrappedJS->GetJSObject(&jso)) &&
-               jso == GetFlatJSObject())
+               jso == mFlatJSObject)
             {
                 // The implementing JSObject is the same as ours! Just say OK
                 // without actually extending the set.
                 //
                 // XXX It is a little cheesy to have FindTearOff return an
                 // 'empty' tearoff. But this is the centralized place to do the
                 // QI activities on the underlying object. *And* most caller to
                 // FindTearOff only look for a non-null result and ignore the
@@ -3058,35 +3057,35 @@ CallMethodHelper::Invoke()
 }
 
 /***************************************************************************/
 // interface methods
 
 /* readonly attribute JSObjectPtr JSObject; */
 NS_IMETHODIMP XPCWrappedNative::GetJSObject(JSObject * *aJSObject)
 {
-    *aJSObject = mFlatJSObject;
+    *aJSObject = GetFlatJSObjectAndMark();
     return NS_OK;
 }
 
 /* readonly attribute nsISupports Native; */
 NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
 {
     // No need to QI here, we already have the correct nsISupports
     // vtable.
     *aNative = mIdentity;
     NS_ADDREF(*aNative);
     return NS_OK;
 }
 
-/* readonly attribute JSObjectPtr JSObjectPrototype; */
+/* reaonly attribute JSObjectPtr JSObjectPrototype; */
 NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
 {
     *aJSObjectPrototype = HasProto() ?
-                GetProto()->GetJSProtoObject() : GetFlatJSObject();
+                GetProto()->GetJSProtoObject() : GetFlatJSObjectAndMark();
     return NS_OK;
 }
 
 #ifndef XPCONNECT_STANDALONE
 nsIPrincipal*
 XPCWrappedNative::GetObjectPrincipal() const
 {
     nsIPrincipal* principal = GetScope()->GetPrincipal();
@@ -3156,21 +3155,21 @@ NS_IMETHODIMP XPCWrappedNative::RefreshP
 {
     XPCCallContext ccx(NATIVE_CALLER);
     if(!ccx.IsValid())
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     if(!HasProto())
         return NS_OK;
 
-    if(!GetFlatJSObject())
+    if(!mFlatJSObject)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     JSAutoEnterCompartment ac;
-    if(!ac.enter(ccx, GetFlatJSObject()))
+    if(!ac.enter(ccx, GetFlatJSObjectAndMark()))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     AutoMarkingWrappedNativeProtoPtr oldProto(ccx);
     AutoMarkingWrappedNativeProtoPtr newProto(ccx);
 
     oldProto = GetProto();
 
     XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
@@ -3184,17 +3183,18 @@ NS_IMETHODIMP XPCWrappedNative::RefreshP
     if(!newProto)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     // If nothing needs to change then we're done.
 
     if(newProto.get() == oldProto.get())
         return NS_OK;
 
-    if(!JS_SetPrototype(ccx, GetFlatJSObject(), newProto->GetJSProtoObject()))
+    if(!JS_SetPrototype(ccx, GetFlatJSObjectAndMark(),
+                        newProto->GetJSProtoObject()))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     SetProto(newProto);
 
     if(mScriptableInfo == oldProto->GetScriptableInfo())
         mScriptableInfo = newProto->GetScriptableInfo();
 
     return NS_OK;
@@ -3772,17 +3772,17 @@ XPCJSObjectHolder::GetJSObject(JSObject*
 XPCJSObjectHolder::XPCJSObjectHolder(XPCCallContext& ccx, JSObject* obj)
     : mJSObj(obj)
 {
     ccx.GetRuntime()->AddObjectHolderRoot(this);
 }
 
 XPCJSObjectHolder::~XPCJSObjectHolder()
 {
-    RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetJSRuntime());
+    RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
 }
 
 void
 XPCJSObjectHolder::TraceJS(JSTracer *trc)
 {
     JS_SET_TRACING_DETAILS(trc, PrintTraceName, this, 0);
     JS_CallTracer(trc, mJSObj, JSTRACE_OBJECT);
 }
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -364,17 +364,17 @@ XPCWrappedNativeScope::GetPrototypeNoHel
 static JSDHashOperator
 WrappedNativeJSGCThingTracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
                              uint32 number, void *arg)
 {
     XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
     if(wrapper->HasExternalReference() && !wrapper->IsWrapperExpired())
     {
         JSTracer* trc = (JSTracer *)arg;
-        JS_CALL_OBJECT_TRACER(trc, wrapper->GetFlatJSObject(),
+        JS_CALL_OBJECT_TRACER(trc, wrapper->GetFlatJSObjectNoMark(),
                               "XPCWrappedNative::mFlatJSObject");
     }
 
     return JS_DHASH_NEXT;
 }
 
 // static
 void
@@ -408,22 +408,22 @@ WrappedNativeSuspecter(JSDHashTable *tab
 {
     SuspectClosure* closure = static_cast<SuspectClosure*>(arg);
     XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
 
     if(wrapper->IsValid() &&
        wrapper->HasExternalReference() &&
        !wrapper->IsWrapperExpired())
     {
-        NS_ASSERTION(NS_IsMainThread(), 
-                     "Suspecting wrapped natives from non-main thread");
+        NS_ASSERTION(NS_IsMainThread() || NS_IsCycleCollectorThread(), 
+                     "Suspecting wrapped natives from non-CC thread");
 
         // Only suspect wrappedJSObjects that are in a compartment that
         // participates in cycle collection.
-        JSObject* obj = wrapper->GetFlatJSObject();
+        JSObject* obj = wrapper->GetFlatJSObjectAndMark();
         if(!xpc::ParticipatesInCycleCollection(closure->cx, obj))
             return JS_DHASH_NEXT;
 
         NS_ASSERTION(!JS_IsAboutToBeFinalized(closure->cx, obj),
                      "WrappedNativeSuspecter attempting to touch dead object");
 
         // Only record objects that might be part of a cycle as roots, unless
         // the callback wants all traces (a debug feature).
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -121,16 +121,17 @@
 #if !defined(__MINGW32__) && !defined(WINCE)
 #ifdef WIN32
 #include <crtdbg.h>
 #include <errno.h>
 #endif
 #endif
 
 #include "nsCycleCollectionParticipant.h"
+#include "nsCycleCollectorUtils.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "nsDeque.h"
 #include "nsCycleCollector.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "prprf.h"
@@ -142,24 +143,35 @@
 #include "nsIObserverService.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsTPtrArray.h"
 #include "nsTArray.h"
 #include "mozilla/Services.h"
 #include "nsICycleCollectorListener.h"
+#include "nsIXPConnect.h"
+#include "nsIJSRuntimeService.h"
 
 #include <stdio.h>
 #include <string.h>
 #ifdef WIN32
 #include <io.h>
 #include <process.h>
 #endif
 
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#include "mozilla/Mutex.h"
+#include "mozilla/CondVar.h"
+
+using namespace mozilla;
+
 //#define COLLECT_TIME_DEBUG
 
 #ifdef DEBUG_CC
 #define IF_DEBUG_CC_PARAM(_p) , _p
 #define IF_DEBUG_CC_ONLY_PARAM(_p) _p
 #else
 #define IF_DEBUG_CC_PARAM(_p)
 #define IF_DEBUG_CC_ONLY_PARAM(_p)
@@ -167,16 +179,26 @@
 
 #define DEFAULT_SHUTDOWN_COLLECTIONS 5
 #ifdef DEBUG_CC
 #define SHUTDOWN_COLLECTIONS(params) params.mShutdownCollections
 #else
 #define SHUTDOWN_COLLECTIONS(params) DEFAULT_SHUTDOWN_COLLECTIONS
 #endif
 
+#if defined(XP_WIN)
+// Defined in nsThreadManager.cpp.
+extern DWORD gTLSThreadIDIndex;
+#elif defined(NS_TLS)
+// Defined in nsThreadManager.cpp.
+extern NS_TLS mozilla::threads::ID gTLSThreadID;
+#else
+PRThread* gCycleCollectorThread = nsnull;
+#endif
+
 // Various parameters of this collector can be tuned using environment
 // variables.
 
 struct nsCycleCollectorParams
 {
     PRBool mDoNothing;
 #ifdef DEBUG_CC
     PRBool mReportStats;
@@ -929,16 +951,21 @@ struct nsCycleCollectionXPCOMRuntime :
     public nsCycleCollectionLanguageRuntime 
 {
     nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                   bool explainLiveExpectedGarbage)
     {
         return NS_OK;
     }
 
+    nsresult FinishTraverse() 
+    {
+        return NS_OK;
+    }
+
     nsresult FinishCycleCollection() 
     {
         return NS_OK;
     }
 
     inline nsCycleCollectionParticipant *ToParticipant(void *p);
 
 #ifdef DEBUG_CC
@@ -948,29 +975,32 @@ struct nsCycleCollectionXPCOMRuntime :
 
 struct nsCycleCollector
 {
     PRBool mCollectionInProgress;
     PRBool mScanInProgress;
     PRBool mFollowupCollection;
     PRUint32 mCollectedObjects;
     PRBool mFirstCollection;
+    PRTime mCollectionStart;
 
     nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
     nsCycleCollectionXPCOMRuntime mXPCOMRuntime;
 
     GCGraph mGraph;
 
     nsCycleCollectorParams mParams;
 
     nsTPtrArray<PtrInfo> *mWhiteNodes;
     PRUint32 mWhiteNodeCount;
 
     nsPurpleBuffer mPurpleBuf;
 
+    nsCOMPtr<nsICycleCollectorListener> mListener;
+
     void RegisterRuntime(PRUint32 langID, 
                          nsCycleCollectionLanguageRuntime *rt);
     nsCycleCollectionLanguageRuntime * GetRuntime(PRUint32 langID);
     void ForgetRuntime(PRUint32 langID);
 
     void SelectPurple(GCGraphBuilder &builder);
     void MarkRoots(GCGraphBuilder &builder);
     void ScanRoots();
@@ -984,30 +1014,38 @@ struct nsCycleCollector
     // old XPCOM binary components.
     PRBool Suspect(nsISupports *n);
     PRBool Forget(nsISupports *n);
     nsPurpleBufferEntry* Suspect2(nsISupports *n);
     PRBool Forget2(nsPurpleBufferEntry *e);
 
     PRUint32 Collect(PRUint32 aTryCollections,
                      nsICycleCollectorListener *aListener);
-    PRBool BeginCollection(nsICycleCollectorListener *aListener);
+
+    // Prepare for and cleanup after one or more collection(s).
+    PRBool PrepareForCollection(nsTPtrArray<PtrInfo> *aWhiteNodes);
+    void CleanupAfterCollection();
+
+    // Start and finish an individual collection.
+    PRBool BeginCollection(PRBool aForceGC,
+                           nsICycleCollectorListener *aListener);
     PRBool FinishCollection();
+
     PRUint32 SuspectedCount();
     void Shutdown();
 
     void ClearGraph()
     {
         mGraph.mNodes.Clear();
         mGraph.mEdges.Clear();
         mGraph.mRootCount = 0;
     }
 
 #ifdef DEBUG_CC
-    nsCycleCollectorStats mStats;    
+    nsCycleCollectorStats mStats;
 
     FILE *mPtrLog;
 
     void Allocated(void *n, size_t sz);
     void Freed(void *n);
 
     void ExplainLiveExpectedGarbage();
     PRBool CreateReversedEdges();
@@ -1150,29 +1188,29 @@ Fault(const char *msg, PtrInfo *pi)
     Fault(msg, pi->mPointer);
 }
 #endif
 
 static inline void
 AbortIfOffMainThreadIfCheckFast()
 {
 #if defined(XP_WIN) || defined(NS_TLS)
-    if (!NS_IsMainThread()) {
+    if (!NS_IsMainThread() && !NS_IsCycleCollectorThread()) {
         NS_RUNTIMEABORT("Main-thread-only object used off the main thread");
     }
 #endif
 }
 
 static nsISupports *
 canonicalize(nsISupports *in)
 {
-    nsCOMPtr<nsISupports> child;
+    nsISupports* child;
     in->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                       getter_AddRefs(child));
-    return child.get();
+                       reinterpret_cast<void**>(&child));
+    return child;
 }
 
 static inline void
 ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp)
 {
     // We use QI to move from an nsISupports to an
     // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
     // object that implements traversal and unlinking logic for the nsISupports
@@ -2418,148 +2456,146 @@ nsCycleCollector::Freed(void *n)
             if (!mPtrLog)
                 mPtrLog = fopen("pointer_log", "w");
             fprintf(mPtrLog, "R %p\n", n);
         }
     }
 }
 #endif
 
-PRUint32
-nsCycleCollector::Collect(PRUint32 aTryCollections,
-                          nsICycleCollectorListener *aListener)
+PRBool
+nsCycleCollector::PrepareForCollection(nsTPtrArray<PtrInfo> *aWhiteNodes)
 {
 #if defined(DEBUG_CC) && !defined(__MINGW32__)
     if (!mParams.mDoNothing && mParams.mHookMalloc)
         InitMemHook();
 #endif
 
     // This can legitimately happen in a few cases. See bug 383651.
     if (mCollectionInProgress)
-        return 0;
+        return PR_FALSE;
 
     NS_TIME_FUNCTION;
 
 #ifdef COLLECT_TIME_DEBUG
-    printf("cc: Starting nsCycleCollector::Collect(%d)\n", aTryCollections);
-    PRTime start = PR_Now();
-#endif
-
-#ifdef DEBUG_CC
-    if (!aListener && mParams.mDrawGraphs) {
-        aListener = new nsCycleCollectorLogger();
-    }
+    printf("cc: nsCycleCollector::PrepareForCollection()\n");
+    mCollectionStart = PR_Now();
 #endif
 
     mCollectionInProgress = PR_TRUE;
 
     nsCOMPtr<nsIObserverService> obs =
         mozilla::services::GetObserverService();
     if (obs)
         obs->NotifyObservers(nsnull, "cycle-collector-begin", nsnull);
 
     mFollowupCollection = PR_FALSE;
     mCollectedObjects = 0;
-    nsAutoTPtrArray<PtrInfo, 4000> whiteNodes;
-    mWhiteNodes = &whiteNodes;
-
-    PRUint32 totalCollections = 0;
-    while (aTryCollections > totalCollections) {
-        // The cycle collector uses the mark bitmap to discover what JS objects
-        // were reachable only from XPConnect roots that might participate in
-        // cycles. If this is the first cycle collection after startup force
-        // a garbage collection, otherwise the GC might not have run yet and
-        // the bitmap is invalid.
-        // Also force a JS GC if we are doing our infamous shutdown dance
-        // (aTryCollections > 1).
-        if ((mFirstCollection || aTryCollections > 1) &&
-            mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]) {
-#ifdef COLLECT_TIME_DEBUG
-            PRTime start = PR_Now();
-#endif
-            static_cast<nsCycleCollectionJSRuntime*>
-                (mRuntimes[nsIProgrammingLanguage::JAVASCRIPT])->Collect();
-            mFirstCollection = PR_FALSE;
-#ifdef COLLECT_TIME_DEBUG
-            printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC);
-#endif
-        }
-
-        PRBool collected = BeginCollection(aListener) && FinishCollection();
-
-#ifdef DEBUG_CC
-        // We wait until after FinishCollection to check the white nodes because
-        // some objects may outlive CollectWhite but then be freed by
-        // FinishCycleCollection (like XPConnect's deferred release of native
-        // objects).
-        PRUint32 i, count = mWhiteNodes->Length();
-        for (i = 0; i < count; ++i) {
-            PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
-            if (pinfo->mLangID == nsIProgrammingLanguage::CPLUSPLUS &&
-                mPurpleBuf.Exists(pinfo->mPointer)) {
-                printf("nsCycleCollector: %s object @%p is still alive after\n"
-                       "  calling RootAndUnlinkJSObjects, Unlink, and Unroot on"
-                       " it!  This probably\n"
-                       "  means the Unlink implementation was insufficient.\n",
-                       pinfo->mName, pinfo->mPointer);
-            }
-        }
-#endif
-        mWhiteNodes->Clear();
-        ClearGraph();
-
-        mParams.mDoNothing = PR_FALSE;
-
-        if (!collected)
-            break;
-        
-        ++totalCollections;
-    }
-
+
+    mWhiteNodes = aWhiteNodes;
+
+    return PR_TRUE;
+}
+
+void
+nsCycleCollector::CleanupAfterCollection()
+{
     mWhiteNodes = nsnull;
-
+    mListener = nsnull;
     mCollectionInProgress = PR_FALSE;
 
 #ifdef XP_OS2
     // Now that the cycle collector has freed some memory, we can try to
     // force the C library to give back as much memory to the system as
     // possible.
     _heapmin();
 #endif
 
 #ifdef COLLECT_TIME_DEBUG
-    printf("cc: Collect() took %lldms\n",
-           (PR_Now() - start) / PR_USEC_PER_MSEC);
+    printf("cc: CleanupAfterCollection(), total time %lldms\n",
+           (PR_Now() - mCollectionStart) / PR_USEC_PER_MSEC);
 #endif
 #ifdef DEBUG_CC
     ExplainLiveExpectedGarbage();
 #endif
+}
+
+PRUint32
+nsCycleCollector::Collect(PRUint32 aTryCollections,
+                          nsICycleCollectorListener *aListener)
+{
+    nsAutoTPtrArray<PtrInfo, 4000> whiteNodes;
+
+    if (!PrepareForCollection(&whiteNodes))
+        return 0;
+
+#ifdef DEBUG_CC
+    nsCOMPtr<nsICycleCollectorListener> tempListener;
+    if (!aListener && mParams.mDrawGraphs) {
+        tempListener = new nsCycleCollectorLogger();
+        aListener = tempListener;
+    }
+#endif
+
+    PRUint32 totalCollections = 0;
+    while (aTryCollections > totalCollections) {
+        // Synchronous cycle collection. Always force a JS GC as well.
+        if (!(BeginCollection(PR_TRUE, aListener) && FinishCollection()))
+            break;
+
+        ++totalCollections;
+    }
+
+    CleanupAfterCollection();
+
     return mCollectedObjects;
 }
 
 PRBool
-nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
+nsCycleCollector::BeginCollection(PRBool aForceGC,
+                                  nsICycleCollectorListener *aListener)
 {
     if (mParams.mDoNothing)
         return PR_FALSE;
 
+    // The cycle collector uses the mark bitmap to discover what JS objects
+    // were reachable only from XPConnect roots that might participate in
+    // cycles. If this is the first cycle collection after startup force
+    // a garbage collection, otherwise the GC might not have run yet and
+    // the bitmap is invalid.
+    if (mFirstCollection && mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]) {
+        aForceGC = PR_TRUE;
+        mFirstCollection = PR_FALSE;
+    }
+
+    if (aForceGC) {
+#ifdef COLLECT_TIME_DEBUG
+        PRTime start = PR_Now();
+#endif
+        static_cast<nsCycleCollectionJSRuntime*>
+            (mRuntimes[nsIProgrammingLanguage::JAVASCRIPT])->Collect();
+#ifdef COLLECT_TIME_DEBUG
+        printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC);
+#endif
+    }
+
     if (aListener && NS_FAILED(aListener->Begin())) {
         aListener = nsnull;
     }
 
     GCGraphBuilder builder(mGraph, mRuntimes, aListener);
     if (!builder.Initialized())
         return PR_FALSE;
 
 #ifdef COLLECT_TIME_DEBUG
     PRTime now = PR_Now();
 #endif
     for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
         if (mRuntimes[i])
-            mRuntimes[i]->BeginCycleCollection(builder, false);
+            mRuntimes[i]->BeginCycleCollection(builder, PR_FALSE);
     }
 
 #ifdef COLLECT_TIME_DEBUG
     printf("cc: mRuntimes[*]->BeginCycleCollection() took %lldms\n",
            (PR_Now() - now) / PR_USEC_PER_MSEC);
 
     now = PR_Now();
 #endif
@@ -2660,39 +2696,43 @@ nsCycleCollector::BeginCollection(nsICyc
                            "  suspect %p %s\n"
                            "  (which could be fixed by improving traversal)\n",
                            pi->mPointer, pi->mName);
                 }
             }
         }
 #endif
 
-#ifdef COLLECT_TIME_DEBUG
-        now = PR_Now();
-#endif
-        RootWhite();
-
-#ifdef COLLECT_TIME_DEBUG
-        printf("cc: RootWhite() took %lldms\n",
-               (PR_Now() - now) / PR_USEC_PER_MSEC);
-#endif
+        for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
+            if (mRuntimes[i])
+                mRuntimes[i]->FinishTraverse();
+        }
     }
     else {
         mScanInProgress = PR_FALSE;
     }
 
     return PR_TRUE;
 }
 
 PRBool
 nsCycleCollector::FinishCollection()
 {
 #ifdef COLLECT_TIME_DEBUG
     PRTime now = PR_Now();
 #endif
+
+    RootWhite();
+
+#ifdef COLLECT_TIME_DEBUG
+    printf("cc: RootWhite() took %lldms\n",
+           (PR_Now() - now) / PR_USEC_PER_MSEC);
+    now = PR_Now();
+#endif
+
     PRBool collected = CollectWhite();
 
 #ifdef COLLECT_TIME_DEBUG
     printf("cc: CollectWhite() took %lldms\n",
            (PR_Now() - now) / PR_USEC_PER_MSEC);
 #endif
 
 #ifdef DEBUG_CC
@@ -2703,16 +2743,40 @@ nsCycleCollector::FinishCollection()
 
     for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
         if (mRuntimes[i])
             mRuntimes[i]->FinishCycleCollection();
     }
 
     mFollowupCollection = PR_TRUE;
 
+#ifdef DEBUG_CC
+    // We wait until after FinishCollection to check the white nodes because
+    // some objects may outlive CollectWhite but then be freed by
+    // FinishCycleCollection (like XPConnect's deferred release of native
+    // objects).
+    PRUint32 i, count = mWhiteNodes->Length();
+    for (i = 0; i < count; ++i) {
+        PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
+        if (pinfo->mLangID == nsIProgrammingLanguage::CPLUSPLUS &&
+            mPurpleBuf.Exists(pinfo->mPointer)) {
+            printf("nsCycleCollector: %s object @%p is still alive after\n"
+                   "  calling RootAndUnlinkJSObjects, Unlink, and Unroot on"
+                   " it!  This probably\n"
+                   "  means the Unlink implementation was insufficient.\n",
+                   pinfo->mName, pinfo->mPointer);
+        }
+    }
+#endif
+
+    mWhiteNodes->Clear();
+    ClearGraph();
+
+    mParams.mDoNothing = PR_FALSE;
+
     return collected;
 }
 
 PRUint32
 nsCycleCollector::SuspectedCount()
 {
     return mPurpleBuf.Count();
 }
@@ -2810,17 +2874,17 @@ nsCycleCollector::ExplainLiveExpectedGar
         // garbage from the NoteRoot calls in such a way that something
         // that's in both is considered expected garbage.
         mExpectedGarbage.EnumerateEntries(&AddExpectedGarbage, &builder);
 
         PRUint32 expectedGarbageCount = builder.Count();
 
         for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
             if (mRuntimes[i])
-                mRuntimes[i]->BeginCycleCollection(builder, true);
+                mRuntimes[i]->BeginCycleCollection(builder, PR_TRUE);
         }
 
         // But just for extra information, add entries from the purple
         // buffer too, since it may give us extra information about
         // traversal deficiencies.
         mPurpleBuf.NoteAll(builder);
 
         MarkRoots(builder);
@@ -3187,48 +3251,22 @@ NS_CycleCollectorSuspect2(nsISupports *n
 }
 
 PRBool
 NS_CycleCollectorForget2(nsPurpleBufferEntry *e)
 {
     return sCollector ? sCollector->Forget2(e) : PR_TRUE;
 }
 
-
-PRUint32
-nsCycleCollector_collect(nsICycleCollectorListener *aListener)
-{
-    return sCollector ? sCollector->Collect(1, aListener) : 0;
-}
-
 PRUint32
 nsCycleCollector_suspectedCount()
 {
     return sCollector ? sCollector->SuspectedCount() : 0;
 }
 
-nsresult 
-nsCycleCollector_startup()
-{
-    NS_ASSERTION(!sCollector, "Forgot to call nsCycleCollector_shutdown?");
-
-    sCollector = new nsCycleCollector();
-    return sCollector ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
-}
-
-void 
-nsCycleCollector_shutdown()
-{
-    if (sCollector) {
-        sCollector->Shutdown();
-        delete sCollector;
-        sCollector = nsnull;
-    }
-}
-
 #ifdef DEBUG
 void
 nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n)
 {
 #ifdef DEBUG_CC
     if (sCollector)
         sCollector->ShouldBeFreed(n);
 #endif
@@ -3238,8 +3276,231 @@ void
 nsCycleCollector_DEBUG_wasFreed(nsISupports *n)
 {
 #ifdef DEBUG_CC
     if (sCollector)
         sCollector->WasFreed(n);
 #endif
 }
 #endif
+
+class nsCycleCollectorRunner : public nsRunnable
+{
+    nsCycleCollector *mCollector;
+    nsCOMPtr<nsICycleCollectorListener> mListener;
+    Mutex mLock;
+    CondVar mRequest;
+    CondVar mReply;
+    PRBool mRunning;
+    PRBool mCollected;
+    PRBool mJSGCHasRun;
+
+public:
+    NS_IMETHOD Run()
+    {
+#ifdef XP_WIN
+        TlsSetValue(gTLSThreadIDIndex,
+                    (void*) mozilla::threads::CycleCollector);
+#elif defined(NS_TLS)
+        gTLSThreadID = mozilla::threads::CycleCollector;
+#else
+        gCycleCollectorThread = PR_GetCurrentThread();
+#endif
+
+        NS_ASSERTION(NS_IsCycleCollectorThread() && !NS_IsMainThread(),
+                     "Wrong thread!");
+
+        MutexAutoLock autoLock(mLock);
+
+        mRunning = PR_TRUE;
+
+        while (1) {
+            mRequest.Wait();
+
+            if (!mRunning) {
+                mReply.Notify();
+                return NS_OK;
+            }
+
+            mCollected = mCollector->BeginCollection(PR_FALSE, mListener);
+
+            mReply.Notify();
+        }
+
+        return NS_OK;
+    }
+
+    nsCycleCollectorRunner(nsCycleCollector *collector)
+        : mCollector(collector),
+          mLock("cycle collector lock"),
+          mRequest(mLock, "cycle collector request condvar"),
+          mReply(mLock, "cycle collector reply condvar"),
+          mRunning(PR_FALSE),
+          mCollected(PR_FALSE),
+          mJSGCHasRun(PR_FALSE)
+    {
+        NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    }
+
+    PRUint32 Collect(nsICycleCollectorListener* aListener)
+    {
+        NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+        MutexAutoLock autoLock(mLock);
+
+        if (!mRunning || !mJSGCHasRun)
+            return 0;
+
+        nsAutoTPtrArray<PtrInfo, 4000> whiteNodes;
+        if (!mCollector->PrepareForCollection(&whiteNodes))
+            return 0;
+
+        NS_ASSERTION(!mListener, "Should have cleared this already!");
+        mListener = aListener;
+
+        mRequest.Notify();
+        mReply.Wait();
+
+        mListener = nsnull;
+
+        if (mCollected) {
+            mCollected = mCollector->FinishCollection();
+
+            mCollector->CleanupAfterCollection();
+
+            return mCollected ? mCollector->mCollectedObjects : 0;
+        }
+
+        return 0;
+    }
+
+    void Shutdown()
+    {
+        NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+        MutexAutoLock autoLock(mLock);
+
+        if (!mRunning)
+            return;
+
+        mRunning = PR_FALSE;
+        mRequest.Notify();
+        mReply.Wait();
+    }
+
+    void JSGCHasRun()
+    {
+        NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+        MutexAutoLock autoLock(mLock);
+        mJSGCHasRun = PR_TRUE;
+    }
+};
+
+// Holds a reference.
+static nsCycleCollectorRunner* sCollectorRunner;
+
+// Holds a reference.
+static nsIThread* sCollectorThread;
+
+static JSBool
+nsCycleCollector_gccallback(JSContext *cx, JSGCStatus status)
+{
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    if (status == JSGC_END) {
+        if (sCollectorRunner)
+            sCollectorRunner->JSGCHasRun();
+
+        nsCOMPtr<nsIJSRuntimeService> rts =
+          do_GetService(nsIXPConnect::GetCID());
+        NS_WARN_IF_FALSE(rts, "Failed to get XPConnect?!");
+        if (rts)
+            rts->UnregisterGCCallback(nsCycleCollector_gccallback);
+    }
+
+    return JS_TRUE;
+}
+
+class nsCycleCollectorGCHookRunnable : public nsRunnable
+{
+public:
+    NS_IMETHOD Run()
+    {
+        NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+        nsCOMPtr<nsIJSRuntimeService> rts =
+            do_GetService(nsIXPConnect::GetCID());
+        if (!rts) {
+            NS_RUNTIMEABORT("This must never fail!");
+        }
+
+        rts->RegisterGCCallback(nsCycleCollector_gccallback);
+        return NS_OK;
+    }
+};
+
+nsresult
+nsCycleCollector_startup()
+{
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    NS_ASSERTION(!sCollector, "Forgot to call nsCycleCollector_shutdown?");
+
+    sCollector = new nsCycleCollector();
+
+    // We can't get XPConnect yet as it hasn't been initialized yet.
+    nsRefPtr<nsCycleCollectorGCHookRunnable> hook =
+        new nsCycleCollectorGCHookRunnable();
+    nsresult rv = NS_DispatchToCurrentThread(hook);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsRefPtr<nsCycleCollectorRunner> runner =
+        new nsCycleCollectorRunner(sCollector);
+
+    nsCOMPtr<nsIThread> thread;
+    rv = NS_NewThread(getter_AddRefs(thread), runner);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    runner.swap(sCollectorRunner);
+    thread.swap(sCollectorThread);
+
+    return rv;
+}
+
+PRUint32
+nsCycleCollector_collect(nsICycleCollectorListener *aListener)
+{
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    if (sCollectorRunner)
+        return sCollectorRunner->Collect(aListener);
+    return sCollector ? sCollector->Collect(1, aListener) : 0;
+}
+
+void
+nsCycleCollector_shutdownThreads()
+{
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    if (sCollectorRunner) {
+        nsRefPtr<nsCycleCollectorRunner> runner;
+        runner.swap(sCollectorRunner);
+        runner->Shutdown();
+    }
+
+    if (sCollectorThread) {
+        nsCOMPtr<nsIThread> thread;
+        thread.swap(sCollectorThread);
+        thread->Shutdown();
+    }
+}
+
+void
+nsCycleCollector_shutdown()
+{
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    NS_ASSERTION(!sCollectorRunner, "Should have finished before!");
+    NS_ASSERTION(!sCollectorThread, "Should have finished before!");
+
+    if (sCollector) {
+        sCollector->Shutdown();
+        delete sCollector;
+        sCollector = nsnull;
+    }
+}
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -49,27 +49,29 @@ class nsCycleCollectionTraversalCallback
 
 // An nsCycleCollectionLanguageRuntime is a per-language object that
 // implements language-specific aspects of the cycle collection task.
 
 struct nsCycleCollectionLanguageRuntime
 {
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                           bool explainLiveExpectedGarbage) = 0;
+    virtual nsresult FinishTraverse() = 0;
     virtual nsresult FinishCycleCollection() = 0;
     virtual nsCycleCollectionParticipant *ToParticipant(void *p) = 0;
 #ifdef DEBUG_CC
     virtual void PrintAllReferencesTo(void *p) = 0;
 #endif
 };
 
 nsresult nsCycleCollector_startup();
 // Returns the number of collected nodes.
 NS_COM PRUint32 nsCycleCollector_collect(nsICycleCollectorListener *aListener);
 NS_COM PRUint32 nsCycleCollector_suspectedCount();
+void nsCycleCollector_shutdownThreads();
 void nsCycleCollector_shutdown();
 
 // The JS runtime is special, it needs to call cycle collection during its GC.
 // If the JS runtime is registered nsCycleCollector_collect will call
 // nsCycleCollectionJSRuntime::Collect which will call
 // nsCycleCollector_doCollect, else nsCycleCollector_collect will call
 // nsCycleCollector_doCollect directly.
 struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -636,16 +636,18 @@ ShutdownXPCOM(nsIServiceManager* servMgr
 #ifdef MOZ_ENABLE_LIBXUL
         mozilla::scache::StartupCache::DeleteSingleton();
 #endif
         if (observerService)
             (void) observerService->
                 NotifyObservers(nsnull, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
                                 nsnull);
 
+        nsCycleCollector_shutdownThreads();
+
         NS_ProcessPendingEvents(thread);
 
         // Shutdown the timer thread and all timers that might still be alive before
         // shutting down the component manager
         nsTimerImpl::Shutdown();
 
         NS_ProcessPendingEvents(thread);
 
--- a/xpcom/glue/Makefile.in
+++ b/xpcom/glue/Makefile.in
@@ -47,17 +47,16 @@ EXTRA_DEPS += $(srcdir)/objs.mk
 
 DIRS            = standalone nomozalloc
 
 MODULE		= xpcom
 LIBRARY_NAME	= xpcomglue_s
 SUPPRESS_FAKELIB = 1
 DIST_INSTALL	= 1
 
-
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../build \
 		$(NULL)
 
 CSRCS		= \
 		$(XPCOM_GLUE_SRC_LCSRCS) \
 		$(NULL)  
 
@@ -76,16 +75,17 @@ SDK_HEADERS = \
 		nsBaseHashtable.h \
 		nsCOMArray.h \
 		nsCOMPtr.h \
 		nsCRTGlue.h \
 		nsCategoryCache.h \
 		nsClassHashtable.h \
 		nsComponentManagerUtils.h \
 		nsCycleCollectionParticipant.h \
+		nsCycleCollectorUtils.h \
 		nsDataHashtable.h \
 		nsDebug.h \
 		nsDeque.h \
 		nsEnumeratorUtils.h \
 		nsHashKeys.h \
 		nsIClassInfoImpl.h \
 		nsID.h \
 		nsIInterfaceRequestorUtils.h \
@@ -109,38 +109,43 @@ SDK_HEADERS = \
 		nsTextFormatter.h \
 		nsTraceRefcnt.h \
 		nsVersionComparator.h \
 		nsVoidArray.h \
 		nsWeakReference.h \
 		pldhash.h \
 		$(NULL)
 
-EXPORTS_NAMESPACES = mozilla
+EXPORTS_NAMESPACES = mozilla mozilla/threads
 
 EXPORTS = \
 		nsThreadUtils.h \
 		nsTPriorityQueue.h \
 		nsProxyRelease.h \
 		nsXPTCUtils.h \
+		nsCycleCollectorUtils.h \
 		$(NULL)
 
 EXPORTS_mozilla = \
   AutoRestore.h \
   BlockingResourceBase.h \
   CondVar.h \
   DeadlockDetector.h \
   GenericFactory.h \
   Monitor.h \
   Mutex.h \
   SSE.h \
   unused.h \
   FileUtils.h \
   $(NULL)
 
+EXPORTS_mozilla/threads = \
+  nsThreadIDs.h \
+  $(NULL)
+
 SDK_LIBRARY     =                        \
 		$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
 		$(NULL)
 
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -91,13 +91,13 @@ NS_IMETHODIMP_(void)
 nsXPCOMCycleCollectionParticipant::Trace(void *p, TraceCallback cb,
                                          void *closure)
 {
 }
 
 PRBool
 nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports *s)
 {
-    nsCOMPtr<nsISupports> foo;
+    nsISupports* foo;
     s->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                      getter_AddRefs(foo));
+                      reinterpret_cast<void**>(&foo));
     return s == foo;
 }
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -196,19 +196,20 @@ public:
 
 #define NS_IMPL_QUERY_CYCLE_COLLECTION(_class)                                 \
   if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {          \
     *aInstancePtr = & NS_CYCLE_COLLECTION_NAME(_class);                        \
     return NS_OK;                                                              \
   } else
 
 #define NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)                       \
-  if ( aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) )                   \
-    foundInterface = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);      \
-  else
+  if ( aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ) {                 \
+    *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);       \
+    return NS_OK;                                                              \
+  } else
 
 #define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)                        \
   NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
 
 #define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)              \
   NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)
 
 #define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)                      \
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectorUtils.cpp
@@ -0,0 +1,94 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Cycle Collector.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsCycleCollectorUtils.h"
+
+#include "prthread.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "nsIThreadManager.h"
+
+#if defined(XP_WIN)
+#include "windows.h"
+#endif
+
+#ifndef MOZILLA_INTERNAL_API
+
+bool
+NS_IsCycleCollectorThread()
+{
+  PRBool result = PR_FALSE;
+  nsCOMPtr<nsIThreadManager> mgr =
+    do_GetService(NS_THREADMANAGER_CONTRACTID);
+  if (mgr)
+    mgr->GetIsCycleCollectorThread(&result);
+  return bool(result);
+}
+
+#elif defined(XP_WIN)
+
+// Defined in nsThreadManager.cpp.
+extern DWORD gTLSThreadIDIndex;
+
+bool
+NS_IsCycleCollectorThread()
+{
+  return TlsGetValue(gTLSThreadIDIndex) ==
+    (void*) mozilla::threads::CycleCollector;
+}
+
+#elif !defined(NS_TLS)
+
+// Defined in nsCycleCollector.cpp
+extern PRThread* gCycleCollectorThread;
+
+bool
+NS_IsCycleCollectorThread()
+{
+  return PR_GetCurrentThread() == gCycleCollectorThread;
+}
+
+#elif !defined(MOZ_ENABLE_LIBXUL)
+
+bool
+NS_IsCycleCollectorThread()
+{
+  return gTLSThreadID == mozilla::threads::CycleCollector;
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectorUtils.h
@@ -0,0 +1,65 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Cycle Collector.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsCycleCollectorUtils_h__
+#define nsCycleCollectorUtils_h__
+
+#include "nscore.h"
+#include "mozilla/threads/nsThreadIDs.h"
+
+#if defined(MOZILLA_INTERNAL_API)
+#define NS_IsCycleCollectorThread NS_IsCycleCollectorThread_P
+#if defined(XP_WIN)
+NS_COM bool NS_IsCycleCollectorThread();
+#elif defined(NS_TLS)
+// Defined in nsThreadManager.cpp.
+extern NS_TLS mozilla::threads::ID gTLSThreadID;
+#ifdef MOZ_ENABLE_LIBXUL
+inline bool NS_IsCycleCollectorThread()
+{
+  return gTLSThreadID == mozilla::threads::CycleCollector;
+}
+#else
+NS_COM bool NS_IsCycleCollectorThread();
+#endif
+#else
+NS_COM_GLUE bool NS_IsCycleCollectorThread();
+#endif
+#else
+NS_COM_GLUE bool NS_IsCycleCollectorThread();
+#endif
+
+#endif /* nsCycleCollectorUtils_h__ */
--- a/xpcom/glue/nsISupportsImpl.h
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -54,16 +54,17 @@
 #if !defined(XPCOM_GLUE_AVOID_NSPR)
 #include "prthread.h" /* needed for thread-safety checks */
 #include "nsAtomicRefcnt.h" /* for NS_Atomic{Increment,Decrement}Refcnt */
 #endif
 
 #include "nsDebug.h"
 #include "nsTraceRefcnt.h"
 #include "nsCycleCollector.h"
+#include "nsCycleCollectorUtils.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // Macros to help detect thread-safety:
 
 #if defined(NS_DEBUG) && !defined(XPCOM_GLUE_AVOID_NSPR)
 
 class nsAutoOwningThread {
 public:
@@ -72,21 +73,32 @@ public:
 
 private:
     void *mThread;
 };
 
 #define NS_DECL_OWNINGTHREAD            nsAutoOwningThread _mOwningThread;
 #define NS_ASSERT_OWNINGTHREAD(_class) \
   NS_CheckThreadSafe(_mOwningThread.GetThread(), #_class " not thread-safe")
+#define NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class) \
+  do { \
+    if (NS_IsCycleCollectorThread()) { \
+      NS_ERROR("Changing refcount of " #_class " object during Traverse is " \
+               "not permitted!"); \
+    } \
+    else { \
+      NS_ASSERT_OWNINGTHREAD(_class); \
+    } \
+  } while (0)
 
 #else // !NS_DEBUG
 
 #define NS_DECL_OWNINGTHREAD            /* nothing */
 #define NS_ASSERT_OWNINGTHREAD(_class)  ((void)0)
+#define NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class)  ((void)0)
 
 #endif // NS_DEBUG
 
 #define NS_CCAR_REFCNT_BIT 1
 #define NS_CCAR_REFCNT_TO_TAGGED(rc_) \
   NS_INT32_TO_PTR((rc_ << 1) | NS_CCAR_REFCNT_BIT)
 #define NS_CCAR_PURPLE_ENTRY_TO_TAGGED(pe_) \
   static_cast<void*>(pe_)
@@ -311,26 +323,27 @@ public:
  * the nsrefcnt return-value and the NS_ASSERT_OWNINGTHREAD() call.
  *
  * @param _class The name of the class implementing the method
  */
 #define NS_INLINE_DECL_REFCOUNTING(_class)                                    \
 public:                                                                       \
   void AddRef(void) {                                                         \
     NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");                 \
-    NS_ASSERT_OWNINGTHREAD(_class);                                           \
+    NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                          \
     ++mRefCnt;                                                                \
     NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this));                     \
   }                                                                           \
   void Release(void) {                                                        \
     NS_PRECONDITION(0 != mRefCnt, "dup release");                             \
-    NS_ASSERT_OWNINGTHREAD(_class);                                           \
+    NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                          \
     --mRefCnt;                                                                \
     NS_LOG_RELEASE(this, mRefCnt, #_class);                                   \
     if (mRefCnt == 0) {                                                       \
+      NS_ASSERT_OWNINGTHREAD(_class);                                         \
       mRefCnt = 1; /* stabilize */                                            \
       delete this;                                                            \
     }                                                                         \
   }                                                                           \
 protected:                                                                    \
   nsAutoRefCnt mRefCnt;                                                       \
   NS_DECL_OWNINGTHREAD                                                        \
 public:
@@ -338,17 +351,17 @@ public:
 /**
  * Use this macro to implement the AddRef method for a given <i>_class</i>
  * @param _class The name of the class implementing the method
  */
 #define NS_IMPL_ADDREF(_class)                                                \
 NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void)                                 \
 {                                                                             \
   NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");                   \
-  NS_ASSERT_OWNINGTHREAD(_class);                                             \
+  NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
   ++mRefCnt;                                                                  \
   NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this));                       \
   return mRefCnt;                                                             \
 }
 
 /**
  * Use this macro to implement the AddRef method for a given <i>_class</i>
  * implemented as a wholly owned aggregated object intended to implement
@@ -381,20 +394,21 @@ NS_IMETHODIMP_(nsrefcnt) _class::AddRef(
  * to be invoked when the object's refcount drops to zero. This
  * allows for arbitrary teardown activity to occur (e.g., deallocation
  * of object allocated with placement new).
  */
 #define NS_IMPL_RELEASE_WITH_DESTROY(_class, _destroy)                        \
 NS_IMETHODIMP_(nsrefcnt) _class::Release(void)                                \
 {                                                                             \
   NS_PRECONDITION(0 != mRefCnt, "dup release");                               \
-  NS_ASSERT_OWNINGTHREAD(_class);                                             \
+  NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
   --mRefCnt;                                                                  \
   NS_LOG_RELEASE(this, mRefCnt, #_class);                                     \
   if (mRefCnt == 0) {                                                         \
+    NS_ASSERT_OWNINGTHREAD(_class);                                           \
     mRefCnt = 1; /* stabilize */                                              \
     _destroy;                                                                 \
     return 0;                                                                 \
   }                                                                           \
   return mRefCnt;                                                             \
 }
 
 /**
@@ -427,35 +441,36 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release
   return (_aggregator)->Release();                                            \
 }
 
 
 #define NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(_class, _basetype)          \
 NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void)                                 \
 {                                                                             \
   NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");                   \
-  NS_ASSERT_OWNINGTHREAD(_class);                                             \
+  NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
   nsrefcnt count =                                                            \
     mRefCnt.incr(NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this));        \
   NS_LOG_ADDREF(this, count, #_class, sizeof(*this));                         \
   return count;                                                               \
 }
 
 #define NS_IMPL_CYCLE_COLLECTING_ADDREF(_class)      \
   NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(_class, _class)
 
 #define NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(_class, _basetype, _destroy)    \
 NS_IMETHODIMP_(nsrefcnt) _class::Release(void)                                \
 {                                                                             \
   NS_PRECONDITION(0 != mRefCnt, "dup release");                               \
-  NS_ASSERT_OWNINGTHREAD(_class);                                             \
+  NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
   nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
   nsrefcnt count = mRefCnt.decr(base);                                        \
   NS_LOG_RELEASE(this, count, #_class);                                       \
   if (count == 0) {                                                           \
+    NS_ASSERT_OWNINGTHREAD(_class);                                           \
     mRefCnt.stabilizeForDeletion(base);                                       \
     _destroy;                                                                 \
     return 0;                                                                 \
   }                                                                           \
   return count;                                                               \
 }
 
 #define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy)       \
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsThreadIDs.h
@@ -0,0 +1,52 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is XPCOM.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _mozilla_threads_nsThreadIDs_h_
+#define _mozilla_threads_nsThreadIDs_h_
+
+namespace mozilla {
+namespace threads {
+
+enum ID {
+  Generic = 0,
+  Main = 1,
+  CycleCollector = 2
+};
+
+} /* namespace threads */
+} /* namespace mozilla */
+
+#endif /* _mozilla_threads_nsThreadIDs_h_ */
--- a/xpcom/glue/nsThreadUtils.cpp
+++ b/xpcom/glue/nsThreadUtils.cpp
@@ -126,33 +126,33 @@ bool NS_IsMainThread()
   PRBool result = PR_FALSE;
   nsCOMPtr<nsIThreadManager> mgr =
     do_GetService(NS_THREADMANAGER_CONTRACTID);
   if (mgr)
     mgr->GetIsMainThread(&result);
   return bool(result);
 }
 #elif defined(XP_WIN)
-extern DWORD gTLSIsMainThreadIndex;
+extern DWORD gTLSThreadIDIndex;
 bool
 NS_IsMainThread()
 {
-  return !!TlsGetValue(gTLSIsMainThreadIndex);
+  return TlsGetValue(gTLSThreadIDIndex) == (void*) mozilla::threads::Main;
 }
 #elif !defined(NS_TLS)
 bool NS_IsMainThread()
 {
   PRBool result = PR_FALSE;
   nsThreadManager::get()->nsThreadManager::GetIsMainThread(&result);
   return bool(result);
 }
 #elif !defined(MOZ_ENABLE_LIBXUL)
 bool NS_IsMainThread()
 {
-  return gTLSIsMainThread;
+  return gTLSThreadID == mozilla::threads::Main;
 }
 #endif
 
 NS_METHOD
 NS_DispatchToCurrentThread(nsIRunnable *event)
 {
 #ifdef MOZILLA_INTERNAL_API
   nsIThread *thread = NS_GetCurrentThread();
--- a/xpcom/glue/nsThreadUtils.h
+++ b/xpcom/glue/nsThreadUtils.h
@@ -42,16 +42,17 @@
 #include "prthread.h"
 #include "prinrval.h"
 #include "nsIThreadManager.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "nsStringGlue.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
+#include "mozilla/threads/nsThreadIDs.h"
 
 // This is needed on some systems to prevent collisions between the symbols
 // appearing in xpcom_core and xpcomglue.  It may be unnecessary in the future
 // with better toolchain support.
 #ifdef MOZILLA_INTERNAL_API
 # define NS_NewThread NS_NewThread_P
 # define NS_GetCurrentThread NS_GetCurrentThread_P
 # define NS_GetMainThread NS_GetMainThread_P
@@ -95,28 +96,25 @@ NS_GetCurrentThread(nsIThread **result);
  *
  * @param result
  *   The resulting nsIThread object.
  */
 extern NS_COM_GLUE NS_METHOD
 NS_GetMainThread(nsIThread **result);
 
 #if defined(MOZILLA_INTERNAL_API) && defined(XP_WIN)
-
 NS_COM bool NS_IsMainThread();
-
 #elif defined(MOZILLA_INTERNAL_API) && defined(NS_TLS)
-// This is defined in nsThreadManager.cpp and initialized to `true` for the
+// This is defined in nsThreadManager.cpp and initialized to `Main` for the
 // main thread by nsThreadManager::Init.
-extern NS_TLS bool gTLSIsMainThread;
-
+extern NS_TLS mozilla::threads::ID gTLSThreadID;
 #ifdef MOZ_ENABLE_LIBXUL
 inline bool NS_IsMainThread()
 {
-  return gTLSIsMainThread;
+  return gTLSThreadID == mozilla::threads::Main;
 }
 #else
 NS_COM bool NS_IsMainThread();
 #endif
 #else
 /**
  * Test to see if the current thread is the main thread.
  *
--- a/xpcom/glue/objs.mk
+++ b/xpcom/glue/objs.mk
@@ -60,16 +60,17 @@ XPCOM_GLUE_SRC_LCPPSRCS =        \
   nsVersionComparator.cpp        \
   nsTHashtable.cpp               \
   nsQuickSort.cpp                \
   nsVoidArray.cpp                \
   nsTArray.cpp                   \
   nsThreadUtils.cpp              \
   nsTObserverArray.cpp           \
   nsCycleCollectionParticipant.cpp \
+  nsCycleCollectorUtils.cpp      \
   nsDeque.cpp \
   $(NULL)
 
 XPCOM_GLUE_SRC_CPPSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCPPSRCS))
 
 # TODO nsAutoLock.cpp should die soon
 
 XPCOM_GLUENS_SRC_LCPPSRCS =      \
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -38,22 +38,23 @@
 
 #include "nsThreadManager.h"
 #include "nsThread.h"
 #include "nsThreadUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsAutoLock.h"
+#include "nsCycleCollectorUtils.h"
 
 #ifdef XP_WIN
 #include <windows.h>
-DWORD gTLSIsMainThreadIndex = TlsAlloc();
+DWORD gTLSThreadIDIndex = TlsAlloc();
 #elif defined(NS_TLS)
-NS_TLS bool gTLSIsMainThread = false;
+NS_TLS mozilla::threads::ID gTLSThreadID = mozilla::threads::Generic;
 #endif
 
 typedef nsTArray< nsRefPtr<nsThread> > nsThreadArray;
 
 //-----------------------------------------------------------------------------
 
 static void
 ReleaseObject(void *data)
@@ -108,19 +109,19 @@ nsThreadManager::Init()
     return rv;
   }
 
   // We need to keep a pointer to the current thread, so we can satisfy
   // GetIsMainThread calls that occur post-Shutdown.
   mMainThread->GetPRThread(&mMainPRThread);
 
 #ifdef XP_WIN
-  TlsSetValue(gTLSIsMainThreadIndex, (void*) 1);
+  TlsSetValue(gTLSThreadIDIndex, (void*) mozilla::threads::Main);
 #elif defined(NS_TLS)
-  gTLSIsMainThread = true;
+  gTLSThreadID = mozilla::threads::Main;
 #endif
 
   mInitialized = PR_TRUE;
   return NS_OK;
 }
 
 void
 nsThreadManager::Shutdown()
@@ -304,12 +305,11 @@ nsThreadManager::GetIsMainThread(PRBool 
 
   *result = (PR_GetCurrentThread() == mMainPRThread);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThreadManager::GetIsCycleCollectorThread(PRBool *result)
 {
-  // Not yet implemented.
-  *result = PR_FALSE;
+  *result = PRBool(NS_IsCycleCollectorThread());
   return NS_OK;
 }