Bug 822849 - Don't run CC/GC timers during shutdown, r=mccr8
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 22 Jan 2013 21:17:48 +0200
changeset 129345 2876e73c9b6fa152ab8da2542adb4db678c13113
parent 129344 653199a8edea98b0eb8c6ec7c58c19260e3884cb
child 129346 1bbce6c7e0069e732090dcb88adb771965ee8039
child 129406 c2ae65f1d5705b2bda3f8f2dec95b4fedb86979b
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs822849
milestone21.0a1
first release with
nightly linux32
2876e73c9b6f / 21.0a1 / 20130123030907 / files
nightly linux64
2876e73c9b6f / 21.0a1 / 20130123030907 / files
nightly mac
2876e73c9b6f / 21.0a1 / 20130123030907 / files
nightly win32
2876e73c9b6f / 21.0a1 / 20130123030907 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 822849 - Don't run CC/GC timers during shutdown, r=mccr8
dom/base/nsJSEnvironment.cpp
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -181,61 +181,75 @@ JSRuntime *nsJSRuntime::sRuntime;
 
 static const char kJSRuntimeServiceContractID[] =
   "@mozilla.org/js/xpc/RuntimeService;1";
 
 static PRTime sFirstCollectionTime;
 
 static bool sIsInitialized;
 static bool sDidShutdown;
-
+static bool sShuttingDown;
 static int32_t sContextCount;
 
 static PRTime sMaxScriptRunTime;
 static PRTime sMaxChromeScriptRunTime;
 
 static nsIScriptSecurityManager *sSecurityManager;
 
-// nsMemoryPressureObserver observes the memory-pressure notifications
+// nsJSEnvironmentObserver observes the memory-pressure notifications
 // and forces a garbage collection and cycle collection when it happens, if
 // the appropriate pref is set.
 
 static bool sGCOnMemoryPressure;
 
 static PRTime
 GetCollectionTimeDelta()
 {
   PRTime now = PR_Now();
   if (sFirstCollectionTime) {
     return now - sFirstCollectionTime;
   }
   sFirstCollectionTime = now;
   return 0;
 }
 
-class nsMemoryPressureObserver MOZ_FINAL : public nsIObserver
+static void
+KillTimers()
+{
+  nsJSContext::KillGCTimer();
+  nsJSContext::KillShrinkGCBuffersTimer();
+  nsJSContext::KillCCTimer();
+  nsJSContext::KillFullGCTimer();
+  nsJSContext::KillInterSliceGCTimer();
+}
+
+class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
 
-NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
+NS_IMPL_ISUPPORTS1(nsJSEnvironmentObserver, nsIObserver)
 
 NS_IMETHODIMP
-nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
-                                  const PRUnichar* aData)
+nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
+                                 const PRUnichar* aData)
 {
-  if (sGCOnMemoryPressure) {
+  if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) {
     nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE,
                                    nsJSContext::NonIncrementalGC,
                                    nsJSContext::NonCompartmentGC,
                                    nsJSContext::ShrinkingGC);
     nsJSContext::CycleCollectNow();
+  } else if (!nsCRT::strcmp(aTopic, "quit-application")) {
+    sShuttingDown = true;
+    KillTimers();
   }
