summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/SConscript1
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/time.h1
-rw-r--r--base/time_unittest.cc109
-rw-r--r--base/time_unittest_win.cc102
-rw-r--r--base/time_win.cc125
6 files changed, 180 insertions, 162 deletions
diff --git a/base/SConscript b/base/SConscript
index e6ce007..a2fc266 100644
--- a/base/SConscript
+++ b/base/SConscript
@@ -290,6 +290,7 @@ if env['PLATFORM'] == 'win32':
'object_watcher_unittest.cc',
'pe_image_unittest.cc',
'sys_string_conversions_win_unittest.cc',
+ 'time_unittest_win.cc',
'win_util_unittest.cc',
'wmi_util_unittest.cc',
'gfx/platform_canvas_unittest_win.cc',
diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj
index 4ffb11b..0c22754 100644
--- a/base/build/base_unittests.vcproj
+++ b/base/build/base_unittests.vcproj
@@ -320,6 +320,10 @@
>
</File>
<File
+ RelativePath="..\time_unittest_win.cc"
+ >
+ </File>
+ <File
RelativePath="..\timer_unittest.cc"
>
</File>
diff --git a/base/time.h b/base/time.h
index c25419f..17296e3 100644
--- a/base/time.h
+++ b/base/time.h
@@ -436,6 +436,7 @@ class TimeTicks {
int64 ticks_;
#if defined(OS_WIN)
+ public:
// The function to use for counting ticks.
typedef int (__stdcall *TickFunction)(void);
static TickFunction tick_function_;
diff --git a/base/time_unittest.cc b/base/time_unittest.cc
index 9da5779..3cd9595 100644
--- a/base/time_unittest.cc
+++ b/base/time_unittest.cc
@@ -2,16 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "build/build_config.h"
-
#include <time.h>
-#if defined(OS_WIN)
-#include <process.h>
-#endif
-
-#include "base/time.h"
#include "base/platform_thread.h"
+#include "base/time.h"
+#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
// Test conversions to/from time_t and exploding/unexploding.
@@ -116,103 +111,3 @@ TEST(TimeDelta, FromAndIn) {
EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF());
EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds());
}
-
-#if defined(OS_WIN)
-
-// TODO(pinkerton): Need to find a way to mock this for non-windows.
-
-namespace {
-
-class MockTimeTicks : public TimeTicks {
- public:
- static int Ticker() {
- return static_cast<int>(InterlockedIncrement(&ticker_));
- }
-
- static void InstallTicker() {
- old_tick_function_ = tick_function_;
- tick_function_ = reinterpret_cast<TickFunction>(&Ticker);
- ticker_ = -5;
- }
-
- static void UninstallTicker() {
- tick_function_ = old_tick_function_;
- }
-
- private:
- static volatile LONG ticker_;
- static TickFunction old_tick_function_;
-};
-
-volatile LONG MockTimeTicks::ticker_;
-MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_;
-
-HANDLE g_rollover_test_start;
-
-unsigned __stdcall RolloverTestThreadMain(void* param) {
- int64 counter = reinterpret_cast<int64>(param);
- DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
- EXPECT_EQ(rv, WAIT_OBJECT_0);
-
- TimeTicks last = TimeTicks::Now();
- for (int index = 0; index < counter; index++) {
- TimeTicks now = TimeTicks::Now();
- int64 milliseconds = (now - last).InMilliseconds();
- EXPECT_GT(milliseconds, 0);
- EXPECT_LT(milliseconds, 250);
- last = now;
- }
- return 0;
-}
-
-} // namespace
-
-TEST(TimeTicks, Rollover) {
- // The internal counter rolls over at ~49days. We'll use a mock
- // timer to test this case.
- // Basic test algorithm:
- // 1) Set clock to rollover - N
- // 2) Create N threads
- // 3) Start the threads
- // 4) Each thread loops through TimeTicks() N times
- // 5) Each thread verifies integrity of result.
-
- const int kThreads = 8;
- // Use int64 so we can cast into a void* without a compiler warning.
- const int64 kChecks = 10;
-
- // It takes a lot of iterations to reproduce the bug!
- // (See bug 1081395)
- for (int loop = 0; loop < 4096; loop++) {
- // Setup
- MockTimeTicks::InstallTicker();
- g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
- HANDLE threads[kThreads];
-
- for (int index = 0; index < kThreads; index++) {
- void* argument = reinterpret_cast<void*>(kChecks);
- unsigned thread_id;
- threads[index] = reinterpret_cast<HANDLE>(
- _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
- &thread_id));
- EXPECT_NE((HANDLE)NULL, threads[index]);
- }
-
- // Start!
- SetEvent(g_rollover_test_start);
-
- // Wait for threads to finish
- for (int index = 0; index < kThreads; index++) {
- DWORD rv = WaitForSingleObject(threads[index], INFINITE);
- EXPECT_EQ(rv, WAIT_OBJECT_0);
- }
-
- CloseHandle(g_rollover_test_start);
-
- // Teardown
- MockTimeTicks::UninstallTicker();
- }
-}
-
-#endif
-
diff --git a/base/time_unittest_win.cc b/base/time_unittest_win.cc
new file mode 100644
index 0000000..5b29db2
--- /dev/null
+++ b/base/time_unittest_win.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <process.h>
+
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockTimeTicks : public TimeTicks {
+ public:
+ static int Ticker() {
+ return static_cast<int>(InterlockedIncrement(&ticker_));
+ }
+
+ static void InstallTicker() {
+ old_tick_function_ = tick_function_;
+ tick_function_ = reinterpret_cast<TickFunction>(&Ticker);
+ ticker_ = -5;
+ }
+
+ static void UninstallTicker() {
+ tick_function_ = old_tick_function_;
+ }
+
+ private:
+ static volatile LONG ticker_;
+ static TickFunction old_tick_function_;
+};
+
+volatile LONG MockTimeTicks::ticker_;
+MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_;
+
+HANDLE g_rollover_test_start;
+
+unsigned __stdcall RolloverTestThreadMain(void* param) {
+ int64 counter = reinterpret_cast<int64>(param);
+ DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
+ EXPECT_EQ(rv, WAIT_OBJECT_0);
+
+ TimeTicks last = TimeTicks::Now();
+ for (int index = 0; index < counter; index++) {
+ TimeTicks now = TimeTicks::Now();
+ int64 milliseconds = (now - last).InMilliseconds();
+ EXPECT_GT(milliseconds, 0);
+ EXPECT_LT(milliseconds, 250);
+ last = now;
+ }
+ return 0;
+}
+
+} // namespace
+
+TEST(TimeTicks, WinRollover) {
+ // The internal counter rolls over at ~49days. We'll use a mock
+ // timer to test this case.
+ // Basic test algorithm:
+ // 1) Set clock to rollover - N
+ // 2) Create N threads
+ // 3) Start the threads
+ // 4) Each thread loops through TimeTicks() N times
+ // 5) Each thread verifies integrity of result.
+
+ const int kThreads = 8;
+ // Use int64 so we can cast into a void* without a compiler warning.
+ const int64 kChecks = 10;
+
+ // It takes a lot of iterations to reproduce the bug!
+ // (See bug 1081395)
+ for (int loop = 0; loop < 4096; loop++) {
+ // Setup
+ MockTimeTicks::InstallTicker();
+ g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
+ HANDLE threads[kThreads];
+
+ for (int index = 0; index < kThreads; index++) {
+ void* argument = reinterpret_cast<void*>(kChecks);
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
+ &thread_id));
+ EXPECT_NE((HANDLE)NULL, threads[index]);
+ }
+
+ // Start!
+ SetEvent(g_rollover_test_start);
+
+ // Wait for threads to finish
+ for (int index = 0; index < kThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], INFINITE);
+ EXPECT_EQ(rv, WAIT_OBJECT_0);
+ }
+
+ CloseHandle(g_rollover_test_start);
+
+ // Teardown
+ MockTimeTicks::UninstallTicker();
+ }
+}
diff --git a/base/time_win.cc b/base/time_win.cc
index 9d38993..a919395 100644
--- a/base/time_win.cc
+++ b/base/time_win.cc
@@ -7,9 +7,11 @@
#pragma comment(lib, "winmm.lib")
#include <windows.h>
#include <mmsystem.h>
+
#include "base/basictypes.h"
#include "base/lock.h"
#include "base/logging.h"
+#include "base/singleton.h"
namespace {
@@ -122,53 +124,39 @@ void Time::Explode(bool is_local, Exploded* exploded) const {
}
// TimeTicks ------------------------------------------------------------------
-
-TimeTicks::TickFunction TimeTicks::tick_function_=
+TimeTicks::TickFunction TimeTicks::tick_function_ =
reinterpret_cast<TickFunction>(&timeGetTime);
-// static
-TimeTicks TimeTicks::Now() {
- // Uses the multimedia timers on Windows to get a higher resolution clock.
- // timeGetTime() provides a resolution which is variable depending on
- // hardware and system configuration. It can also be changed by other
- // apps. This class does not attempt to change the resolution of the
- // timer, because we don't want to affect other applications.
-
- // timeGetTime() should at least be accurate to ~5ms on all systems.
- // timeGetTime() returns a 32-bit millisecond counter which has rollovers
- // every ~49 days.
- static DWORD last_tick_count = 0;
- static int64 tick_rollover_accum = 0;
- static Lock* tick_lock = NULL; // To protect during rollover periods.
-
- // Lazily create the lock we use.
- if (!tick_lock) {
- Lock* new_lock = new Lock;
- if (InterlockedCompareExchangePointer(
- reinterpret_cast<PVOID*>(&tick_lock), new_lock, NULL)) {
- delete new_lock;
- }
- }
+namespace {
- // Atomically protect the low and high 32bit values for time.
- // In the future we may be able to optimize with
- // InterlockedCompareExchange64, but that doesn't work on XP.
- DWORD tick_count;
- int64 rollover_count;
- {
- AutoLock lock(*tick_lock);
- tick_count = tick_function_();
- if (tick_count < last_tick_count)
- tick_rollover_accum += GG_INT64_C(0x100000000);
-
- last_tick_count = tick_count;
- rollover_count = tick_rollover_accum;
+// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
+// because it returns the number of millisecond since Windows has started, which
+// will roll over the 32-bit value every ~49 days. We try to track rollover
+// ourselves, which works if TimeTicks::Now() is called at least every 49 days.
+class NowSingleton {
+ public:
+ NowSingleton()
+ : rollover_(TimeDelta::FromMilliseconds(0)),
+ last_seen_(TimeTicks::tick_function_()) { }
+
+ TimeDelta Now() {
+ AutoLock locked(lock_);
+ // We should hold the lock while calling tick_function_ to make sure that
+ // we keep our last_seen_ stay correctly in sync.
+ DWORD now = TimeTicks::tick_function_();
+ if (now < last_seen_)
+ rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days.
+ last_seen_ = now;
+ return TimeDelta::FromMilliseconds(now) + rollover_;
}
- // GetTickCount returns milliseconds, we want microseconds.
- return TimeTicks((tick_count + rollover_count) *
- Time::kMicrosecondsPerMillisecond);
-}
+ private:
+ Lock lock_; // To protected last_seen_ and rollover_.
+ TimeDelta rollover_; // Accumulation of time lost do to rollover.
+ DWORD last_seen_; // The last timeGetTime value we saw, to detect rollover.
+
+ DISALLOW_COPY_AND_ASSIGN(NowSingleton);
+};
// Overview of time counters:
// (1) CPU cycle counter. (Retrieved via RDTSC)
@@ -198,23 +186,50 @@ TimeTicks TimeTicks::Now() {
// (3) System time. The system time provides a low-resolution (typically 10ms
// to 55 milliseconds) time stamp but is comparatively less expensive to
// retrieve and more reliable.
+class UnreliableHighResNowSingleton {
+ public:
+ UnreliableHighResNowSingleton() : ticks_per_microsecond_(0) {
+ LARGE_INTEGER ticks_per_sec = {0};
+ if (!QueryPerformanceFrequency(&ticks_per_sec))
+ return; // Broken, we don't guarantee this function works.
+ ticks_per_microsecond_ =
+ ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond;
+ }
-// static
-TimeTicks TimeTicks::UnreliableHighResNow() {
+ bool IsBroken() {
+ return ticks_per_microsecond_ == 0;
+ }
+
+ TimeDelta Now() {
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ return TimeDelta::FromMicroseconds(now.QuadPart / ticks_per_microsecond_);
+ }
+
+ private:
// Cached clock frequency -> microseconds. This assumes that the clock
// frequency is faster than one microsecond (which is 1MHz, should be OK).
- static int64 ticks_per_microsecond = 0;
+ int64 ticks_per_microsecond_; // 0 indicates QPF failed and we're broken.
- if (ticks_per_microsecond == 0) {
- LARGE_INTEGER ticks_per_sec = { 0, 0 };
- if (!QueryPerformanceFrequency(&ticks_per_sec))
- return TimeTicks(0); // Broken, we don't guarantee this function works.
- ticks_per_microsecond =
- ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond;
- }
+ DISALLOW_COPY_AND_ASSIGN(UnreliableHighResNowSingleton);
+};
+
+} // namespace
- LARGE_INTEGER now;
- QueryPerformanceCounter(&now);
- return TimeTicks(now.QuadPart / ticks_per_microsecond);
+// static
+TimeTicks TimeTicks::Now() {
+ return TimeTicks() + Singleton<NowSingleton>::get()->Now();
}
+// static
+TimeTicks TimeTicks::UnreliableHighResNow() {
+ UnreliableHighResNowSingleton* now =
+ Singleton<UnreliableHighResNowSingleton>::get();
+
+ if (now->IsBroken()) {
+ NOTREACHED() << "QueryPerformanceCounter is broken.";
+ return TimeTicks(0);
+ }
+
+ return TimeTicks() + now->Now();
+}