Backed out changeset 8272530c90ef (bug 1276626) for bug 1284511 r=blassey a=merge
authorTooru Fujisawa <arai_a@mac.com>
Fri, 09 Sep 2016 00:45:40 +0900
changeset 313244 176aff980979bf588baed78c2824571a6a7f2b96
parent 313243 5ff6c6e6718608582a064b6b42d8e077ff3bf606
child 313261 4cb8b7cc27e113f269ab8ecede30f832ffede2dc
child 313308 3fa2c49e0077823bf2ddc25549517e164f113b71
child 313354 e41ac717a38f8d6518be63e0831b5eb1657a223d
push id30676
push userkwierso@gmail.com
push dateThu, 08 Sep 2016 22:22:24 +0000
treeherdermozilla-central@176aff980979 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, merge
bugs1276626, 1284511
milestone51.0a1
backs out8272530c90ef5a0df5f5dd973c6fdd2a067e4b27
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
Backed out changeset 8272530c90ef (bug 1276626) for bug 1284511 r=blassey a=merge
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -970,17 +970,16 @@ class Watchdog
     explicit Watchdog(WatchdogManager* aManager)
       : mManager(aManager)
       , mLock(nullptr)
       , mWakeup(nullptr)
       , mThread(nullptr)
       , mHibernating(false)
       , mInitialized(false)
       , mShuttingDown(false)
-      , mSlowScriptSecondHalfCount(0)
       , mMinScriptRunTimeSeconds(1)
     {}
     ~Watchdog() { MOZ_ASSERT(!Initialized()); }
 
     WatchdogManager* Manager() { return mManager; }
     bool Initialized() { return mInitialized; }
     bool ShuttingDown() { return mShuttingDown; }
     PRLock* GetLock() { return mLock; }
@@ -1080,33 +1079,25 @@ class Watchdog
         PR_NotifyCondVar(mWakeup);
     }
 
     int32_t MinScriptRunTimeSeconds()
     {
         return mMinScriptRunTimeSeconds;
     }
 
-    uint32_t GetSlowScriptSecondHalfCount() { return mSlowScriptSecondHalfCount; }
-    void IncrementSlowScriptSecondHalfCount() { mSlowScriptSecondHalfCount++; }
-    void ResetSlowScriptSecondHalfCount() { mSlowScriptSecondHalfCount = 0; }
-
   private:
     WatchdogManager* mManager;
 
     PRLock* mLock;
     PRCondVar* mWakeup;
     PRThread* mThread;
     bool mHibernating;
     bool mInitialized;
     bool mShuttingDown;
-
-    // See the comment in WatchdogMain.
-    uint32_t mSlowScriptSecondHalfCount;
-
     mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
 };
 
 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
 
 class WatchdogManager : public nsIObserver
 {
@@ -1160,18 +1151,16 @@ class WatchdogManager : public nsIObserv
         // The watchdog reads this state, so acquire the lock before writing it.
         MOZ_ASSERT(NS_IsMainThread());
         Maybe<AutoLockWatchdog> lock;
         if (mWatchdog)
             lock.emplace(mWatchdog);
 
         // Write state.
         mTimestamps[TimestampRuntimeStateChange] = PR_Now();
-        if (mWatchdog)
-            mWatchdog->ResetSlowScriptSecondHalfCount();
         mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
 
         // The watchdog may be hibernating, waiting for the runtime to go
         // active. Wake it up if necessary.
         if (active && mWatchdog && mWatchdog->Hibernating())
             mWatchdog->WakeUp();
     }
     bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; }
@@ -1284,59 +1273,36 @@ WatchdogMain(void* arg)
 
         // Rise and shine.
         manager->RecordTimestamp(TimestampWatchdogWakeup);
 
         // Don't request an interrupt callback unless the current script has
         // been running long enough that we might show the slow script dialog.
         // Triggering the callback from off the main thread can be expensive.
 
