Bug 845545: Part 3 - Give the CycleCollectedJSRuntime more control over
authorKyle Huey <khuey@kylehuey.com>
Sat, 03 Aug 2013 16:55:39 -0700
changeset 153596 d18e1e6db0dcc133fa155e991347d73ac22eb9df
parent 153595 b7dbdc2a38f9679122cc299ee84713fac521a341
child 153597 688333343bdd7891c53376816ef7ca231d12e65d
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs845545
milestone25.0a1
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 845545: Part 3 - Give the CycleCollectedJSRuntime more control over SnowWhite. r=smaug
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
xpcom/base/CycleCollectedJSRuntime.h
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -217,16 +217,53 @@ XPCJSRuntime::CustomContextCallback(JSCo
         if (!callbacks[i](cx, operation)) {
             return false;
         }
     }
 
     return true;
 }
 
+class AsyncFreeSnowWhite : public nsRunnable
+{
+public:
+  NS_IMETHOD Run()
+  {
+      TimeStamp start = TimeStamp::Now();
+      bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
+      Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
+                            uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
+      if (hadSnowWhiteObjects && !mContinuation) {
+          mContinuation = true;
+          if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
+              mActive = false;
+          }
+      } else {
+          mActive = false;
+      }
+      return NS_OK;
+  }
+
+  void Dispatch(bool aContinuation = false)
+  {
+      if (mContinuation) {
+          mContinuation = aContinuation;
+      }
+      if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) {
+          mActive = true;
+      }
+  }
+
+  AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {}
+
+public:
+  bool mContinuation;
+  bool mActive;
+};
+
 namespace xpc {
 
 CompartmentPrivate::~CompartmentPrivate()
 {
     MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
 }
 
 bool CompartmentPrivate::TryParseLocationURI()
@@ -562,16 +599,22 @@ XPCJSRuntime::PrepareForCollection()
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
     }
 }
 
 void
+XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation)
+{
+    mAsyncSnowWhiteFreer->Dispatch(aContinuation);
+}
+
+void
 xpc_UnmarkSkippableJSHolders()
 {
     if (nsXPConnect::XPConnect()->GetRuntime()) {
         nsXPConnect::XPConnect()->GetRuntime()->UnmarkSkippableJSHolders();
     }
 }
 
 template<class T> static void
@@ -2693,16 +2736,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mWrappedJSToReleaseArray(),
    mNativesToReleaseArray(),
    mDoingFinalization(false),
    mVariantRoots(nullptr),
    mWrappedJSRoots(nullptr),
    mObjectHolderRoots(nullptr),
    mWatchdogManager(new WatchdogManager(this)),
    mJunkScope(nullptr),
+   mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
    mExceptionManagerNotAvailable(false)
 {
 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
     DEBUG_WrappedNativeHashtable =
         PL_NewDHashTable(PL_DHashGetStubOps(), nullptr,
                          sizeof(PLDHashEntryStub), 128);
 #endif
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -602,16 +602,18 @@ enum WatchdogTimestampCategory
 {
     TimestampRuntimeStateChange = 0,
     TimestampWatchdogWakeup,
     TimestampWatchdogHibernateStart,
     TimestampWatchdogHibernateStop,
     TimestampCount
 };
 
+class AsyncFreeSnowWhite;
+
 class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime
 {
 public:
     static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect);
     static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); }
 
     // Make this public for now.  Ideally we'd hide the JSRuntime inside.
     JSRuntime* Runtime() const
@@ -724,16 +726,17 @@ public:
     }
 
     void TraceNativeBlackRoots(JSTracer* trc);
     void TraceAdditionalNativeGrayRoots(JSTracer* aTracer);
     void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb);
     void UnmarkSkippableJSHolders();
     void PrepareForForgetSkippable() MOZ_OVERRIDE;
     void PrepareForCollection() MOZ_OVERRIDE;
+    void DispatchDeferredDeletion(bool continuation) MOZ_OVERRIDE;
 
     void CustomGCCallback(JSGCStatus status) MOZ_OVERRIDE;
     bool CustomContextCallback(JSContext *cx, unsigned operation) MOZ_OVERRIDE;
     static void GCSliceCallback(JSRuntime *rt,
                                 JS::GCProgress progress,
                                 const JS::GCDescription &desc);
     static void FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, JSBool isCompartmentGC);
 
@@ -866,16 +869,17 @@ private:
     XPCRootSetElem *mVariantRoots;
     XPCRootSetElem *mWrappedJSRoots;
     XPCRootSetElem *mObjectHolderRoots;
     nsTArray<xpcGCCallback> extraGCCallbacks;
     nsTArray<xpcContextCallback> extraContextCallbacks;
     nsRefPtr<WatchdogManager> mWatchdogManager;
     JS::GCSliceCallback mPrevGCSliceCallback;
     JSObject* mJunkScope;
