Bug 784859 - Part 6: Remove the need for locking in all calls to TimeStamp::Now; r=bbondy
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 08 Sep 2012 14:13:17 -0400
changeset 104653 d4a8c7efef04
parent 104652 9544f94ccdb8
child 104654 898df3138008
push id23436
push userryanvm@gmail.com
push date2012-09-09 01:10 +0000
treeherdermozilla-central@f31d1aa89848 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbondy
bugs784859
milestone18.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 784859 - Part 6: Remove the need for locking in all calls to TimeStamp::Now; r=bbondy
xpcom/ds/TimeStamp_windows.cpp
--- a/xpcom/ds/TimeStamp_windows.cpp
+++ b/xpcom/ds/TimeStamp_windows.cpp
@@ -170,17 +170,17 @@ static LONGLONG sOverrunThreshold;
 static const DWORD kLockSpinCount = 4096;
 
 // Common mutex (thanks the relative complexity of the logic, this is better
 // then using CMPXCHG8B.)
 // It is protecting the globals bellow.
 CRITICAL_SECTION sTimeStampLock;
 
 // ----------------------------------------------------------------------------
-// Globals heavily chaning at runtime, protected with sTimeStampLock mutex
+// Globals heavily changing at runtime, protected with sTimeStampLock mutex
 // ----------------------------------------------------------------------------
 
 // The calibrated difference between QPC and GTC.
 //
 // Kept in [mt]
 static LONGLONG sSkew = 0;
 
 // Keeps the last result we have returned from sGetTickCount64 (bellow).  Protects
@@ -195,23 +195,32 @@ static ULONGLONG sLastGTCResult = 0;
 // Kept in [mt]
 static ULONGLONG sLastResult = 0;
 
 // Time of the last performed calibration.
 //
 // Kept in [ms]
 static ULONGLONG sLastCalibrated;
 
+// The following variable stores two booleans, both initialized to false.
+//
+// The lower word is fallbackToGTC:
 // After we have detected a run out of bounderies set this to true.  This
 // then disallows use of QPC result for the hi-res timer.
-static bool sFallBackToGTC = false;
-
+//
+// The higher word is forceRecalibrate:
 // Set to true to force recalibration on QPC read.  This is generally set after
 // system wake up, during which skew can change a lot.
