OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 // Windows Timer Primer | 6 // Windows Timer Primer |
7 // | 7 // |
8 // A good article: http://www.ddj.com/windows/184416651 | 8 // A good article: http://www.ddj.com/windows/184416651 |
9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 | 9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 |
10 // | 10 // |
(...skipping 19 matching lines...) Expand all Loading... | |
30 // To work around all this, we're going to generally use timeGetTime(). We | 30 // To work around all this, we're going to generally use timeGetTime(). We |
31 // will only increase the system-wide timer if we're not running on battery | 31 // will only increase the system-wide timer if we're not running on battery |
32 // power. | 32 // power. |
33 | 33 |
34 #include "base/time/time.h" | 34 #include "base/time/time.h" |
35 | 35 |
36 #include <windows.h> | 36 #include <windows.h> |
37 #include <mmsystem.h> | 37 #include <mmsystem.h> |
38 #include <stdint.h> | 38 #include <stdint.h> |
39 | 39 |
40 #include "base/atomicops.h" | |
40 #include "base/bit_cast.h" | 41 #include "base/bit_cast.h" |
41 #include "base/cpu.h" | 42 #include "base/cpu.h" |
42 #include "base/lazy_instance.h" | 43 #include "base/lazy_instance.h" |
43 #include "base/logging.h" | 44 #include "base/logging.h" |
44 #include "base/synchronization/lock.h" | 45 #include "base/synchronization/lock.h" |
45 #include "base/threading/platform_thread.h" | 46 #include "base/threading/platform_thread.h" |
46 | 47 |
47 using base::ThreadTicks; | 48 using base::ThreadTicks; |
48 using base::Time; | 49 using base::Time; |
49 using base::TimeDelta; | 50 using base::TimeDelta; |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
316 | 317 |
317 // We define a wrapper to adapt between the __stdcall and __cdecl call of the | 318 // We define a wrapper to adapt between the __stdcall and __cdecl call of the |
318 // mock function, and to avoid a static constructor. Assigning an import to a | 319 // mock function, and to avoid a static constructor. Assigning an import to a |
319 // function pointer directly would require setup code to fetch from the IAT. | 320 // function pointer directly would require setup code to fetch from the IAT. |
320 DWORD timeGetTimeWrapper() { | 321 DWORD timeGetTimeWrapper() { |
321 return timeGetTime(); | 322 return timeGetTime(); |
322 } | 323 } |
323 | 324 |
324 DWORD (*g_tick_function)(void) = &timeGetTimeWrapper; | 325 DWORD (*g_tick_function)(void) = &timeGetTimeWrapper; |
325 | 326 |
326 // Accumulation of time lost due to rollover (in milliseconds). | 327 // A structure holding the most significant bits of "last seen" and a |
327 int64_t g_rollover_ms = 0; | 328 // "rollover" counter. |
329 union LastTimeAndRolloversState { | |
330 // The state as a single 32-bit opaque value. | |
331 int32_t as_opaque_32; | |
328 | 332 |
329 // The last timeGetTime value we saw, to detect rollover. | 333 // The state as usable values. |
330 DWORD g_last_seen_now = 0; | 334 struct { |
331 | 335 // The top 8-bits of the "last" time. This is enough to check for rollovers |
332 // Lock protecting rollover_ms and last_seen_now. | 336 // and the small bit-size means fewer CompareAndSwap operations to store |
333 // Note: this is a global object, and we usually avoid these. However, the time | 337 // changes in state, which in turn makes for fewer retries. |
334 // code is low-level, and we don't want to use Singletons here (it would be too | 338 uint8_t last_8; |
335 // easy to use a Singleton without even knowing it, and that may lead to many | 339 // A count of the number of detected rollovers. Using this as the upper |
brucedawson
2016/10/07 21:00:05
How can a 16-bit value be the upper half of a 64-b
bcwhite
2016/10/11 15:27:52
Done.
| |
336 // gotchas). Its impact on startup time should be negligible due to low-level | 340 // half of a 64-bit value results in a 48-bit tick counter. This extends |
337 // nature of time code. | 341 // the total rollover period from about 49 days to about 8800 years. |
338 base::Lock g_rollover_lock; | 342 uint16_t rollovers; |
343 } as_values; | |
344 }; | |
345 base::subtle::Atomic32 g_last_time_and_rollovers = 0; | |
346 static_assert( | |
347 sizeof(LastTimeAndRolloversState) <= sizeof(g_last_time_and_rollovers), | |
348 "LastTimeAndRolloversState does not fit in a single atomic word"); | |
339 | 349 |
340 // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic | 350 // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic |
341 // because it returns the number of milliseconds since Windows has started, | 351 // because it returns the number of milliseconds since Windows has started, |
342 // which will roll over the 32-bit value every ~49 days. We try to track | 352 // which will roll over the 32-bit value every ~49 days. We try to track |
343 // rollover ourselves, which works if TimeTicks::Now() is called at least every | 353 // rollover ourselves, which works if TimeTicks::Now() is called at least every |
344 // 49 days. | 354 // 48 days. |
brucedawson
2016/10/07 21:00:04
Why the change to 48 days? Everywhere else it rema
bcwhite
2016/10/11 15:27:52
It's actually 48.8 days. Because only the top 8 b
| |
345 TimeDelta RolloverProtectedNow() { | 355 TimeDelta RolloverProtectedNow() { |
346 base::AutoLock locked(g_rollover_lock); | 356 LastTimeAndRolloversState state; |
347 // We should hold the lock while calling tick_function to make sure that | 357 DWORD now; // DWORD is always unsigned 32 bits. |
348 // we keep last_seen_now stay correctly in sync. | 358 |
349 DWORD now = g_tick_function(); | 359 while (true) { |
350 if (now < g_last_seen_now) | 360 // Fetch the "now" and "last" tick values, updating "last" with "now" and |
351 g_rollover_ms += 0x100000000I64; // ~49.7 days. | 361 // incrementing the "rollovers" counter if the tick-value has wrapped back |
352 g_last_seen_now = now; | 362 // around. Atomic operations ensure that both "last" and "rollovers" are |
353 return TimeDelta::FromMilliseconds(now + g_rollover_ms); | 363 // always updated together. |
364 int32_t original = base::subtle::Acquire_Load(&g_last_time_and_rollovers); | |
365 state.as_opaque_32 = original; | |
366 now = g_tick_function(); | |
367 uint8_t now_8 = static_cast<uint8_t>(now >> 24); | |
368 if (now_8 < state.as_values.last_8) | |
369 ++state.as_values.rollovers; | |
370 state.as_values.last_8 = now_8; | |
371 | |
372 // If the state hasn't changed, exit the loop. | |
373 if (state.as_opaque_32 == original) | |
374 break; | |
375 | |
376 // Save the changed state. If the existing value is unchanged from the | |
377 // original, exit the loop. | |
378 int32_t check = base::subtle::Release_CompareAndSwap( | |
379 &g_last_time_and_rollovers, original, state.as_opaque_32); | |
380 if (check == original) | |
381 break; | |
382 | |
383 // Another thread has done something in between so retry from the top. | |
384 } | |
385 | |
386 return TimeDelta::FromMilliseconds( | |
387 now + (static_cast<uint64_t>(state.as_values.rollovers) << 32)); | |
354 } | 388 } |
355 | 389 |
356 // Discussion of tick counter options on Windows: | 390 // Discussion of tick counter options on Windows: |
357 // | 391 // |
358 // (1) CPU cycle counter. (Retrieved via RDTSC) | 392 // (1) CPU cycle counter. (Retrieved via RDTSC) |
359 // The CPU counter provides the highest resolution time stamp and is the least | 393 // The CPU counter provides the highest resolution time stamp and is the least |
360 // expensive to retrieve. However, on older CPUs, two issues can affect its | 394 // expensive to retrieve. However, on older CPUs, two issues can affect its |
361 // reliability: First it is maintained per processor and not synchronized | 395 // reliability: First it is maintained per processor and not synchronized |
362 // between processors. Also, the counters will change frequency due to thermal | 396 // between processors. Also, the counters will change frequency due to thermal |
363 // and power changes, and stop in some states. | 397 // and power changes, and stop in some states. |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
476 TimeDelta InitialNowFunction() { | 510 TimeDelta InitialNowFunction() { |
477 InitializeNowFunctionPointer(); | 511 InitializeNowFunctionPointer(); |
478 return g_now_function(); | 512 return g_now_function(); |
479 } | 513 } |
480 | 514 |
481 } // namespace | 515 } // namespace |
482 | 516 |
483 // static | 517 // static |
484 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( | 518 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( |
485 TickFunctionType ticker) { | 519 TickFunctionType ticker) { |
486 base::AutoLock locked(g_rollover_lock); | |
487 TickFunctionType old = g_tick_function; | 520 TickFunctionType old = g_tick_function; |
488 g_tick_function = ticker; | 521 g_tick_function = ticker; |
489 g_rollover_ms = 0; | 522 base::subtle::NoBarrier_Store(&g_last_time_and_rollovers, 0); |
490 g_last_seen_now = 0; | |
491 return old; | 523 return old; |
492 } | 524 } |
493 | 525 |
494 // static | 526 // static |
495 TimeTicks TimeTicks::Now() { | 527 TimeTicks TimeTicks::Now() { |
496 return TimeTicks() + g_now_function(); | 528 return TimeTicks() + g_now_function(); |
497 } | 529 } |
498 | 530 |
499 // static | 531 // static |
500 bool TimeTicks::IsHighResolution() { | 532 bool TimeTicks::IsHighResolution() { |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
630 TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { | 662 TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { |
631 return TimeTicks() + QPCValueToTimeDelta(qpc_value); | 663 return TimeTicks() + QPCValueToTimeDelta(qpc_value); |
632 } | 664 } |
633 | 665 |
634 // TimeDelta ------------------------------------------------------------------ | 666 // TimeDelta ------------------------------------------------------------------ |
635 | 667 |
636 // static | 668 // static |
637 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { | 669 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { |
638 return QPCValueToTimeDelta(qpc_value); | 670 return QPCValueToTimeDelta(qpc_value); |
639 } | 671 } |
OLD | NEW |