-        // If we spend too much time running JS code in an event handler, then
-        // we want to show the slow script UI.  The timeout T is controlled by
-        // prefs.  We want to avoid showing the slow script dialog if the
-        // user's laptop goes to sleep in the middle of running a script.  To
-        // ensure this, we invoke the interrupt callback only after the T/2
-        // elapsed twice.  If the computer is put to sleep during one of the
-        // T/2 periods, the script still has the other T/2 seconds to finish.
-        //
-        //   + <-- TimestampRuntimeStateChange = PR_Now()
-        //   |     mSlowScriptSecondHalfCount = 0
-        //   |
-        //   | T/2
-        //   |
-        //   + <-- mSlowScriptSecondHalfCount = 1
-        //   |
-        //   | T/2
-        //   |
-        //   + <-- mSlowScriptSecondHalfCount = 2
-        //   |     Invoke interrupt callback
-        //   |
-        //   | T/2
-        //   |
-        //   + <-- mSlowScriptSecondHalfCount = 3
-        //   |
-        //   | T/2
-        //   |
-        //   + <-- mSlowScriptSecondHalfCount = 4
-        //         Invoke interrupt callback
-        //
-        PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC;
-        if (manager->IsRuntimeActive()) {
-            uint32_t count = self->GetSlowScriptSecondHalfCount() + 1;
-            if (manager->TimeSinceLastRuntimeStateChange() >= usecs * count / 2) {
-                self->IncrementSlowScriptSecondHalfCount();
-                if (count % 2 == 0) {
-                    bool debuggerAttached = false;
-                    nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
-                    if (dbg)
-                        dbg->GetIsDebuggerAttached(&debuggerAttached);
-                    if (!debuggerAttached)
-                        JS_RequestInterruptCallback(manager->Runtime()->Context());
-                }
-            }
+        // We want to avoid showing the slow script dialog if the user's laptop
+        // goes to sleep in the middle of running a script. To ensure this, we
+        // invoke the interrupt callback after only half the timeout has
+        // elapsed. The callback simply records the fact that it was called in
+        // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
+        // seconds and invoke the callback again. This time around it sees
+        // mSlowScriptSecondHalf is set and so it shows the slow script
+        // dialog. If the computer is put to sleep during one of the (timeout/2)
+        // periods, the script still has the other (timeout/2) seconds to
+        // finish.
+        PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
+        if (manager->IsRuntimeActive() &&
+            manager->TimeSinceLastRuntimeStateChange() >= usecs)
+        {
+            bool debuggerAttached = false;
+            nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
+            if (dbg)
+                dbg->GetIsDebuggerAttached(&debuggerAttached);
+            if (!debuggerAttached)
+                JS_RequestInterruptCallback(manager->Runtime()->Context());
         }
     }
 
     // Tell the manager that we've shut down.
     self->Finished();
 }
 
 PRTime
