Bug 1155328. r=smaug
authorKyle Huey <khuey@kylehuey.com>
Wed, 06 Jan 2016 13:18:29 -0800
changeset 278868 d33b8f5dfd87dd0f5c1050d8772eb0ad6400efba
parent 278867 3ee42c6c0e252b0315dfc4d911b283ebe8698b39
child 278869 bea110e17c0bb189504274c24ef9de94316bd0c6
push id29860
push usercbook@mozilla.com
push dateThu, 07 Jan 2016 10:51:20 +0000
treeherdermozilla-central@e0bcd16e1d4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1155328
milestone46.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 1155328. r=smaug
dom/xul/nsXULElement.cpp
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -30,16 +30,17 @@
 #include "nsIDOMNodeList.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMElementCSSInlineStyle.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDocument.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "nsFocusManager.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsNameSpaceManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
@@ -2696,55 +2697,90 @@ nsXULPrototypeScript::DeserializeOutOfLi
             }
         }
     }
     return rv;
 }
 
 class NotifyOffThreadScriptCompletedRunnable : public nsRunnable
 {
-    RefPtr<nsIOffThreadScriptReceiver> mReceiver;
+    // An array of all outstanding script receivers. All reference counting of
+    // these objects happens on the main thread. When we return to the main
+    // thread from script compilation we make sure our receiver is still in
+    // this array (still alive) before proceeding. This array is cleared during
+    // shutdown, potentially before all outstanding script compilations have
+    // finished. We do not need to worry about pointer replay here, because
+    // a) we should not be starting script compilation after clearing this
+    // array and b) in all other cases the receiver will still be alive.
+    static StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> sReceivers;
+    static bool sSetupClearOnShutdown;
+
+    nsIOffThreadScriptReceiver* mReceiver;
     void *mToken;
 
 public:
-    NotifyOffThreadScriptCompletedRunnable(already_AddRefed<nsIOffThreadScriptReceiver> aReceiver,
+    NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver* aReceiver,
                                            void *aToken)
       : mReceiver(aReceiver), mToken(aToken)
     {}
 
+    static void NoteReceiver(nsIOffThreadScriptReceiver* aReceiver) {
+        if (!sSetupClearOnShutdown) {
+            ClearOnShutdown(&sReceivers);
+            sSetupClearOnShutdown = true;
+            sReceivers = new nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>();
+        }
+
+        // If we ever crash here, it's because we tried to lazy compile script
+        // too late in shutdown.
+        sReceivers->AppendElement(aReceiver);
+    }
+
     NS_DECL_NSIRUNNABLE
 };
 
+StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> NotifyOffThreadScriptCompletedRunnable::sReceivers;
+bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown = false;
+
 NS_IMETHODIMP
 NotifyOffThreadScriptCompletedRunnable::Run()
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Note: this unroots mScript so that it is available to be collected by the
     // JS GC. The receiver needs to root the script before performing a call that
     // could GC.
     JSScript *script;
     {
         AutoSafeJSContext cx;
         JSAutoCompartment ac(cx, xpc::CompilationScope());
         script = JS::FinishOffThreadScript(cx, JS_GetRuntime(cx), mToken);
     }
 
-    return mReceiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
+    if (!sReceivers) {
+        // We've already shut down.
+        return NS_OK;
+    }
+
+    auto index = sReceivers->IndexOf(mReceiver);
+    MOZ_RELEASE_ASSERT(index != sReceivers->NoIndex);
+    nsCOMPtr<nsIOffThreadScriptReceiver> receiver = (*sReceivers)[index].forget();
+    sReceivers->RemoveElementAt(index);
+
+    return receiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
 }
 
 static void
 OffThreadScriptReceiverCallback(void *aToken, void *aCallbackData)
 {
     // Be careful not to adjust the refcount on the receiver, as this callback
     // may be invoked off the main thread.
     nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData);
     RefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
-        new NotifyOffThreadScriptCompletedRunnable(
-            already_AddRefed<nsIOffThreadScriptReceiver>(aReceiver), aToken);
+        new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken);
     NS_DispatchToMainThread(notify);
 }
 
 nsresult
 nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
                               nsIURI* aURI, uint32_t aLineNo,
                               nsIDocument* aDocument,
                               nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
@@ -2773,18 +2809,17 @@ nsXULPrototypeScript::Compile(JS::Source
 
     if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
         if (!JS::CompileOffThread(cx, options,
                                   aSrcBuf.get(), aSrcBuf.length(),
                                   OffThreadScriptReceiverCallback,
                                   static_cast<void*>(aOffThreadReceiver))) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
-        // This reference will be consumed by the NotifyOffThreadScriptCompletedRunnable.
-        NS_ADDREF(aOffThreadReceiver);
+        NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
     } else {
         JS::Rooted<JSScript*> script(cx);
         if (!JS::Compile(cx, options, aSrcBuf, &script))
             return NS_ERROR_OUT_OF_MEMORY;
         Set(script);
     }
     return NS_OK;
 }