Bug 1363829 P15 Expose nsITimer::GetAllowedEarlyFiringMicroseconds(). r=froydnj
authorBen Kelly <ben@wanderview.com>
Wed, 31 May 2017 17:13:20 -0700
changeset 412226 d27d106fc45d32435d4468ed8282f4233af5aa22
parent 412225 9b5fb2901eac7ccd48c7c0bb58ea229f817e963b
child 412227 95421442eec4c1a357d8fd647b2f28dfd8e44760
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1363829
milestone55.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 1363829 P15 Expose nsITimer::GetAllowedEarlyFiringMicroseconds(). r=froydnj
xpcom/threads/TimerThread.cpp
xpcom/threads/TimerThread.h
xpcom/threads/nsITimer.idl
xpcom/threads/nsTimerImpl.cpp
--- a/xpcom/threads/TimerThread.cpp
+++ b/xpcom/threads/TimerThread.cpp
@@ -29,17 +29,18 @@ using namespace mozilla::tasktracer;
 NS_IMPL_ISUPPORTS(TimerThread, nsIRunnable, nsIObserver)
 
 TimerThread::TimerThread() :
   mInitialized(false),
   mMonitor("TimerThread.mMonitor"),
   mShutdown(false),
   mWaiting(false),
   mNotified(false),
-  mSleeping(false)
+  mSleeping(false),
+  mAllowedEarlyFiringMicroseconds(0)
 {
 }
 
 TimerThread::~TimerThread()
 {
   mThread = nullptr;
 
   NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
@@ -402,17 +403,17 @@ TimerThread::Run()
 
   size_t usIntervalResolution;
   BinarySearchIf(MicrosecondsToInterval(), 0, usForPosInterval, IntervalComparator(), &usIntervalResolution);
   MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution - 1) == 0);
   MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution) == 1);
 
   // 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 = usIntervalResolution / 2;
+  mAllowedEarlyFiringMicroseconds = usIntervalResolution / 2;
   bool forceRunNextTimer = false;
 
   while (!mShutdown) {
     // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
     PRIntervalTime waitFor;
     bool forceRunThisTimer = forceRunNextTimer;
     forceRunNextTimer = false;
 
@@ -485,34 +486,34 @@ TimerThread::Run()
 
       if (!mTimers.IsEmpty()) {
         TimeStamp timeout = mTimers[0]->Value()->mTimeout;
 
         // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
         // is due now or overdue.
         //
         // Note that we can only sleep for integer values of a certain
-        // resolution. We use halfMicrosecondsIntervalResolution, calculated
+        // resolution. We use mAllowedEarlyFiringMicroseconds, calculated
         // 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 (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
           // 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) {
+        if (microseconds < mAllowedEarlyFiringMicroseconds) {
           forceRunNextTimer = false;
           goto next; // round down; execute event now
         }
         waitFor = PR_MicrosecondsToInterval(
           static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
         if (waitFor == 0) {
           waitFor = 1;  // round up, wait the minimum time we can wait
         }
@@ -800,8 +801,14 @@ TimerThread::Observe(nsISupports* /* aSu
     DoBeforeSleep();
   } else if (strcmp(aTopic, "wake_notification") == 0 ||
              strcmp(aTopic, "resume_process_notification") == 0) {
     DoAfterSleep();
   }
 
   return NS_OK;
 }
+
+uint32_t
+TimerThread::AllowedEarlyFiringMicroseconds() const
+{
+  return mAllowedEarlyFiringMicroseconds;
+}
--- a/xpcom/threads/TimerThread.h
+++ b/xpcom/threads/TimerThread.h
@@ -51,16 +51,19 @@ public:
   void DoBeforeSleep();
   void DoAfterSleep();
 
   bool IsOnTimerThread() const
   {
     return mThread == NS_GetCurrentThread();
   }
 
+  uint32_t
+  AllowedEarlyFiringMicroseconds() const;
+
 private:
   ~TimerThread();
 
   bool    mInitialized;
 
   // These internal helper methods must be called while mMonitor is held.
   // AddTimerInternal returns false if the insertion failed.
   bool    AddTimerInternal(nsTimerImpl* aTimer);
@@ -111,16 +114,17 @@ private:
     {
       // This is reversed because std::push_heap() sorts the "largest" to
       // the front of the heap.  We want that to be the earliest timer.
       return aRight->mTimeout < aLeft->mTimeout;
     }
   };
 
   nsTArray<UniquePtr<Entry>> mTimers;
+  uint32_t mAllowedEarlyFiringMicroseconds;
 };
 
 struct TimerAdditionComparator
 {
   TimerAdditionComparator(const mozilla::TimeStamp& aNow,
                           nsTimerImpl* aTimerToInsert) :
     now(aNow)
 #ifdef DEBUG
--- a/xpcom/threads/nsITimer.idl
+++ b/xpcom/threads/nsITimer.idl
@@ -260,16 +260,22 @@ interface nsITimer : nsISupports
   /**
    * The nsIEventTarget where the callback will be dispatched. Note that this
    * target may only be set before the call to one of the init methods above.
    *
    * By default the target is the thread that created the timer.
    */
   attribute nsIEventTarget target;
 
+  /**
+   * The number of microseconds this nsITimer implementation can possibly
+   * fire early.
+   */
+  [noscript] readonly attribute unsigned long allowedEarlyFiringMicroseconds;
+
 %{C++
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
 %}
 };
 
 %{C++
 #define NS_TIMER_CONTRACTID "@mozilla.org/timer;1"
 #define NS_TIMER_CALLBACK_TOPIC "timer-callback"
--- a/xpcom/threads/nsTimerImpl.cpp
+++ b/xpcom/threads/nsTimerImpl.cpp
@@ -453,16 +453,22 @@ nsTimerImpl::SetTarget(nsIEventTarget* a
   if (aTarget) {
     mEventTarget = aTarget;
   } else {
     mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
   }
   return NS_OK;
 }
 
+nsresult
+nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut)
+{
+  *aValueOut = gThread ? gThread->AllowedEarlyFiringMicroseconds() : 0;
+  return NS_OK;
+}
 
 void
 nsTimerImpl::Fire(int32_t aGeneration)
 {
   uint8_t oldType;
   uint32_t oldDelay;
   TimeStamp oldTimeout;
   nsCOMPtr<nsITimer> kungFuDeathGrip;