Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: base/time/time_win.cc

Issue 2393953003: Go lock-free for RolloverProtectedNow() (Closed)
Patch Set: change opaque field into atomic type Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 base::subtle::Atomic32 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 bits 47-32
336 // gotchas). Its impact on startup time should be negligible due to low-level 340 // of the upper half of a 64-bit value results in a 48-bit tick counter.
337 // nature of time code. 341 // This extends the total rollover period from about 49 days to about 8800
338 base::Lock g_rollover_lock; 342 // years while still allowing it to be stored with last_8 in a single
343 // 32-bit value.
344 uint16_t rollovers;
345 } as_values;
346 };
347 base::subtle::Atomic32 g_last_time_and_rollovers = 0;
348 static_assert(
349 sizeof(LastTimeAndRolloversState) <= sizeof(g_last_time_and_rollovers),
350 "LastTimeAndRolloversState does not fit in a single atomic word");
339 351
340 // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic 352 // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
341 // because it returns the number of milliseconds since Windows has started, 353 // 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 354 // 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 355 // rollover ourselves, which works if TimeTicks::Now() is called at least every
344 // 49 days. 356 // 48.8 days (not 49 days because only changes in the top 8 bits get noticed).
345 TimeDelta RolloverProtectedNow() { 357 TimeDelta RolloverProtectedNow() {
346 base::AutoLock locked(g_rollover_lock); 358 LastTimeAndRolloversState state;
347 // We should hold the lock while calling tick_function to make sure that 359 DWORD now; // DWORD is always unsigned 32 bits.
348 // we keep last_seen_now stay correctly in sync. 360
349 DWORD now = g_tick_function(); 361 while (true) {
350 if (now < g_last_seen_now) 362 // Fetch the "now" and "last" tick values, updating "last" with "now" and
351 g_rollover_ms += 0x100000000I64; // ~49.7 days. 363 // incrementing the "rollovers" counter if the tick-value has wrapped back
352 g_last_seen_now = now; 364 // around. Atomic operations ensure that both "last" and "rollovers" are
353 return TimeDelta::FromMilliseconds(now + g_rollover_ms); 365 // always updated together.
366 int32_t original = base::subtle::Acquire_Load(&g_last_time_and_rollovers);
367 state.as_opaque_32 = original;
368 now = g_tick_function();
369 uint8_t now_8 = static_cast<uint8_t>(now >> 24);
370 if (now_8 < state.as_values.last_8)
371 ++state.as_values.rollovers;
372 state.as_values.last_8 = now_8;
373
374 // If the state hasn't changed, exit the loop.
375 if (state.as_opaque_32 == original)
376 break;
377
378 // Save the changed state. If the existing value is unchanged from the
379 // original, exit the loop.
380 int32_t check = base::subtle::Release_CompareAndSwap(
381 &g_last_time_and_rollovers, original, state.as_opaque_32);
382 if (check == original)
383 break;
384
385 // Another thread has done something in between so retry from the top.
386 }
387
388 return TimeDelta::FromMilliseconds(
389 now + (static_cast<uint64_t>(state.as_values.rollovers) << 32));
354 } 390 }
355 391
356 // Discussion of tick counter options on Windows: 392 // Discussion of tick counter options on Windows:
357 // 393 //
358 // (1) CPU cycle counter. (Retrieved via RDTSC) 394 // (1) CPU cycle counter. (Retrieved via RDTSC)
359 // The CPU counter provides the highest resolution time stamp and is the least 395 // 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 396 // expensive to retrieve. However, on older CPUs, two issues can affect its
361 // reliability: First it is maintained per processor and not synchronized 397 // reliability: First it is maintained per processor and not synchronized
362 // between processors. Also, the counters will change frequency due to thermal 398 // between processors. Also, the counters will change frequency due to thermal
363 // and power changes, and stop in some states. 399 // and power changes, and stop in some states.
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 TimeDelta InitialNowFunction() { 512 TimeDelta InitialNowFunction() {
477 InitializeNowFunctionPointer(); 513 InitializeNowFunctionPointer();
478 return g_now_function(); 514 return g_now_function();
479 } 515 }
480 516
481 } // namespace 517 } // namespace
482 518
483 // static 519 // static
484 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( 520 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
485 TickFunctionType ticker) { 521 TickFunctionType ticker) {
486 base::AutoLock locked(g_rollover_lock);
487 TickFunctionType old = g_tick_function; 522 TickFunctionType old = g_tick_function;
488 g_tick_function = ticker; 523 g_tick_function = ticker;
489 g_rollover_ms = 0; 524 base::subtle::NoBarrier_Store(&g_last_time_and_rollovers, 0);
490 g_last_seen_now = 0;
491 return old; 525 return old;
492 } 526 }
493 527
494 // static 528 // static
495 TimeTicks TimeTicks::Now() { 529 TimeTicks TimeTicks::Now() {
496 return TimeTicks() + g_now_function(); 530 return TimeTicks() + g_now_function();
497 } 531 }
498 532
499 // static 533 // static
500 bool TimeTicks::IsHighResolution() { 534 bool TimeTicks::IsHighResolution() {
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
630 TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { 664 TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) {
631 return TimeTicks() + QPCValueToTimeDelta(qpc_value); 665 return TimeTicks() + QPCValueToTimeDelta(qpc_value);
632 } 666 }
633 667
634 // TimeDelta ------------------------------------------------------------------ 668 // TimeDelta ------------------------------------------------------------------
635 669
636 // static 670 // static
637 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { 671 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
638 return QPCValueToTimeDelta(qpc_value); 672 return QPCValueToTimeDelta(qpc_value);
639 } 673 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698