+    nsRefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
 
     nsCOMPtr<nsIException>   mPendingException;
     nsCOMPtr<nsIExceptionManager> mExceptionManager;
     bool mExceptionManagerNotAvailable;
 
 #define XPCCCX_STRING_CACHE_SIZE 2
 
     // String wrapper entry, holds a string, and a boolean that tells
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -199,16 +199,18 @@ public:
   virtual void PrepareForCollection() {}
 
   void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
                         DeferredFinalizeFunction aFunc,
                         void* aThing);
   void DeferredFinalize(nsISupports* aSupports);
 
   void DumpJSHeap(FILE* aFile);
+  
+  virtual void DispatchDeferredDeletion(bool aContinuation) = 0;
 
 private:
   JSGCThingParticipant mGCThingCycleCollectorGlobal;
 
   JSZoneParticipant mJSZoneCycleCollectorGlobal;
 
   JSRuntime* mJSRuntime;
 
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -996,18 +996,16 @@ class nsCycleCollector
     // Strong reference
     nsCycleCollectorRunner *mRunner;
     PRThread* mThread;
 
 public:
     nsCycleCollectorParams mParams;
 
 private:
-    nsRefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
-
     nsTArray<PtrInfo*> *mWhiteNodes;
     uint32_t mWhiteNodeCount;
 
     // mVisitedRefCounted and mVisitedGCed are only used for telemetry
     uint32_t mVisitedRefCounted;
     uint32_t mVisitedGCed;
 
     CC_BeforeUnlinkCallback mBeforeUnlinkCB;
@@ -1075,28 +1073,18 @@ public:
     void FixGrayBits(bool aForceGC);
     bool ShouldMergeZones(ccType aCCType);
     void CleanupAfterCollection();
 
     // Start and finish an individual collection.
     bool BeginCollection(ccType aCCType, nsICycleCollectorListener *aListener);
     bool FinishCollection(nsICycleCollectorListener *aListener);
 
-    AsyncFreeSnowWhite* AsyncSnowWhiteFreer()
-    {
-        return mAsyncSnowWhiteFreer;
-    }
-
     bool FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
 
-    // If there is a cycle collector available in the current thread,
-    // this calls FreeSnowWhite(false). Returns true if some
-    // snow-white objects were found.
-    static bool TryToFreeSnowWhite();
-
     uint32_t SuspectedCount();
     void Shutdown();
 
     void ClearGraph()
     {
         mGraph.mNodes.Clear();
         mGraph.mEdges.Clear();
         mGraph.mWeakMaps.Clear();
@@ -2178,54 +2166,16 @@ ChildFinder::NoteJSChild(void *child)
 static bool
 MayHaveChild(void *o, nsCycleCollectionParticipant* cp)
 {
     ChildFinder cf;
     cp->Traverse(o, cf);
     return cf.MayHaveChild();
 }
 
-class AsyncFreeSnowWhite : public nsRunnable
-{
-public:
-  NS_IMETHOD Run()
-  {
-      TimeStamp start = TimeStamp::Now();
-      bool hadSnowWhiteObjects = nsCycleCollector::TryToFreeSnowWhite();
-      Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
-                            uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
-      if (hadSnowWhiteObjects && !mContinuation) {
-          mContinuation = true;
-          if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
-              mActive = false;
-          }
-      } else {
-          mActive = false;
-      }
-      return NS_OK;
-  }
-
-  static void Dispatch(nsCycleCollector* aCollector, bool aContinuation = false)
-  {
-      AsyncFreeSnowWhite* swf = aCollector->AsyncSnowWhiteFreer();
-      if (swf->mContinuation) {
-          swf->mContinuation = aContinuation;
-      }
-      if (!swf->mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(swf))) {
-          swf->mActive = true;
-      }
-  }
-
-  AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {}
-
-public:
-  bool mContinuation;
-  bool mActive;
-};
-
 struct SnowWhiteObject
 {
   void* mPointer;
   nsCycleCollectionParticipant* mParticipant;
   nsCycleCollectingAutoRefCnt* mRefCnt;
 };
 
 class SnowWhiteKiller
