Bug 851340 - Make the rooting of scripts in nsXULPrototypeCache more efficient; r=smaug
authorTerrence Cole <terrence@mozilla.com>
Tue, 19 Mar 2013 10:20:21 -0700
changeset 125943 9b80bf15146cb8f06ecea1ea47eb4efd2b9b31f6
parent 125942 4b3ba25df1afbcd80b77a1465ce50c8acbb1a086
child 125944 6cb8dceb3ffdf8a98dea755f4356f787d10574e5
push id25181
push usertcole@mozilla.com
push dateFri, 22 Mar 2013 18:05:28 +0000
treeherdermozilla-inbound@9b80bf15146c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs851340
milestone22.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 851340 - Make the rooting of scripts in nsXULPrototypeCache more efficient; r=smaug
content/base/src/nsCCUncollectableMarker.cpp
content/base/src/nsCCUncollectableMarker.h
content/xul/document/src/nsXULDocument.h
content/xul/document/src/nsXULPrototypeCache.cpp
content/xul/document/src/nsXULPrototypeCache.h
js/xpconnect/src/XPCJSRuntime.cpp
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -429,18 +429,31 @@ TraceActiveWindowGlobal(const uint64_t& 
       xulDoc->TraceProtos(closure->mTrc, closure->mGCNumber);
     }
 #endif
   }
   return PL_DHASH_NEXT;
 }
 
 void
-mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber)
+mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC)
 {
+#ifdef MOZ_XUL
+  // Mark the scripts held in the XULPrototypeCache. This is required to keep
+  // the JS script in the cache live across GC.
+  nsXULPrototypeCache* cache = nsXULPrototypeCache::MaybeGetInstance();
+  if (cache) {
+    if (aIsShutdownGC) {
+      cache->FlushScripts();
+    } else {
+      cache->MarkInGC(aTrc);
+    }
+  }
+#endif
+
   if (!nsCCUncollectableMarker::sGeneration) {
     return;
   }
 
   TraceClosure closure(aTrc, aGCNumber);
 
   // Mark globals of active windows black.
   nsGlobalWindow::WindowByIdTable* windowsById =
--- a/content/base/src/nsCCUncollectableMarker.h
+++ b/content/base/src/nsCCUncollectableMarker.h
@@ -40,13 +40,13 @@ class nsCCUncollectableMarker MOZ_FINAL 
 
 private:
   nsCCUncollectableMarker() {}
 
 };
 
 namespace mozilla {
 namespace dom {
-void TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber);
+void TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC);
 }
 }
 
 #endif
--- a/content/xul/document/src/nsXULDocument.h
+++ b/content/xul/document/src/nsXULDocument.h
@@ -246,18 +246,16 @@ protected:
 
     static nsIAtom** kIdentityAttrs[];
 
     static nsIRDFService* gRDFService;
     static nsIRDFResource* kNC_persist;
     static nsIRDFResource* kNC_attribute;
     static nsIRDFResource* kNC_value;
 
-    static nsXULPrototypeCache* gXULCache;
-
     static PRLogModuleInfo* gXULLog;
 
     nsresult
     Persist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute);
 
     // IMPORTANT: The ownership implicit in the following member
     // variables has been explicitly checked and set using nsCOMPtr
     // for owning pointers and raw COM interface pointers for weak
