Bug 916255 - Allow script loaders to have multiple in flight off thread parses (r=bz)
authorBrian Hacket <bhackett1024@gmail.com>
Thu, 05 Dec 2013 14:55:31 -0600
changeset 173770 ec5f9193d1786e68bb1b8ebb846caa93e0eec78a
parent 173769 2722412248da07351ad840e2e9deac96feac2c58
child 173771 a3f7a5b3bb3a099b521d5050a044f64e5e52e494
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs916255
milestone28.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 916255 - Allow script loaders to have multiple in flight off thread parses (r=bz)
content/base/src/nsScriptLoader.cpp
content/base/src/nsScriptLoader.h
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -712,111 +712,111 @@ nsScriptLoader::ProcessScriptElement(nsI
       "Not safe to run a parser-inserted script?");
   return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
 }
 
 namespace {
 
 class NotifyOffThreadScriptLoadCompletedRunnable : public nsRunnable
 {
-    nsRefPtr<nsScriptLoader> mLoader;
-    void *mToken;
+  nsRefPtr<nsScriptLoadRequest> mRequest;
+  nsRefPtr<nsScriptLoader> mLoader;
+  void *mToken;
 
 public:
-    NotifyOffThreadScriptLoadCompletedRunnable(already_AddRefed<nsScriptLoader> aLoader,
-                                               void *aToken)
-      : mLoader(aLoader), mToken(aToken)
-    {}
+  NotifyOffThreadScriptLoadCompletedRunnable(nsScriptLoadRequest* aRequest,
+                                             nsScriptLoader* aLoader)
+    : mRequest(aRequest), mLoader(aLoader), mToken(NULL)
+  {}
 
-    NS_DECL_NSIRUNNABLE
+  void SetToken(void* aToken) {
+    MOZ_ASSERT(aToken && !mToken);
+    mToken = aToken;
+  }
+
+  NS_DECL_NSIRUNNABLE
 };
 
 } /* anonymous namespace */
 
 nsresult
-nsScriptLoader::ProcessOffThreadRequest(void **aOffThreadToken)
+nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest, void **aOffThreadToken)
 {
-    nsCOMPtr<nsScriptLoadRequest> request = mOffThreadScriptRequest;
-    mOffThreadScriptRequest = nullptr;
-    nsresult rv = ProcessRequest(request, aOffThreadToken);
-    mDocument->UnblockOnload(false);
-    return rv;
+  nsresult rv = ProcessRequest(aRequest, aOffThreadToken);
+  mDocument->UnblockOnload(false);
+  return rv;
 }
 
 NS_IMETHODIMP
 NotifyOffThreadScriptLoadCompletedRunnable::Run()
 {
-    MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_IsMainThread());
 
-    nsresult rv = mLoader->ProcessOffThreadRequest(&mToken);
+  nsresult rv = mLoader->ProcessOffThreadRequest(mRequest, &mToken);
 
-    if (mToken) {
-      // The result of the off thread parse was not actually needed to process
-      // the request (disappearing window, some other error, ...). Finish the
-      // request to avoid leaks in the JS engine.
-      nsCOMPtr<nsIJSRuntimeService> svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-      NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
-      JSRuntime *rt;
-      svc->GetRuntime(&rt);
-      NS_ENSURE_TRUE(rt, NS_ERROR_FAILURE);
-      JS::FinishOffThreadScript(nullptr, rt, mToken);
-    }
+  if (mToken) {
+    // The result of the off thread parse was not actually needed to process
+    // the request (disappearing window, some other error, ...). Finish the
+    // request to avoid leaks in the JS engine.
+    nsCOMPtr<nsIJSRuntimeService> svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
+    NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
+    JSRuntime *rt;
+    svc->GetRuntime(&rt);
+    NS_ENSURE_TRUE(rt, NS_ERROR_FAILURE);
+    JS::FinishOffThreadScript(nullptr, rt, mToken);
+  }
 
