Simplify and guard access to WindowsRealTimeClock.

Addresses data race in get_time() causing incorrect timer roll-over
detection. This roll-over caused NTP timers to jump by 2^32
milliseconds affecting A/V sync and end-to-end delay calculations.

BUG=4206
R=dvyukov@google.com, tommi@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/33959004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8112 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/system_wrappers/source/clock.cc b/webrtc/system_wrappers/source/clock.cc
index 33eb856..8e69652 100644
--- a/webrtc/system_wrappers/source/clock.cc
+++ b/webrtc/system_wrappers/source/clock.cc
@@ -12,14 +12,14 @@
 
 #if defined(_WIN32)
 // Windows needs to be included before mmsystem.h
-#include <Windows.h>
-#include <WinSock.h>
+#include "webrtc/base/win32.h"
 #include <MMSystem.h>
 #elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
 #include <sys/time.h>
 #include <time.h>
 #endif
 
+#include "webrtc/base/criticalsection.h"
 #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h"
 #include "webrtc/system_wrappers/interface/tick_util.h"
 
@@ -33,99 +33,6 @@
       static_cast<int64_t>(ntp_frac_ms + 0.5);
 }
 
-#if defined(_WIN32)
-
-struct reference_point {
-  FILETIME      file_time;
-  LARGE_INTEGER counterMS;
-};
-
-struct WindowsHelpTimer {
-  volatile LONG _timeInMs;
-  volatile LONG _numWrapTimeInMs;
-  reference_point _ref_point;
-
-  volatile LONG _sync_flag;
-};
-
-void Synchronize(WindowsHelpTimer* help_timer) {
-  const LONG start_value = 0;
-  const LONG new_value = 1;
-  const LONG synchronized_value = 2;
-
-  LONG compare_flag = new_value;
-  while (help_timer->_sync_flag == start_value) {
-    const LONG new_value = 1;
-    compare_flag = InterlockedCompareExchange(
-        &help_timer->_sync_flag, new_value, start_value);
-  }
-  if (compare_flag != start_value) {
-    // This thread was not the one that incremented the sync flag.
-    // Block until synchronization finishes.
-    while (compare_flag != synchronized_value) {
-      ::Sleep(0);
-    }
-    return;
-  }
-  // Only the synchronizing thread gets here so this part can be
-  // considered single threaded.
-
-  // set timer accuracy to 1 ms
-  timeBeginPeriod(1);
-  FILETIME    ft0 = { 0, 0 },
-              ft1 = { 0, 0 };
-  //
-  // Spin waiting for a change in system time. Get the matching
-  // performance counter value for that time.
-  //
-  ::GetSystemTimeAsFileTime(&ft0);
-  do {
-    ::GetSystemTimeAsFileTime(&ft1);
-
-    help_timer->_ref_point.counterMS.QuadPart = ::timeGetTime();
-    ::Sleep(0);
-  } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) &&
-          (ft0.dwLowDateTime == ft1.dwLowDateTime));
-  help_timer->_ref_point.file_time = ft1;
-  timeEndPeriod(1);
-}
-
-void get_time(WindowsHelpTimer* help_timer, FILETIME& current_time) {
-  // we can't use query performance counter due to speed stepping
-  DWORD t = timeGetTime();
-  // NOTE: we have a missmatch in sign between _timeInMs(LONG) and
-  // (DWORD) however we only use it here without +- etc
-  volatile LONG* timeInMsPtr = &help_timer->_timeInMs;
-  // Make sure that we only inc wrapper once.
-  DWORD old = InterlockedExchange(timeInMsPtr, t);
-  if(old > t) {
-    // wrap
-    help_timer->_numWrapTimeInMs++;
-  }
-  LARGE_INTEGER elapsedMS;
-  elapsedMS.HighPart = help_timer->_numWrapTimeInMs;
-  elapsedMS.LowPart = t;
-
-  elapsedMS.QuadPart = elapsedMS.QuadPart -
-      help_timer->_ref_point.counterMS.QuadPart;
-
-  // Translate to 100-nanoseconds intervals (FILETIME resolution)
-  // and add to reference FILETIME to get current FILETIME.
-  ULARGE_INTEGER filetime_ref_as_ul;
-
-  filetime_ref_as_ul.HighPart =
-      help_timer->_ref_point.file_time.dwHighDateTime;
-  filetime_ref_as_ul.LowPart =
-      help_timer->_ref_point.file_time.dwLowDateTime;
-  filetime_ref_as_ul.QuadPart +=
-      (ULONGLONG)((elapsedMS.QuadPart)*1000*10);
-
-  // Copy to result
-  current_time.dwHighDateTime = filetime_ref_as_ul.HighPart;
-  current_time.dwLowDateTime = filetime_ref_as_ul.LowPart;
-}
-#endif
-
 class RealTimeClock : public Clock {
   // Return a timestamp in milliseconds relative to some arbitrary source; the
   // source is fixed for this clock.
@@ -178,14 +85,24 @@
 };
 
 #if defined(_WIN32)