-static bool sForceRecalibrate = false;
+static union CalibrationFlags {
+  struct {
+    bool fallBackToGTC;
+    bool forceRecalibrate;
+  } flags;
+  uint32_t dwordValue;
+} sCalibrationFlags;
 
 
 namespace mozilla {
 
 
 static ULONGLONG
 CalibratedPerformanceCounter();
 
@@ -308,18 +317,21 @@ NS_IMETHODIMP
 StandbyObserver::Observe(nsISupports *subject,
                          const char *topic,
                          const PRUnichar *data)
 {
   AutoCriticalSection lock(&sTimeStampLock);
 
   // Clear the potentiall fallback flag now and try using
   // QPC again after wake up.
-  sFallBackToGTC = false;
-  sForceRecalibrate = true;
+  CalibrationFlags value;
+  value.flags.fallBackToGTC = false;
+  value.flags.forceRecalibrate = true;
+  sCalibrationFlags.dwordValue = value.dwordValue; // aligned 32-bit writes are atomic
+
   LOG(("TimeStamp: system has woken up, reset GTC fallback"));
 
   return NS_OK;
 }
 
 
 // ----------------------------------------------------------------------------
 // The timer core implementation
@@ -443,17 +455,17 @@ PerformanceCounter()
   ::QueryPerformanceCounter(&pc);
   return pc.QuadPart * 1000ULL;
 }
 
 // Called when we detect a larger deviation of QPC to disable it.
 static inline void
 RecordFlaw()
 {
-  sFallBackToGTC = true;
+  sCalibrationFlags.flags.fallBackToGTC = true;
 
   LOG(("TimeStamp: falling back to GTC :("));
 
 #if 0
   // This code has been disabled, because we:
   // 0. InitResolution must not be called under the lock (would reenter) while
   //    we shouldn't release it here just to allow it
   // 1. may return back to using QPC after system wake up
@@ -476,52 +488,56 @@ RecordFlaw()
 // Arguments:
 // overflow - the calculated overflow out of the bounderies for skew difference
 // qpc - current value of QueryPerformanceCounter
 // gtc - current value of GetTickCount, more actual according possible system
 //       sleep between read of QPC and GTC
 static inline bool
 CheckCalibration(LONGLONG overflow, ULONGLONG qpc, ULONGLONG gtc)
 {
-  if (sFallBackToGTC) {
+  CalibrationFlags value;
+  value.dwordValue = sCalibrationFlags.dwordValue; // aligned 32-bit reads are atomic
+  if (value.flags.fallBackToGTC) {
     // We are forbidden to use QPC
     return false;
   }
 
   ULONGLONG sinceLastCalibration = gtc - sLastCalibrated;
 
-  if (overflow && !sForceRecalibrate) {
+  if (overflow && !value.flags.forceRecalibrate) {
     // Calculate trend of the overflow to correspond to the calibration
     // interval, we may get here long after the last calibration because we
     // either didn't read the hi-res function or the system was suspended.
     ULONGLONG trend = LONGLONG(overflow *
       (double(kCalibrationInterval) / sinceLastCalibration));
 
     LOG(("TimeStamp: calibration after %llus with overflow %1.4fms"
          ", adjusted trend per calibration interval is %1.4fms",
          sinceLastCalibration / 1000,
          mt2ms_d(overflow),
          mt2ms_d(trend)));
 
     if (trend > ms2mt(kOverflowLimit)) {
-      // This sets sFallBackToGTC, we have detected
+      // This sets fallBackToGTC, we have detected
       // an unreliability of QPC, stop using it.
+      AutoCriticalSection lock(&sTimeStampLock);
       RecordFlaw();
       return false;
     }
   }
 
-  if (sinceLastCalibration > kCalibrationInterval || sForceRecalibrate) {
+  if (sinceLastCalibration > kCalibrationInterval || value.flags.forceRecalibrate) {
     // Recalculate the skew now
+    AutoCriticalSection lock(&sTimeStampLock);
     sSkew = qpc - ms2mt(gtc);
     sLastCalibrated = gtc;
     LOG(("TimeStamp: new skew is %1.2fms (force:%d)",
-      mt2ms_d(sSkew), sForceRecalibrate));
+      mt2ms_d(sSkew), value.flags.forceRecalibrate));
 
-    sForceRecalibrate = false;
+    sCalibrationFlags.flags.forceRecalibrate = false;
   }
 
   return true;
 }
 
 // AtomicStoreIfGreaterThan tries to store the maximum of two values in one of them
 // without locking.  The only scenario in which two racing threads may corrupt the
 // maximum value is when they both try to increase the value without knowing about
@@ -565,41 +581,36 @@ CalibratedPerformanceCounter()
   // the largest bottleneck, let threads read the value concurently to have
   // possibly a better performance.
 
   ULONGLONG qpc = PerformanceCounter();
 
   // Rollover protection
   ULONGLONG gtc = sGetTickCount64();
 
-  ULONGLONG result;
-  {
-  AutoCriticalSection lock(&sTimeStampLock);
-
   LONGLONG diff = qpc - ms2mt(gtc) - sSkew;
   LONGLONG overflow = 0;
 
   if (diff < sUnderrunThreshold) {
     overflow = sUnderrunThreshold - diff;
   }
   else if (diff > sOverrunThreshold) {
     overflow = diff - sOverrunThreshold;
   }
 
-  result = qpc;
+  ULONGLONG result = qpc;
   if (!CheckCalibration(overflow, qpc, gtc)) {
     // We are back on GTC, QPC has been observed unreliable
     result = ms2mt(gtc) + sSkew;
   }
 
 #if 0
   LOG(("TimeStamp: result = %1.2fms, diff = %1.4fms",
       mt2ms_d(result), mt2ms_d(diff)));
 #endif
-  }
 
   return AtomicStoreIfGreaterThan(&sLastResult, result);
 }
 
 // ----------------------------------------------------------------------------
 // TimeDuration and TimeStamp implementation
 // ----------------------------------------------------------------------------
 
@@ -666,17 +677,17 @@ TimeStamp::Startup()
 
   InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
 
   LARGE_INTEGER freq;
   BOOL QPCAvailable = ::QueryPerformanceFrequency(&freq);
   if (!QPCAvailable) {
     // No Performance Counter.  Fall back to use GetTickCount.
     sFrequencyPerSec = 1;
-    sFallBackToGTC = true;
+    sCalibrationFlags.flags.fallBackToGTC = true;
     InitResolution();
 
     LOG(("TimeStamp: using GetTickCount"));
     return NS_OK;
   }
 
   sFrequencyPerSec = freq.QuadPart;