@@ -2297,30 +2247,30 @@ public:
     {
         // Note, we must call the callback before SnowWhiteKiller calls
         // DeleteCycleCollectable!
         if (mCallback) {
             mCallback();
         }
         if (HasSnowWhiteObjects()) {
             // Effectively a continuation.
-            AsyncFreeSnowWhite::Dispatch(mCollector, true);
+            nsCycleCollector_dispatchDeferredDeletion(true);
         }
     }
 
     void
     Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry)
     {
         MOZ_ASSERT(aEntry->mObject, "null mObject in purple buffer");
         if (!aEntry->mRefCnt->get()) {
             if (!mAsyncSnowWhiteFreeing) {
                 SnowWhiteKiller::Visit(aBuffer, aEntry);
             } else if (!mDispatchedDeferredDeletion) {
                 mDispatchedDeferredDeletion = true;
-                nsCycleCollector_dispatchDeferredDeletion();
+                nsCycleCollector_dispatchDeferredDeletion(false);
             }
             return;
         }
         void *o = aEntry->mObject;
         nsCycleCollectionParticipant *cp = aEntry->mParticipant;
         CanonicalizeParticipant(&o, &cp);
         if (aEntry->mRefCnt->IsPurple() && !cp->CanSkip(o, false) &&
             (!mRemoveChildlessNodes || MayHaveChild(o, cp))) {
@@ -2359,25 +2309,16 @@ nsCycleCollector::FreeSnowWhite(bool aUn
                               visitor.HasSnowWhiteObjects();
         if (!visitor.HasSnowWhiteObjects()) {
             break;
         }
     } while (aUntilNoSWInPurpleBuffer);
     return hadSnowWhiteObjects;
 }
 
-/* static */ bool
-nsCycleCollector::TryToFreeSnowWhite()
-{
-  CollectorData* data = sCollectorData.get();
-  return data->mCollector ?
-      data->mCollector->FreeSnowWhite(false) :
-      false;
-}
-
 void
 nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
 {
     mPurpleBuf.SelectPointers(builder);
 }
 
 void
 nsCycleCollector::ForgetSkippable(bool aRemoveChildlessNodes,
@@ -2644,17 +2585,17 @@ nsCycleCollector::CollectWhite(nsICycleC
     for (uint32_t i = 0; i < count; ++i) {
         PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
         rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
         if (NS_FAILED(rv))
             Fault("Failed unroot call while unlinking", pinfo);
     }
     timeLog.Checkpoint("CollectWhite::Unroot");
 
-    nsCycleCollector_dispatchDeferredDeletion();
+    nsCycleCollector_dispatchDeferredDeletion(false);
 
     return count > 0;
 }
 
 
 ////////////////////////
 // Memory reporter
 ////////////////////////
@@ -2735,17 +2676,16 @@ NS_IMPL_ISUPPORTS1(CycleCollectorMultiRe
 
 nsCycleCollector::nsCycleCollector(CCThreadingModel aModel) :
     mCollectionInProgress(false),
     mScanInProgress(false),
     mResults(nullptr),
     mJSRuntime(nullptr),
     mRunner(nullptr),
     mThread(PR_GetCurrentThread()),
-    mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
     mWhiteNodes(nullptr),
     mWhiteNodeCount(0),
     mVisitedRefCounted(0),
     mVisitedGCed(0),
     mBeforeUnlinkCB(nullptr),
     mForgetSkippableCB(nullptr),
     mReporter(nullptr),
     mUnmergedNeeded(0),
@@ -3374,22 +3314,38 @@ nsCycleCollector_forgetSkippable(bool aR
     PROFILER_LABEL("CC", "nsCycleCollector_forgetSkippable");
     TimeLog timeLog;
     data->mCollector->ForgetSkippable(aRemoveChildlessNodes,
                                       aAsyncSnowWhiteFreeing);
     timeLog.Checkpoint("ForgetSkippable()");
 }
 
 void
-nsCycleCollector_dispatchDeferredDeletion()
+nsCycleCollector_dispatchDeferredDeletion(bool aContinuation)
 {
-    CollectorData* data = sCollectorData.get();
-    if (data && data->mCollector) {
-        AsyncFreeSnowWhite::Dispatch(data->mCollector);
+    CollectorData *data = sCollectorData.get();
+
+    if (!data || !data->mRuntime) {
+        return;
     }
+
+    data->mRuntime->DispatchDeferredDeletion(aContinuation);
+}
+
+bool
+nsCycleCollector_doDeferredDeletion()
+{
+    CollectorData *data = sCollectorData.get();
+
+    // We should have started the cycle collector by now.
+    MOZ_ASSERT(data);
+    MOZ_ASSERT(data->mCollector);
+    MOZ_ASSERT(data->mRuntime);
+
+    return data->mCollector->FreeSnowWhite(false);
 }
 
 void
 nsCycleCollector_collect(bool aManuallyTriggered,
                          nsCycleCollectorResults *aResults,
                          nsICycleCollectorListener *aListener)
 {
     CollectorData *data = sCollectorData.get();
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -52,17 +52,18 @@ typedef void (*CC_BeforeUnlinkCallback)(
 void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
 
 typedef void (*CC_ForgetSkippableCallback)(void);
 void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
 
 void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
                                       bool aAsyncSnowWhiteFreeing = false);
 
-void nsCycleCollector_dispatchDeferredDeletion();
+void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false);
+bool nsCycleCollector_doDeferredDeletion();
 
 void nsCycleCollector_collect(bool aManuallyTriggered,
                               nsCycleCollectorResults *aResults,
                               nsICycleCollectorListener *aListener);
 uint32_t nsCycleCollector_suspectedCount();
 void nsCycleCollector_shutdownThreads();
 void nsCycleCollector_shutdown();