summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/event_recorder.cc10
-rw-r--r--base/message_loop.cc18
-rw-r--r--base/time.h5
-rw-r--r--base/time_unittest_win.cc11
-rw-r--r--base/time_win.cc148
5 files changed, 101 insertions, 91 deletions
diff --git a/base/event_recorder.cc b/base/event_recorder.cc
index 23c092f..ddc7617 100644
--- a/base/event_recorder.cc
+++ b/base/event_recorder.cc
@@ -53,9 +53,6 @@ bool EventRecorder::StartRecording(std::wstring& filename) {
return false;
}
- // Set the faster clock, if possible.
- ::timeBeginPeriod(1);
-
// Set the recording hook. JOURNALRECORD can only be used as a global hook.
journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
GetModuleHandle(NULL), 0);
@@ -79,8 +76,6 @@ void EventRecorder::StopRecording() {
return;
}
- ::timeEndPeriod(1);
-
DCHECK(file_ != NULL);
fclose(file_);
file_ = NULL;
@@ -109,9 +104,6 @@ bool EventRecorder::StartPlayback(std::wstring& filename) {
return false;
}
- // Set the faster clock, if possible.
- ::timeBeginPeriod(1);
-
// Playback time is tricky. When playing back, we read a series of events,
// each with timeouts. Simply subtracting the delta between two timers will
// lead to fast playback (about 2x speed). The API has two events, one
@@ -150,8 +142,6 @@ void EventRecorder::StopPlayback() {
fclose(file_);
file_ = NULL;
- ::timeEndPeriod(1);
-
journal_hook_ = NULL;
is_playing_ = false;
}
diff --git a/base/message_loop.cc b/base/message_loop.cc
index fffeb09..ee33aa1 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -68,19 +68,6 @@ MessageLoop::MessageLoop(Type type)
DCHECK(!tls_index_.Get()) << "should only have one message loop per thread";
tls_index_.Set(this);
- // TODO(darin): This does not seem like the best place for this code to live!
-#if defined(OS_WIN)
- // We've experimented with all sorts of timers, and initially tried
- // to avoid using timeBeginPeriod because it does affect the system
- // globally. However, after much investigation, it turns out that all
- // of the major plugins (flash, windows media 9-11, and quicktime)
- // already use timeBeginPeriod to increase the speed of the clock.
- // Since the browser must work with these plugins, the browser already
- // needs to support a fast clock. We may as well use this ourselves,
- // as it really is the best timer mechanism for our needs.
- timeBeginPeriod(1);
-#endif
-
// TODO(darin): Choose the pump based on the requested type.
#if defined(OS_WIN)
if (type_ == TYPE_DEFAULT) {
@@ -119,11 +106,6 @@ MessageLoop::~MessageLoop() {
delayed_work_queue_.pop();
delete task;
}
-
-#if defined(OS_WIN)
- // Match timeBeginPeriod() from construction.
- timeEndPeriod(1);
-#endif
}
void MessageLoop::AddDestructionObserver(DestructionObserver *obs) {
diff --git a/base/time.h b/base/time.h
index c25419f..cc66eda 100644
--- a/base/time.h
+++ b/base/time.h
@@ -436,9 +436,8 @@ class TimeTicks {
int64 ticks_;
#if defined(OS_WIN)
- // The function to use for counting ticks.
- typedef int (__stdcall *TickFunction)(void);
- static TickFunction tick_function_;
+ typedef DWORD (*TickFunctionType)(void);
+ static TickFunctionType SetMockTickFunction(TickFunctionType ticker);
#endif
};
diff --git a/base/time_unittest_win.cc b/base/time_unittest_win.cc
index 5b29db2..87cf698 100644
--- a/base/time_unittest_win.cc
+++ b/base/time_unittest_win.cc
@@ -12,27 +12,26 @@ namespace {
class MockTimeTicks : public TimeTicks {
public:
- static int Ticker() {
+ static DWORD Ticker() {
return static_cast<int>(InterlockedIncrement(&ticker_));
}
static void InstallTicker() {
- old_tick_function_ = tick_function_;
- tick_function_ = reinterpret_cast<TickFunction>(&Ticker);
+ old_tick_function_ = SetMockTickFunction(&Ticker);
ticker_ = -5;
}
static void UninstallTicker() {
- tick_function_ = old_tick_function_;
+ SetMockTickFunction(old_tick_function_);
}
private:
static volatile LONG ticker_;
- static TickFunction old_tick_function_;
+ static TickFunctionType old_tick_function_;
};
volatile LONG MockTimeTicks::ticker_;
-MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_;
+MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;
HANDLE g_rollover_test_start;
diff --git a/base/time_win.cc b/base/time_win.cc
index 9d38993..77c18ae 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,56 @@ void Time::Explode(bool is_local, Exploded* exploded) const {
}
// TimeTicks ------------------------------------------------------------------
+namespace {
-TimeTicks::TickFunction TimeTicks::tick_function_=
- reinterpret_cast<TickFunction>(&timeGetTime);
+// We define a wrapper to adapt between the __stdcall and __cdecl call of the
+// mock function, and to avoid a static constructor. Assigning an import to a
+// function pointer directly would require setup code to fetch from the IAT.
+DWORD timeGetTimeWrapper() {
+ return 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;
- }
+DWORD (*tick_function)(void) = &timeGetTimeWrapper;
+
+// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
+// because it returns the number of milliseconds 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_(0) {
+ // Request a resolution of 1ms from timeGetTime(). This can have some
+ // consequences on other applications (see MSDN), but we've found that it
+ // is very common in other applications (Flash, Media Player, etc), so it
+ // is not really a dangerous thing to do. We need this because the default
+ // resolution of ~15ms is much too low for accurate timing and timers.
+ ::timeBeginPeriod(1);
}
- // 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;
+ ~NowSingleton() {
+ ::timeEndPeriod(1);
}
- // GetTickCount returns milliseconds, we want microseconds.
- return TimeTicks((tick_count + rollover_count) *
- Time::kMicrosecondsPerMillisecond);
-}
+ 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 = tick_function();
+ if (now < last_seen_)
+ rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days.
+ last_seen_ = now;
+ return TimeDelta::FromMilliseconds(now) + rollover_;
+ }
+
+ private:
+ Lock lock_; // To protected last_seen_ and rollover_.
+ TimeDelta rollover_; // Accumulation of time lost due 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 +203,58 @@ 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);
+};
- LARGE_INTEGER now;
- QueryPerformanceCounter(&now);
- return TimeTicks(now.QuadPart / ticks_per_microsecond);
+} // namespace
+
+// static
+TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
+ TickFunctionType ticker) {
+ TickFunctionType old = tick_function;
+ tick_function = ticker;
+ return old;
}
+// 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();
+}