| Index: base/time/time_win.cc
|
| diff --git a/base/time/time_win.cc b/base/time/time_win.cc
|
| index ba6fc1da3bf707aef82567868e425111e16ea083..631eb3a292bbce00286ac8682242dde5be3eb42d 100644
|
| --- a/base/time/time_win.cc
|
| +++ b/base/time/time_win.cc
|
| @@ -37,6 +37,7 @@
|
| #include <mmsystem.h>
|
| #include <stdint.h>
|
|
|
| +#include "base/atomicops.h"
|
| #include "base/bit_cast.h"
|
| #include "base/cpu.h"
|
| #include "base/lazy_instance.h"
|
| @@ -323,34 +324,69 @@ DWORD timeGetTimeWrapper() {
|
|
|
| DWORD (*g_tick_function)(void) = &timeGetTimeWrapper;
|
|
|
| -// Accumulation of time lost due to rollover (in milliseconds).
|
| -int64_t g_rollover_ms = 0;
|
| -
|
| -// The last timeGetTime value we saw, to detect rollover.
|
| -DWORD g_last_seen_now = 0;
|
| -
|
| -// Lock protecting rollover_ms and last_seen_now.
|
| -// Note: this is a global object, and we usually avoid these. However, the time
|
| -// code is low-level, and we don't want to use Singletons here (it would be too
|
| -// easy to use a Singleton without even knowing it, and that may lead to many
|
| -// gotchas). Its impact on startup time should be negligible due to low-level
|
| -// nature of time code.
|
| -base::Lock g_rollover_lock;
|
| +// A structure holding the most significant bits of "last seen" and a
|
| +// "rollover" counter.
|
| +union LastTimeAndRolloversState {
|
| + // The state as a single 32-bit opaque value.
|
| + base::subtle::Atomic32 as_opaque_32;
|
| +
|
| + // The state as usable values.
|
| + struct {
|
| + // The top 8-bits of the "last" time. This is enough to check for rollovers
|
| + // and the small bit-size means fewer CompareAndSwap operations to store
|
| + // changes in state, which in turn makes for fewer retries.
|
| + uint8_t last_8;
|
| + // A count of the number of detected rollovers. Using this as bits 47-32
|
| + // of the upper half of a 64-bit value results in a 48-bit tick counter.
|
| + // This extends the total rollover period from about 49 days to about 8800
|
| + // years while still allowing it to be stored with last_8 in a single
|
| + // 32-bit value.
|
| + uint16_t rollovers;
|
| + } as_values;
|
| +};
|
| +base::subtle::Atomic32 g_last_time_and_rollovers = 0;
|
| +static_assert(
|
| + sizeof(LastTimeAndRolloversState) <= sizeof(g_last_time_and_rollovers),
|
| + "LastTimeAndRolloversState does not fit in a single atomic word");
|
|
|
| // 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.
|
| +// 48.8 days (not 49 days because only changes in the top 8 bits get noticed).
|
| TimeDelta RolloverProtectedNow() {
|
| - base::AutoLock locked(g_rollover_lock);
|
| - // We should hold the lock while calling tick_function to make sure that
|
| - // we keep last_seen_now stay correctly in sync.
|
| - DWORD now = g_tick_function();
|
| - if (now < g_last_seen_now)
|
| - g_rollover_ms += 0x100000000I64; // ~49.7 days.
|
| - g_last_seen_now = now;
|
| - return TimeDelta::FromMilliseconds(now + g_rollover_ms);
|
| + LastTimeAndRolloversState state;
|
| + DWORD now; // DWORD is always unsigned 32 bits.
|
| +
|
| + while (true) {
|
| + // Fetch the "now" and "last" tick values, updating "last" with "now" and
|
| + // incrementing the "rollovers" counter if the tick-value has wrapped back
|
| + // around. Atomic operations ensure that both "last" and "rollovers" are
|
| + // always updated together.
|
| + int32_t original = base::subtle::Acquire_Load(&g_last_time_and_rollovers);
|
| + state.as_opaque_32 = original;
|
| + now = g_tick_function();
|
| + uint8_t now_8 = static_cast<uint8_t>(now >> 24);
|
| + if (now_8 < state.as_values.last_8)
|
| + ++state.as_values.rollovers;
|
| + state.as_values.last_8 = now_8;
|
| +
|
| + // If the state hasn't changed, exit the loop.
|
| + if (state.as_opaque_32 == original)
|
| + break;
|
| +
|
| + // Save the changed state. If the existing value is unchanged from the
|
| + // original, exit the loop.
|
| + int32_t check = base::subtle::Release_CompareAndSwap(
|
| + &g_last_time_and_rollovers, original, state.as_opaque_32);
|
| + if (check == original)
|
| + break;
|
| +
|
| + // Another thread has done something in between so retry from the top.
|
| + }
|
| +
|
| + return TimeDelta::FromMilliseconds(
|
| + now + (static_cast<uint64_t>(state.as_values.rollovers) << 32));
|
| }
|
|
|
| // Discussion of tick counter options on Windows:
|
| @@ -483,11 +519,9 @@ TimeDelta InitialNowFunction() {
|
| // static
|
| TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
|
| TickFunctionType ticker) {
|
| - base::AutoLock locked(g_rollover_lock);
|
| TickFunctionType old = g_tick_function;
|
| g_tick_function = ticker;
|
| - g_rollover_ms = 0;
|
| - g_last_seen_now = 0;
|
| + base::subtle::NoBarrier_Store(&g_last_time_and_rollovers, 0);
|
| return old;
|
| }
|
|
|
|
|