Bug 1178890 - Update timer arrays after sleep to account for time sleeping. r=bwc, r=froydnj, a=sledru
authorRandell Jesup <rjesup@jesup.org>
Thu, 09 Jul 2015 20:18:34 -0400
changeset 281489 47ef905be01bf21a6b1a1945cc9f4ab45f240c91
parent 281488 73b08d7b5a60dbf4e17492d4f66bd1cb2e85e53f
child 281490 1ef901bf9cd674dbbd54b76b689bd125dcf6cd5f
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwc, froydnj, sledru
bugs1178890
milestone41.0a2
Bug 1178890 - Update timer arrays after sleep to account for time sleeping. r=bwc, r=froydnj, a=sledru
xpcom/threads/TimerThread.cpp
xpcom/threads/TimerThread.h
--- a/xpcom/threads/TimerThread.cpp
+++ b/xpcom/threads/TimerThread.cpp
@@ -25,17 +25,18 @@ NS_IMPL_ISUPPORTS(TimerThread, nsIRunnab
 
 TimerThread::TimerThread() :
   mInitInProgress(false),
   mInitialized(false),
   mMonitor("TimerThread.mMonitor"),
   mShutdown(false),
   mWaiting(false),
   mNotified(false),
-  mSleeping(false)
+  mSleeping(false),
+  mLastTimerEventLoopRun(TimeStamp::Now())
 {
 }
 
 TimerThread::~TimerThread()
 {
   mThread = nullptr;
 
   NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
@@ -237,16 +238,17 @@ TimerThread::Run()
       uint32_t milliseconds = 100;
       if (ChaosMode::isActive(ChaosMode::TimerScheduling)) {
         milliseconds = ChaosMode::randomUint32LessThan(200);
       }
       waitFor = PR_MillisecondsToInterval(milliseconds);
     } else {
       waitFor = PR_INTERVAL_NO_TIMEOUT;
       TimeStamp now = TimeStamp::Now();
+      mLastTimerEventLoopRun = now;
       nsTimerImpl* timer = nullptr;
 
       if (!mTimers.IsEmpty()) {
         timer = mTimers[0];
 
         if (now >= timer->mTimeout || forceRunThisTimer) {
     next:
           // NB: AddRef before the Release under RemoveTimerInternal to avoid
@@ -430,16 +432,17 @@ TimerThread::RemoveTimer(nsTimerImpl* aT
 
   return NS_OK;
 }
 
 // This function must be called from within a lock
 int32_t
 TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
 {
+  mMonitor.AssertCurrentThreadOwns();
   if (mShutdown) {
     return -1;
   }
 
   TimeStamp now = TimeStamp::Now();
 
   TimerAdditionComparator c(now, aTimer);
   nsTimerImpl** insertSlot = mTimers.InsertElementSorted(aTimer, c);
@@ -458,51 +461,74 @@ TimerThread::AddTimerInternal(nsTimerImp
 #endif
 
   return insertSlot - mTimers.Elements();
 }
 
 bool
 TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
 {
+  mMonitor.AssertCurrentThreadOwns();
   if (!mTimers.RemoveElement(aTimer)) {
     return false;
   }
 
   ReleaseTimerInternal(aTimer);
   return true;
 }
 
 void
 TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
 {
+  if (!mShutdown) {
+    // copied to a local array before releasing in shutdown
+    mMonitor.AssertCurrentThreadOwns();
+  }
   // Order is crucial here -- see nsTimerImpl::Release.
   aTimer->mArmed = false;
   NS_RELEASE(aTimer);
 }
 
 void
 TimerThread::DoBeforeSleep()
 {
+  // Mainthread
+  MonitorAutoLock lock(mMonitor);
+  mLastTimerEventLoopRun = TimeStamp::Now();
   mSleeping = true;
 }
 
+// Note: wake may be notified without preceding sleep notification
 void
 TimerThread::DoAfterSleep()
 {
-  mSleeping = true; // wake may be notified without preceding sleep notification
+  // Mainthread
+  TimeStamp now = TimeStamp::Now();
+
+  MonitorAutoLock lock(mMonitor);
+
+  // an over-estimate of time slept, usually small
+  TimeDuration slept = now - mLastTimerEventLoopRun;
+
+  // Adjust all old timers to expire roughly similar times in the future
+  // compared to when we went to sleep, by adding the time we slept to the
+  // target time. It's slightly possible a few will end up slightly in the
+  // past and fire immediately, but ordering should be preserved.  All
+  // timers retain the exact same order (and relative times) as before
+  // going to sleep.
   for (uint32_t i = 0; i < mTimers.Length(); i ++) {
     nsTimerImpl* timer = mTimers[i];
-    // get and set the delay to cause its timeout to be recomputed
-    uint32_t delay;
-    timer->GetDelay(&delay);
-    timer->SetDelay(delay);
+    timer->mTimeout += slept;
   }
+  mSleeping = false;
+  mLastTimerEventLoopRun = now;
 
-  mSleeping = false;
+  // Wake up the timer thread to process the updated array
+  mNotified = true;
+  mMonitor.Notify();
 }
 
 
 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
 NS_IMETHODIMP
 TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
                      const char16_t* /* aData */)
 {
--- a/xpcom/threads/TimerThread.h
+++ b/xpcom/threads/TimerThread.h
@@ -56,30 +56,31 @@ public:
   }
 
 private:
   ~TimerThread();
 
   mozilla::Atomic<bool> mInitInProgress;
   bool    mInitialized;
 
-  // These two internal helper methods must be called while mLock is held.
+  // These two internal helper methods must be called while mMonitor is held.
   // AddTimerInternal returns the position where the timer was added in the
   // list, or -1 if it failed.
   int32_t AddTimerInternal(nsTimerImpl* aTimer);
   bool    RemoveTimerInternal(nsTimerImpl* aTimer);
   void    ReleaseTimerInternal(nsTimerImpl* aTimer);
 
   nsCOMPtr<nsIThread> mThread;
   Monitor mMonitor;
 
   bool mShutdown;
   bool mWaiting;
   bool mNotified;
   bool mSleeping;
+  TimeStamp mLastTimerEventLoopRun;
 
   nsTArray<nsTimerImpl*> mTimers;
 };
 
 struct TimerAdditionComparator
 {
   TimerAdditionComparator(const mozilla::TimeStamp& aNow,
                           nsTimerImpl* aTimerToInsert) :