+
   return NS_OK;
 }
 
 class nsRootedJSValueArray {
 public:
   explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {}
 
   bool SetCapacity(JSContext *cx, size_t capacity) {
@@ -3062,17 +3076,17 @@ nsJSContext::LoadEnd()
   sLoadingInProgress = false;
   PokeGC(js::gcreason::LOAD_END);
 }
 
 // static
 void
 nsJSContext::PokeGC(js::gcreason::Reason aReason, int aDelay)
 {
-  if (sGCTimer) {
+  if (sGCTimer || sShuttingDown) {
     // There's already a timer for GC'ing, just return
     return;
   }
 
   CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
 
   if (!sGCTimer) {
     // Failed to create timer (probably because we're in XPCOM shutdown)
@@ -3091,17 +3105,17 @@ nsJSContext::PokeGC(js::gcreason::Reason
 
   first = false;
 }
 
 // static
 void
 nsJSContext::PokeShrinkGCBuffers()
 {
-  if (sShrinkGCBuffersTimer) {
+  if (sShrinkGCBuffersTimer || sShuttingDown) {
     return;
   }
 
   CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
 
   if (!sShrinkGCBuffersTimer) {
     // Failed to create timer (probably because we're in XPCOM shutdown)
     return;
@@ -3111,17 +3125,17 @@ nsJSContext::PokeShrinkGCBuffers()
                                               NS_SHRINK_GC_BUFFERS_DELAY,
                                               nsITimer::TYPE_ONE_SHOT);
 }
 
 // static
 void
 nsJSContext::MaybePokeCC()
 {
-  if (sCCTimer || sDidShutdown) {
+  if (sCCTimer || sShuttingDown) {
     return;
   }
 
   if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
     sCCTimerFireCount = 0;
     CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
     if (!sCCTimer) {
       return;
@@ -3254,35 +3268,37 @@ DOMGCSliceCallback(JSRuntime *aRt, js::G
     nsJSContext::KillShrinkGCBuffersTimer();
   } else if (aProgress == js::GC_CYCLE_END) {
     sCCLockedOut = false;
   }
 
   // The GC has more work to do, so schedule another GC slice.
   if (aProgress == js::GC_SLICE_END) {
     nsJSContext::KillInterSliceGCTimer();
-    CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
-    sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired,
-                                             NULL,
-                                             NS_INTERSLICE_GC_DELAY,
-                                             nsITimer::TYPE_ONE_SHOT);
+    if (!sShuttingDown) {
+      CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
+      sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired,
+                                               NULL,
+                                               NS_INTERSLICE_GC_DELAY,
+                                               nsITimer::TYPE_ONE_SHOT);
+    }
   }
 
   if (aProgress == js::GC_CYCLE_END) {
     // May need to kill the inter-slice GC timer
     nsJSContext::KillInterSliceGCTimer();
 
     sCCollectedWaitingForGC = 0;
     sCleanupsSinceLastGC = 0;
     sNeedsFullCC = true;
     nsJSContext::MaybePokeCC();
 
     if (aDesc.isCompartment) {
       ++sCompartmentGCCount;
-      if (!sFullGCTimer) {
+      if (!sFullGCTimer && !sShuttingDown) {
         CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
         js::gcreason::Reason reason = js::gcreason::FULL_GC_TIMER;
         sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
                                            reinterpret_cast<void *>(reason),
                                            NS_FULL_GC_DELAY,
                                            nsITimer::TYPE_ONE_SHOT);
       }
     } else {
@@ -3398,16 +3414,17 @@ nsJSRuntime::Startup()
   sPostGCEventsToConsole = false;
   sDisableExplicitCompartmentGC = false;
   sNeedsFullCC = false;
   gNameSpaceManager = nullptr;
   sRuntimeService = nullptr;
   sRuntime = nullptr;
   sIsInitialized = false;
   sDidShutdown = false;
+  sShuttingDown = false;
   sContextCount = 0;
   sSecurityManager = nullptr;
 }
 
 static int
 MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure)
 {
   // Default limit on script run time to 10 seconds. 0 means let
@@ -3714,19 +3731,19 @@ nsJSRuntime::Init()
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs)
     return NS_ERROR_FAILURE;
 
   Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
                                "javascript.options.gc_on_memory_pressure",
                                true);
 
-  nsIObserver* memPressureObserver = new nsMemoryPressureObserver();
-  NS_ENSURE_TRUE(memPressureObserver, NS_ERROR_OUT_OF_MEMORY);
-  obs->AddObserver(memPressureObserver, "memory-pressure", false);
+  nsIObserver* observer = new nsJSEnvironmentObserver();
+  obs->AddObserver(observer, "memory-pressure", false);
+  obs->AddObserver(observer, "quit-application", false);
 
   sIsInitialized = true;
 
   return NS_OK;
 }
 
 //static
 nsScriptNameSpaceManager*
@@ -3745,32 +3762,29 @@ nsJSRuntime::GetNameSpaceManager()
 
   return gNameSpaceManager;
 }
 
 /* static */
 void
 nsJSRuntime::Shutdown()
 {
-  nsJSContext::KillGCTimer();
-  nsJSContext::KillShrinkGCBuffersTimer();
-  nsJSContext::KillCCTimer();
-  nsJSContext::KillFullGCTimer();
-  nsJSContext::KillInterSliceGCTimer();
+  KillTimers();
 
   NS_IF_RELEASE(gNameSpaceManager);
 
   if (!sContextCount) {
     // We're being shutdown, and there are no more contexts
     // alive, release the JS runtime service and the security manager.
 
     NS_IF_RELEASE(sRuntimeService);
     NS_IF_RELEASE(sSecurityManager);
   }
 
+  sShuttingDown = true;
   sDidShutdown = true;
 }
 
 // Script object mananagement - note duplicate implementation
 // in nsJSContext above...
 nsresult
 nsJSRuntime::HoldScriptObject(void* aScriptObject)
 {