--- a/content/xul/document/src/nsXULPrototypeCache.cpp
+++ b/content/xul/document/src/nsXULPrototypeCache.cpp
@@ -185,68 +185,38 @@ nsXULPrototypeCache::GetScript(nsIURI* a
 {
     CacheScriptEntry entry;
     if (!mScriptTable.Get(aURI, &entry)) {
         return nullptr;
     }
     return entry.mScriptObject;
 }
 
-
-/* static */
-static PLDHashOperator
-ReleaseScriptObjectCallback(nsIURI* aKey, CacheScriptEntry &aData, void* aClosure)
-{
-    nsCOMPtr<nsIScriptRuntime> rt;
-    if (NS_SUCCEEDED(NS_GetJSRuntime(getter_AddRefs(rt))))
-        rt->DropScriptObject(aData.mScriptObject);
-    return PL_DHASH_REMOVE;
-}
-
 nsresult
 nsXULPrototypeCache::PutScript(nsIURI* aURI, JSScript* aScriptObject)
 {
     CacheScriptEntry existingEntry;
     if (mScriptTable.Get(aURI, &existingEntry)) {
 #ifdef DEBUG
         nsAutoCString scriptName;
         aURI->GetSpec(scriptName);
         nsAutoCString message("Loaded script ");
         message += scriptName;
         message += " twice (bug 392650)";
         NS_WARNING(message.get());
 #endif
-        // Reuse the callback used for enumeration in FlushScripts
-        ReleaseScriptObjectCallback(aURI, existingEntry, nullptr);
     }
 
     CacheScriptEntry entry = {aScriptObject};
 
     mScriptTable.Put(aURI, entry);
 
-    // Lock the object from being gc'd until it is removed from the cache
-    nsCOMPtr<nsIScriptRuntime> rt;
-    nsresult rv = NS_GetJSRuntime(getter_AddRefs(rt));
-    if (NS_SUCCEEDED(rv))
-        rv = rt->HoldScriptObject(aScriptObject);
-    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to GC lock the object");
-
-    // On failure doing the lock, we should remove the map entry?
-    return rv;
+    return NS_OK;
 }
 
-void
-nsXULPrototypeCache::FlushScripts()
-{
-    // This callback will unlock each object so it can once again be gc'd.
-    // XXX - this might be slow - we fetch the runtime each and every object.
-    mScriptTable.Enumerate(ReleaseScriptObjectCallback, nullptr);
-}
-
-
 nsresult
 nsXULPrototypeCache::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
 {
     nsIURI* uri = aDocumentInfo->DocumentURI();
 
     nsRefPtr<nsXBLDocumentInfo> info;
     mXBLDocTable.Get(uri, getter_AddRefs(info));
     if (!info) {
@@ -302,25 +272,27 @@ nsXULPrototypeCache::FlushSkinFiles()
   mStyleSheetTable.Enumerate(FlushSkinSheets, nullptr);
 
   // Iterate over all the remaining XBL and make sure cached
   // scoped skin stylesheets are flushed and refetched by the
   // prototype bindings.
   mXBLDocTable.Enumerate(FlushScopedSkinStylesheets, nullptr);
 }
 
+void
+nsXULPrototypeCache::FlushScripts()
+{
+    mScriptTable.Clear();
+}
 
 void
 nsXULPrototypeCache::Flush()
 {
     mPrototypeTable.Clear();
-
-    // Clear the script cache, as it refers to prototype-owned mJSObjects.
-    FlushScripts();
-
+    mScriptTable.Clear();
     mStyleSheetTable.Clear();
     mXBLDocTable.Clear();
 }
 
 
 bool
 nsXULPrototypeCache::IsEnabled()
 {
@@ -661,8 +633,23 @@ MarkXULInCCGeneration(nsIURI* aKey, nsRe
 }
 
 void
 nsXULPrototypeCache::MarkInCCGeneration(uint32_t aGeneration)
 {
     mXBLDocTable.Enumerate(MarkXBLInCCGeneration, &aGeneration);
     mPrototypeTable.Enumerate(MarkXULInCCGeneration, &aGeneration);
 }
+
+static PLDHashOperator
+MarkScriptsInGC(nsIURI* aKey, CacheScriptEntry& aScriptEntry, void* aClosure)
+{
+    JSTracer* trc = static_cast<JSTracer*>(aClosure);
+    JS_CALL_SCRIPT_TRACER(trc, aScriptEntry.mScriptObject,
+                          "nsXULPrototypeCache script");
+    return PL_DHASH_NEXT;
+}
+
+void
+nsXULPrototypeCache::MarkInGC(JSTracer* aTrc)
+{
+    mScriptTable.Enumerate(MarkScriptsInGC, aTrc);
+}
--- a/content/xul/document/src/nsXULPrototypeCache.h
+++ b/content/xul/document/src/nsXULPrototypeCache.h
@@ -102,42 +102,44 @@ public:
      */
     nsresult GetInputStream(nsIURI* aURI, nsIObjectInputStream** objectInput);
     nsresult FinishInputStream(nsIURI* aURI);
     nsresult GetOutputStream(nsIURI* aURI, nsIObjectOutputStream** objectOutput);
     nsresult FinishOutputStream(nsIURI* aURI);
     nsresult HasData(nsIURI* aURI, bool* exists);
 
     static nsXULPrototypeCache* GetInstance();
+    static nsXULPrototypeCache* MaybeGetInstance() { return sInstance; }
 
     static void ReleaseGlobals()
     {
         NS_IF_RELEASE(sInstance);
     }
 
     void MarkInCCGeneration(uint32_t aGeneration);
+    void MarkInGC(JSTracer* aTrc);
+    void FlushScripts();
 protected:
     friend nsresult
     NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 
     nsXULPrototypeCache();
     virtual ~nsXULPrototypeCache();
 
     static nsXULPrototypeCache* sInstance;
 
-    void FlushScripts();
     void FlushSkinFiles();
 
     nsRefPtrHashtable<nsURIHashKey,nsXULPrototypeDocument>  mPrototypeTable; // owns the prototypes
     nsRefPtrHashtable<nsURIHashKey,nsCSSStyleSheet>        mStyleSheetTable;
     nsDataHashtable<nsURIHashKey,CacheScriptEntry>         mScriptTable;
     nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>  mXBLDocTable;
 
     nsTHashtable<nsURIHashKey> mCacheURITable;
 
     nsInterfaceHashtable<nsURIHashKey, nsIStorageStream> mOutputStreamTable;
     nsInterfaceHashtable<nsURIHashKey, nsIObjectInputStream> mInputStreamTable;
- 
+
     // Bootstrap caching service
     nsresult BeginCaching(nsIURI* aDocumentURI);
 };
 
 #endif // nsXULPrototypeCache_h__
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -372,17 +372,18 @@ void XPCJSRuntime::TraceBlackJS(JSTracer
 
         // 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);
     }
 
-    dom::TraceBlackJS(trc, JS_GetGCParameter(self->GetJSRuntime(), JSGC_NUMBER));
+    dom::TraceBlackJS(trc, JS_GetGCParameter(self->GetJSRuntime(), JSGC_NUMBER),
+                      self->GetXPConnect()->IsShuttingDown());
 }
 
 // static
 void XPCJSRuntime::TraceGrayJS(JSTracer* trc, void* data)
 {
     XPCJSRuntime* self = (XPCJSRuntime*)data;
 
     // Mark these roots as gray so the CC can walk them later.