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 id17032
push userrsayre@mozilla.com
push dateWed, 17 Nov 2010 21:55:39 +0000
treeherdermozilla-central@78a42f77bb90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs580096
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
 }