@@ -1369,16 +1335,17 @@ XPCJSRuntime::InterruptCallback(JSContex
 {
     XPCJSRuntime* self = XPCJSRuntime::Get();
 
     // Normally we record mSlowScriptCheckpoint when we start to process an
     // event. However, we can run JS outside of event handlers. This code takes
     // care of that case.
     if (self->mSlowScriptCheckpoint.IsNull()) {
         self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
+        self->mSlowScriptSecondHalf = false;
         self->mSlowScriptActualWait = mozilla::TimeDuration();
         self->mTimeoutAccumulated = false;
         return true;
     }
 
     // Sometimes we get called back during XPConnect initialization, before Gecko
     // has finished bootstrapping. Avoid crashing in nsContentUtils below.
     if (!nsContentUtils::IsInitialized())
@@ -1389,21 +1356,30 @@ XPCJSRuntime::InterruptCallback(JSContex
     // is.
     TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
     bool chrome = nsContentUtils::IsCallerChrome();
     const char* prefName = chrome ? PREF_MAX_SCRIPT_RUN_TIME_CHROME
                                   : PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
     int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
 
     // If there's no limit, or we're within the limit, let it go.
-    if (limit == 0 || duration.ToSeconds() < limit)
+    if (limit == 0 || duration.ToSeconds() < limit / 2.0)
         return true;
 
     self->mSlowScriptActualWait += duration;
 
+    // In order to guard against time changes or laptops going to sleep, we
+    // don't trigger the slow script warning until (limit/2) seconds have
+    // elapsed twice.
+    if (!self->mSlowScriptSecondHalf) {
+        self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
+        self->mSlowScriptSecondHalf = true;
+        return true;
+    }
+
     //
     // This has gone on long enough! Time to take action. ;-)
     //
 
     // Get the DOM window associated with the running script. If the script is
     // running in a non-DOM scope, we have to just let it keep running.
     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     RefPtr<nsGlobalWindow> win = WindowOrNull(global);
@@ -3340,16 +3316,17 @@ XPCJSRuntime::XPCJSRuntime()
    mGCIsRunning(false),
    mNativesToReleaseArray(),
    mDoingFinalization(false),
    mVariantRoots(nullptr),
    mWrappedJSRoots(nullptr),
    mObjectHolderRoots(nullptr),
    mWatchdogManager(new WatchdogManager(this)),
    mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
+   mSlowScriptSecondHalf(false),
    mTimeoutAccumulated(false),
    mPendingResult(NS_OK)
 {
 }
 
 #ifdef XP_WIN
 static size_t
 GetWindowsStackSize()
@@ -3673,31 +3650,33 @@ XPCJSRuntime::BeforeProcessTask(bool aMi
             // condition is triggered here by a Promise "then" callback.
 
             NS_DispatchToMainThread(new Runnable());
         }
     }
 
     // Start the slow script timer.
     mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
+    mSlowScriptSecondHalf = false;
     mSlowScriptActualWait = mozilla::TimeDuration();
     mTimeoutAccumulated = false;
 
     // As we may be entering a nested event loop, we need to
     // cancel any ongoing performance measurement.
     js::ResetPerformanceMonitoring(Get()->Context());
 
     CycleCollectedJSRuntime::BeforeProcessTask(aMightBlock);
 }
 
 void
 XPCJSRuntime::AfterProcessTask(uint32_t aNewRecursionDepth)
 {
     // Now that we're back to the event loop, reset the slow script checkpoint.
     mSlowScriptCheckpoint = mozilla::TimeStamp();
+    mSlowScriptSecondHalf = false;
 
     // Call cycle collector occasionally.
     MOZ_ASSERT(NS_IsMainThread());
     nsJSContext::MaybePokeCC();
 
     CycleCollectedJSRuntime::AfterProcessTask(aNewRecursionDepth);
 
     // Now that we are certain that the event is complete,
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -630,19 +630,33 @@ private:
     RefPtr<WatchdogManager> mWatchdogManager;
     JS::GCSliceCallback mPrevGCSliceCallback;
     JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback;
     JS::PersistentRootedObject mUnprivilegedJunkScope;
     JS::PersistentRootedObject mPrivilegedJunkScope;
     JS::PersistentRootedObject mCompilationScope;
     RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
 
-    // mSlowScriptCheckpoint is set to the time when we started processing the
-    // current event.  We use it to determine whether the interrupt callback
-    // needs to do anything.
+    // If we spend too much time running JS code in an event handler, then we
+    // want to show the slow script UI. The timeout T is controlled by prefs. We
+    // invoke the interrupt callback once after T/2 seconds and set
+    // mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
+    // interrupt callback again. Since mSlowScriptSecondHalf is now true, it
+    // shows the slow script UI. The reason we invoke the callback twice is to
+    // ensure that putting the computer to sleep while running a script doesn't
+    // cause the UI to be shown. If the laptop goes to sleep during one of the
+    // timeout periods, the script still has the other T/2 seconds to complete
+    // before the slow script UI is shown.
+    bool mSlowScriptSecondHalf;
+
+    // mSlowScriptCheckpoint is set to the time when:
+    // 1. We started processing the current event, or
+    // 2. mSlowScriptSecondHalf was set to true
+    // (whichever comes later). We use it to determine whether the interrupt
+    // callback needs to do anything.
     mozilla::TimeStamp mSlowScriptCheckpoint;
     // Accumulates total time we actually waited for telemetry
     mozilla::TimeDuration mSlowScriptActualWait;
     bool mTimeoutAccumulated;
 
     // mPendingResult is used to implement Components.returnCode.  Only really
     // meaningful while calling through XPCWrappedJS.
     nsresult mPendingResult;