-    return rv;
+  return rv;
 }
 
 static void
 OffThreadScriptLoaderCallback(void *aToken, void *aCallbackData)
 {
-    // Be careful not to adjust the refcount on the loader, as this callback
-    // may be invoked off the main thread.
-    nsScriptLoader* aLoader = static_cast<nsScriptLoader*>(aCallbackData);
-    nsRefPtr<NotifyOffThreadScriptLoadCompletedRunnable> notify =
-        new NotifyOffThreadScriptLoadCompletedRunnable(
-            already_AddRefed<nsScriptLoader>(aLoader), aToken);
-    NS_DispatchToMainThread(notify);
+  NotifyOffThreadScriptLoadCompletedRunnable* aRunnable =
+    static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData);
+  aRunnable->SetToken(aToken);
+  NS_DispatchToMainThread(aRunnable);
+  NS_RELEASE(aRunnable);
 }
 
 nsresult
 nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
 {
   if (!aRequest->mElement->GetScriptAsync() || aRequest->mIsInline) {
     return NS_ERROR_FAILURE;
   }
 
-  if (mOffThreadScriptRequest) {
-    return NS_ERROR_FAILURE;
-  }
-
   JSObject *unrootedGlobal;
   nsCOMPtr<nsIScriptContext> context = GetScriptContext(&unrootedGlobal);
   if (!context) {
     return NS_ERROR_FAILURE;
   }
   AutoPushJSContext cx(context->GetNativeContext());
   JS::Rooted<JSObject*> global(cx, unrootedGlobal);
 
   JS::CompileOptions options(cx);
   FillCompileOptionsForRequest(aRequest, global, &options);
 
   if (!JS::CanCompileOffThread(cx, options)) {
     return NS_ERROR_FAILURE;
   }
 
-  mOffThreadScriptRequest = aRequest;
+  NotifyOffThreadScriptLoadCompletedRunnable* runnable =
+    new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
+
+  // This reference will be consumed by OffThreadScriptLoaderCallback.
+  NS_ADDREF(runnable);
+
   if (!JS::CompileOffThread(cx, global, options,
                             aRequest->mScriptText.get(), aRequest->mScriptText.Length(),
                             OffThreadScriptLoaderCallback,
-                            static_cast<void*>(this))) {
+                            static_cast<void*>(runnable))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  // This reference will be consumed by the NotifyOffThreadScriptLoadCompletedRunnable.
-  NS_ADDREF(this);
-
   mDocument->BlockOnload();
 
   return NS_OK;
 }
 
 nsresult
 nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest, void **aOffThreadToken)
 {
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -206,17 +206,18 @@ public:
                           const nsAString &aType,
                           const nsAString &aCrossOrigin,
                           bool aScriptFromHead);
 
   /**
    * Process a request that was deferred so that the script could be compiled
    * off thread.
    */
-  nsresult ProcessOffThreadRequest(void **aOffThreadToken);
+  nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest,
+                                   void **aOffThreadToken);
 
 private:
   /**
    * Unblocks the creator parser of the parser-blocking scripts.
    */
   void UnblockParser(nsScriptLoadRequest* aParserBlockingRequest);
 
   /**
@@ -311,17 +312,16 @@ private:
       return aRequest == aPi.mRequest;
     }
   };
   struct PreloadURIComparator {
     bool Equals(const PreloadInfo &aPi, nsIURI * const &aURI) const;
   };
   nsTArray<PreloadInfo> mPreloads;
 
-  nsCOMPtr<nsScriptLoadRequest> mOffThreadScriptRequest;
   nsCOMPtr<nsIScriptElement> mCurrentScript;
   nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript;
   // XXXbz do we want to cycle-collect these or something?  Not sure.
   nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
   uint32_t mBlockerCount;
   bool mEnabled;
   bool mDeferEnabled;
   bool mDocumentParsingDone;