From 53ef4cd684d725ac1a188eb80457b71631206201 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Mon, 3 Mar 2014 18:12:51 +1300 Subject: [PATCH] Bug 955888. Part 8: In chaos mode, vary timer duration (while respecting order of firing). r=bz We also ensure that the mean timer duration is unchanged. --HG-- extra : rebase_source : 21a43c2c6a0f677263464b8a23c5f605e36b9d06 --- xpcom/threads/TimerThread.cpp | 50 ++++++++++++++++++++++++++++++----- xpcom/threads/TimerThread.h | 1 + 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp index 0303283076cb..c192b76ed75c 100644 --- a/xpcom/threads/TimerThread.cpp +++ b/xpcom/threads/TimerThread.cpp @@ -12,6 +12,8 @@ #include "nsIObserverService.h" #include "nsIServiceManager.h" #include "mozilla/Services.h" +#include "mozilla/ChaosMode.h" +#include "mozilla/ArrayUtils.h" #include @@ -25,6 +27,7 @@ TimerThread::TimerThread() : mMonitor("TimerThread.mMonitor"), mShutdown(false), mWaiting(false), + mNotified(false), mSleeping(false) { } @@ -133,8 +136,10 @@ nsresult TimerThread::Shutdown() mShutdown = true; // notify the cond var so that Run() can return - if (mWaiting) + if (mWaiting) { + mNotified = true; mMonitor.Notify(); + } // Need to copy content of mTimers array to a local array // because call to timers' ReleaseCallback() (and release its self) @@ -199,14 +204,21 @@ NS_IMETHODIMP TimerThread::Run() // Half of the amount of microseconds needed to get positive PRIntervalTime. // We use this to decide how to round our wait times later int32_t halfMicrosecondsIntervalResolution = high >> 1; + bool forceRunNextTimer = false; while (!mShutdown) { // Have to use PRIntervalTime here, since PR_WaitCondVar takes it PRIntervalTime waitFor; + bool forceRunThisTimer = forceRunNextTimer; + forceRunNextTimer = false; if (mSleeping) { // Sleep for 0.1 seconds while not firing timers. - waitFor = PR_MillisecondsToInterval(100); + uint32_t milliseconds = 100; + if (ChaosMode::isActive()) { + milliseconds = ChaosMode::randomUint32LessThan(200); + } + waitFor = PR_MillisecondsToInterval(milliseconds); } else { waitFor = PR_INTERVAL_NO_TIMEOUT; TimeStamp now = TimeStamp::Now(); @@ -215,7 +227,7 @@ NS_IMETHODIMP TimerThread::Run() if (!mTimers.IsEmpty()) { timer = mTimers[0]; - if (now >= timer->mTimeout) { + if (now >= timer->mTimeout || forceRunThisTimer) { next: // NB: AddRef before the Release under RemoveTimerInternal to avoid // mRefCnt passing through zero, in case all other refs than the one @@ -283,8 +295,22 @@ NS_IMETHODIMP TimerThread::Run() // before, to do the optimal rounding (i.e., of how to decide what // interval is so small we should not wait at all). double microseconds = (timeout - now).ToMilliseconds()*1000; - if (microseconds < halfMicrosecondsIntervalResolution) + + if (ChaosMode::isActive()) { + // The mean value of sFractions must be 1 to ensure that + // the average of a long sequence of timeouts converges to the + // actual sum of their times. + static const float sFractions[] = { + 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f + }; + microseconds *= sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))]; + forceRunNextTimer = true; + } + + if (microseconds < halfMicrosecondsIntervalResolution) { + forceRunNextTimer = false; goto next; // round down; execute event now + } waitFor = PR_MicrosecondsToInterval(static_cast(microseconds)); // Floor is accurate enough. if (waitFor == 0) waitFor = 1; // round up, wait the minimum time we can wait @@ -303,7 +329,11 @@ NS_IMETHODIMP TimerThread::Run() } mWaiting = true; + mNotified = false; mMonitor.Wait(waitFor); + if (mNotified) { + forceRunNextTimer = false; + } mWaiting = false; } @@ -320,8 +350,10 @@ nsresult TimerThread::AddTimer(nsTimerImpl *aTimer) return NS_ERROR_OUT_OF_MEMORY; // Awaken the timer thread. - if (mWaiting && i == 0) + if (mWaiting && i == 0) { + mNotified = true; mMonitor.Notify(); + } return NS_OK; } @@ -339,8 +371,10 @@ nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer) return NS_ERROR_OUT_OF_MEMORY; // Awaken the timer thread. - if (mWaiting && i == 0) + if (mWaiting && i == 0) { + mNotified = true; mMonitor.Notify(); + } return NS_OK; } @@ -360,8 +394,10 @@ nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer) return NS_ERROR_NOT_AVAILABLE; // Awaken the timer thread. - if (mWaiting) + if (mWaiting) { + mNotified = true; mMonitor.Notify(); + } return NS_OK; } diff --git a/xpcom/threads/TimerThread.h b/xpcom/threads/TimerThread.h index 3e76bfc77887..eaa005f71948 100644 --- a/xpcom/threads/TimerThread.h +++ b/xpcom/threads/TimerThread.h @@ -71,6 +71,7 @@ private: bool mShutdown; bool mWaiting; + bool mNotified; bool mSleeping; nsTArray mTimers;