Bug 1382254 - Only disallow budget throttling. r=ehsan
authorAndreas Farre <farre@mozilla.com>
Thu, 20 Jul 2017 17:06:20 +0200
changeset 419990 7ca61520bf6db069041bede5bb902a01071782b2
parent 419989 15b71aceffb44d3ec2f3c1016cf36aac4a0bbd71
child 419991 ff7c09a77c2914fc37307168ec1a5c80d79bb814
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1382254
milestone56.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 1382254 - Only disallow budget throttling. r=ehsan Don't consider WebSockets, IndexedDB, etc when considering background tabs for ordinary throttling since that makes us throttle way less than we used to.
dom/base/TimeoutManager.cpp
dom/base/TimeoutManager.h
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TimeoutManager.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "mozilla/TimeStamp.h"
+#include "nsIDocShell.h"
 #include "nsITimeoutHandler.h"
 #include "mozilla/dom/TabGroup.h"
 #include "OrderedTimeoutIterator.h"
 #include "TimeoutExecutor.h"
 #include "TimeoutBudgetManager.h"
 #include "mozilla/net/WebSocketEventService.h"
 #include "mozilla/MediaManager.h"
 
@@ -102,72 +103,29 @@ TimeoutManager::IsBackground() const
 }
 
 bool
 TimeoutManager::IsActive() const
 {
   // A window is considered active if:
   // * It is a chrome window
   // * It is playing audio
-  // * If it is using user media
-  // * If it is using WebRTC
-  // * If it has open WebSockets
-  // * If it has active IndexedDB databases
   //
   // Note that a window can be considered active if it is either in the
   // foreground or in the background.
 
   if (mWindow.IsChromeWindow()) {
     return true;
   }
 
   // Check if we're playing audio
   if (mWindow.AsInner()->IsPlayingAudio()) {
     return true;
   }
 
-  // Check if there are any active IndexedDB databases
-  if (mWindow.AsInner()->HasActiveIndexedDBDatabases()) {
-    return true;
-  }
-
-  // Check if we have active GetUserMedia
-  if (MediaManager::Exists() &&
-      MediaManager::Get()->IsWindowStillActive(mWindow.WindowID())) {
-    return true;
-  }
-
-  bool active = false;
-#if 0
-  // Check if we have active PeerConnections This doesn't actually
-  // work, since we sometimes call IsActive from Resume, which in turn
-  // is sometimes called from nsGlobalWindow::LeaveModalState. The
-  // problem here is that LeaveModalState can be called with pending
-  // exeptions on the js context, and the following call to
-  // HasActivePeerConnection is a JS call, which will assert on that
-  // exception. Also, calling JS is expensive so we should try to fix
-  // this in some other way.
-  nsCOMPtr<IPeerConnectionManager> pcManager =
-    do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
-
-  if (pcManager && NS_SUCCEEDED(pcManager->HasActivePeerConnection(
-                     mWindow.WindowID(), &active)) &&
-      active) {
-    return true;
-  }
-#endif // MOZ_WEBRTC
-
-  // Check if we have web sockets
-  RefPtr<WebSocketEventService> eventService = WebSocketEventService::Get();
-  if (eventService &&
-      NS_SUCCEEDED(eventService->HasListenerFor(mWindow.WindowID(), &active)) &&
-      active) {
-    return true;
-  }
-
   return false;
 }
 
 
 uint32_t
 TimeoutManager::CreateFiringId()
 {
   uint32_t id = mNextFiringId;
@@ -241,17 +199,17 @@ TimeoutManager::MinSchedulingDelay() con
   // mExecutionBudget is -15ms
   // factor is 0.01, which is 1 ms/100ms
   // delay is 1000ms
   // then we will compute the minimum delay:
   // max(1000, - (- 15) * 1/0.01) = max(1000, 1500) = 1500
   TimeDuration unthrottled =
     isBackground ? TimeDuration::FromMilliseconds(gMinBackgroundTimeoutValue)
                  : TimeDuration();
-  if (mBudgetThrottleTimeouts && mExecutionBudget < TimeDuration()) {
+  if (BudgetThrottlingEnabled() && mExecutionBudget < TimeDuration()) {
     // Only throttle if execution budget is less than 0
     double factor = 1.0 / GetRegenerationFactor(mWindow.IsBackgroundInternal());
     return TimeDuration::Min(
       TimeDuration::FromMilliseconds(gBudgetThrottlingMaxDelay),
       TimeDuration::Max(unthrottled, -mExecutionBudget.MultDouble(factor)));
   }
   //
   return unthrottled;
@@ -368,17 +326,17 @@ TimeoutManager::UpdateBudget(const TimeS
 
   // The budget is adjusted by increasing it with the time since the
   // last budget update factored with the regeneration rate. If a
   // runnable has executed, subtract that duration from the
   // budget. The budget updated without consideration of wether the
   // window is active or not. If throttling is enabled and the window
   // is active and then becomes inactive, an overdrawn budget will
   // still be counted against the minimum delay.
-  if (mBudgetThrottleTimeouts) {
+  if (BudgetThrottlingEnabled()) {
     bool isBackground = mWindow.IsBackgroundInternal();
     double factor = GetRegenerationFactor(isBackground);
     TimeDuration regenerated = (aNow - mLastBudgetUpdate).MultDouble(factor);
     // Clamp the budget to the maximum allowed budget.
     mExecutionBudget = TimeDuration::Min(
       GetMaxBudget(isBackground), mExecutionBudget - aDuration + regenerated);
   }
   mLastBudgetUpdate = aNow;
@@ -1236,16 +1194,76 @@ ThrottleTimeoutsCallback::Notify(nsITime
 {
   mWindow->AsInner()->TimeoutManager().StartThrottlingTimeouts();
   mWindow = nullptr;
   return NS_OK;
 }
 
 }
 
+bool
+TimeoutManager::BudgetThrottlingEnabled() const
+{
+  // A window can be throttled using budget if
+  // * It isn't active
+  // * If it isn't using user media
+  // * If it isn't using WebRTC
+  // * If it hasn't got open WebSockets
+  // * If it hasn't got active IndexedDB databases
+  //
+  // Note that we allow both foreground and background to be
+  // considered for budget throttling. What determines if they are if
+  // budget throttling is enabled is the regeneration factor.
+
+  if (!mBudgetThrottleTimeouts || IsActive()) {
+    return false;
+  }
+
+  // Check if there are any active IndexedDB databases
+  if (mWindow.AsInner()->HasActiveIndexedDBDatabases()) {
+    return false;
+  }
+
+  // Check if we have active GetUserMedia
+  if (MediaManager::Exists() &&
+      MediaManager::Get()->IsWindowStillActive(mWindow.WindowID())) {
+    return false;
+  }
+
+  bool active = false;
+#if 0
+  // Check if we have active PeerConnections This doesn't actually
+  // work, since we sometimes call IsActive from Resume, which in turn
+  // is sometimes called from nsGlobalWindow::LeaveModalState. The
+  // problem here is that LeaveModalState can be called with pending
+  // exeptions on the js context, and the following call to
+  // HasActivePeerConnection is a JS call, which will assert on that
+  // exception. Also, calling JS is expensive so we should try to fix
+  // this in some other way.
+  nsCOMPtr<IPeerConnectionManager> pcManager =
+    do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
+
+  if (pcManager && NS_SUCCEEDED(pcManager->HasActivePeerConnection(
+                     mWindow.WindowID(), &active)) &&
+      active) {
+    return false;
+  }
+#endif // MOZ_WEBRTC
+
+  // Check if we have web sockets
+  RefPtr<WebSocketEventService> eventService = WebSocketEventService::Get();
+  if (eventService &&
+      NS_SUCCEEDED(eventService->HasListenerFor(mWindow.WindowID(), &active)) &&
+      active) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 TimeoutManager::StartThrottlingTimeouts()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(mThrottleTimeoutsTimer);
 
   MOZ_LOG(gLog, LogLevel::Debug,
           ("TimeoutManager %p started to throttle tracking timeouts\n", this));
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -143,16 +143,18 @@ private:
                          const TimeStamp& aNow = TimeStamp::Now());
 
   void RecordExecution(Timeout* aRunningTimeout,
                        Timeout* aTimeout);
 
   void UpdateBudget(const TimeStamp& aNow,
                     const TimeDuration& aDuration = TimeDuration());
 
+  bool BudgetThrottlingEnabled() const;
+
 private:
   struct Timeouts {
     explicit Timeouts(const TimeoutManager& aManager)
       : mManager(aManager)
     {
     }
 
     // Insert aTimeout into the list, before all timeouts that would