+// TODO(pbos): Consider modifying the implementation to synchronize itself
+// against system time (update ref_point_, make it non-const) periodically to
+// prevent clock drift.
 class WindowsRealTimeClock : public RealTimeClock {
  public:
-  WindowsRealTimeClock(WindowsHelpTimer* helpTimer)
-      : _helpTimer(helpTimer) {}
+  WindowsRealTimeClock()
+      : last_time_ms_(0),
+        num_timer_wraps_(0),
+        ref_point_(GetSystemReferencePoint()) {}
 
   virtual ~WindowsRealTimeClock() {}
 
  protected:
+  struct ReferencePoint {
+    FILETIME file_time;
+    LARGE_INTEGER counter_ms;
+  };
+
   virtual timeval CurrentTimeVal() const OVERRIDE {
     const uint64_t FILETIME_1970 = 0x019db1ded53e8000;
 
@@ -195,7 +112,7 @@
 
     // We can't use query performance counter since they can change depending on
     // speed stepping.
-    get_time(_helpTimer, StartTime);
+    GetTime(&StartTime);
 
     Time = (((uint64_t) StartTime.dwHighDateTime) << 32) +
            (uint64_t) StartTime.dwLowDateTime;
@@ -208,7 +125,65 @@
     return tv;
   }
 
-  WindowsHelpTimer* _helpTimer;
+  void GetTime(FILETIME* current_time) const {
+    DWORD t;
+    LARGE_INTEGER elapsed_ms;
+    {
+      rtc::CritScope lock(&crit_);
+      // time MUST be fetched inside the critical section to avoid non-monotonic
+      // last_time_ms_ values that'll register as incorrect wraparounds due to
+      // concurrent calls to GetTime.
+      t = timeGetTime();
+      if (t < last_time_ms_)
+        num_timer_wraps_++;
+      last_time_ms_ = t;
+      elapsed_ms.HighPart = num_timer_wraps_;
+    }
+    elapsed_ms.LowPart = t;
+    elapsed_ms.QuadPart = elapsed_ms.QuadPart - ref_point_.counter_ms.QuadPart;
+
+    // Translate to 100-nanoseconds intervals (FILETIME resolution)
+    // and add to reference FILETIME to get current FILETIME.
+    ULARGE_INTEGER filetime_ref_as_ul;
+    filetime_ref_as_ul.HighPart = ref_point_.file_time.dwHighDateTime;
+    filetime_ref_as_ul.LowPart = ref_point_.file_time.dwLowDateTime;
+    filetime_ref_as_ul.QuadPart +=
+        static_cast<ULONGLONG>((elapsed_ms.QuadPart) * 1000 * 10);
+
+    // Copy to result
+    current_time->dwHighDateTime = filetime_ref_as_ul.HighPart;
+    current_time->dwLowDateTime = filetime_ref_as_ul.LowPart;
+  }
+
+  static ReferencePoint GetSystemReferencePoint() {
+    ReferencePoint ref = {0};
+    FILETIME ft0 = {0};
+    FILETIME ft1 = {0};
+    // Spin waiting for a change in system time. As soon as this change happens,
+    // get the matching call for timeGetTime() as soon as possible. This is
+    // assumed to be the most accurate offset that we can get between
+    // timeGetTime() and system time.
+
+    // Set timer accuracy to 1 ms.
+    timeBeginPeriod(1);
+    GetSystemTimeAsFileTime(&ft0);
+    do {
+      GetSystemTimeAsFileTime(&ft1);
+
+      ref.counter_ms.QuadPart = timeGetTime();
+      Sleep(0);
+    } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) &&
+             (ft0.dwLowDateTime == ft1.dwLowDateTime));
+    ref.file_time = ft1;
+    timeEndPeriod(1);
+    return ref;
+  }
+
+  // mutable as time-accessing functions are const.
+  mutable rtc::CriticalSection crit_;
+  mutable DWORD last_time_ms_;
+  mutable LONG num_timer_wraps_;
+  const ReferencePoint ref_point_;
 };
 
 #elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
@@ -230,32 +205,25 @@
 };
 #endif
 
-
 #if defined(_WIN32)
-// Keeps the global state for the Windows implementation of RtpRtcpClock.
-// Note that this is a POD. Only PODs are allowed to have static storage
-// duration according to the Google Style guide.
-//
-// Note that on Windows, GetSystemTimeAsFileTime has poorer (up to 15 ms)
-// resolution than the media timers, hence the WindowsHelpTimer context
-// object and Synchronize API to sync the two.
-//
-// We only sync up once, which means that on Windows, our realtime clock
-// wont respond to system time/date changes without a program restart.
-// TODO(henrike): We should probably call sync more often to catch
-// drift and time changes for parity with other platforms.
-
-static WindowsHelpTimer *SyncGlobalHelpTimer() {
-  static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0};
-  Synchronize(&global_help_timer);
-  return &global_help_timer;
-}
+static WindowsRealTimeClock* volatile g_shared_clock = nullptr;
 #endif
-
 Clock* Clock::GetRealTimeClock() {
 #if defined(_WIN32)
-  static WindowsRealTimeClock clock(SyncGlobalHelpTimer());
-  return &clock;
+  // This read relies on volatile read being atomic-load-acquire. This is
+  // true in MSVC since at least 2005:
+  // "A read of a volatile object (volatile read) has Acquire semantics"
+  if (g_shared_clock != nullptr)
+    return g_shared_clock;
+  WindowsRealTimeClock* clock = new WindowsRealTimeClock;
+  if (InterlockedCompareExchangePointer(
+          reinterpret_cast<void* volatile*>(&g_shared_clock), clock, nullptr) !=
+      nullptr) {
+    // g_shared_clock was assigned while we constructed/tried to assign our
+    // instance, delete our instance and use the existing one.
+    delete clock;
+  }
+  return g_shared_clock;
 #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
   static UnixRealTimeClock clock;
   return &clock;