tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

time_win.cc (9100B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 // Windows Timer Primer
      8 //
      9 // A good article:  http://www.ddj.com/windows/184416651
     10 // A good mozilla bug:  http://bugzilla.mozilla.org/show_bug.cgi?id=363258
     11 //
     12 // The default windows timer, GetSystemTimeAsFileTime is not very precise.
     13 // It is only good to ~15.5ms.
     14 //
     15 // QueryPerformanceCounter is the logical choice for a high-precision timer.
     16 // However, it is known to be buggy on some hardware.  Specifically, it can
     17 // sometimes "jump".  On laptops, QPC can also be very expensive to call.
     18 // It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower
     19 // on laptops.  A unittest exists which will show the relative cost of various
     20 // timers on any system.
     21 //
     22 // The next logical choice is timeGetTime().  timeGetTime has a precision of
     23 // 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
     24 // applications on the system.  By default, precision is only 15.5ms.
     25 // Unfortunately, we don't want to call timeBeginPeriod because we don't
     26 // want to affect other applications.  Further, on mobile platforms, use of
     27 // faster multimedia timers can hurt battery life.  See the intel
     28 // article about this here:
     29 // http://softwarecommunity.intel.com/articles/eng/1086.htm
     30 //
     31 // To work around all this, we're going to generally use timeGetTime().  We
     32 // will only increase the system-wide timer if we're not running on battery
     33 // power.  Using timeBeginPeriod(1) is a requirement in order to make our
     34 // message loop waits have the same resolution that our time measurements
     35 // do.  Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
     36 // there is nothing else to waken the Wait.
     37 
     38 #include "base/time.h"
     39 
     40 #ifndef __MINGW32__
     41 #  pragma comment(lib, "winmm.lib")
     42 #endif
     43 #include <windows.h>
     44 #include <mmsystem.h>
     45 
     46 #include "base/basictypes.h"
     47 #include "base/logging.h"
     48 #include "mozilla/Casting.h"
     49 #include "mozilla/StaticMutex.h"
     50 
     51 using base::Time;
     52 using base::TimeDelta;
     53 using base::TimeTicks;
     54 using mozilla::BitwiseCast;
     55 
     56 namespace {
     57 
     58 // From MSDN, FILETIME "Contains a 64-bit value representing the number of
     59 // 100-nanosecond intervals since January 1, 1601 (UTC)."
     60 int64_t FileTimeToMicroseconds(const FILETIME& ft) {
     61  // Need to BitwiseCast to fix alignment, then divide by 10 to convert
     62  // 100-nanoseconds to milliseconds. This only works on little-endian
     63  // machines.
     64  return BitwiseCast<int64_t>(ft) / 10;
     65 }
     66 
     67 void MicrosecondsToFileTime(int64_t us, FILETIME* ft) {
     68  DCHECK(us >= 0) << "Time is less than 0, negative values are not "
     69                     "representable in FILETIME";
     70 
     71  // Multiply by 10 to convert milliseconds to 100-nanoseconds. BitwiseCast will
     72  // handle alignment problems. This only works on little-endian machines.
     73  *ft = BitwiseCast<FILETIME>(us * 10);
     74 }
     75 
     76 int64_t CurrentWallclockMicroseconds() {
     77  FILETIME ft;
     78  ::GetSystemTimeAsFileTime(&ft);
     79  return FileTimeToMicroseconds(ft);
     80 }
     81 
     82 // Time between resampling the un-granular clock for this API.  60 seconds.
     83 const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond;
     84 
     85 int64_t initial_time = 0;
     86 TimeTicks initial_ticks;
     87 
     88 void InitializeClock() {
     89  initial_ticks = TimeTicks::Now();
     90  initial_time = CurrentWallclockMicroseconds();
     91 }
     92 
     93 }  // namespace
     94 
     95 // Time -----------------------------------------------------------------------
     96 
     97 // The internal representation of Time uses FILETIME, whose epoch is 1601-01-01
     98 // 00:00:00 UTC.  ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the
     99 // number of leap year days between 1601 and 1970: (1970-1601)/4 excluding
    100 // 1700, 1800, and 1900.
    101 // static
    102 const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000);
    103 
    104 // static
    105 Time Time::Now() {
    106  if (initial_time == 0) InitializeClock();
    107 
    108  // We implement time using the high-resolution timers so that we can get
    109  // timeouts which are smaller than 10-15ms.  If we just used
    110  // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
    111  //
    112  // To make this work, we initialize the clock (initial_time) and the
    113  // counter (initial_ctr).  To compute the initial time, we can check
    114  // the number of ticks that have elapsed, and compute the delta.
    115  //
    116  // To avoid any drift, we periodically resync the counters to the system
    117  // clock.
    118  while (true) {
    119    TimeTicks ticks = TimeTicks::Now();
    120 
    121    // Calculate the time elapsed since we started our timer
    122    TimeDelta elapsed = ticks - initial_ticks;
    123 
    124    // Check if enough time has elapsed that we need to resync the clock.
    125    if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) {
    126      InitializeClock();
    127      continue;
    128    }
    129 
    130    return Time(elapsed + Time(initial_time));
    131  }
    132 }
    133 
    134 // static
    135 Time Time::NowFromSystemTime() {
    136  // Force resync.
    137  InitializeClock();
    138  return Time(initial_time);
    139 }
    140 
    141 // static
    142 Time Time::FromExploded(bool is_local, const Exploded& exploded) {
    143  // Create the system struct representing our exploded time. It will either be
    144  // in local time or UTC.
    145  SYSTEMTIME st;
    146  st.wYear = exploded.year;
    147  st.wMonth = exploded.month;
    148  st.wDayOfWeek = exploded.day_of_week;
    149  st.wDay = exploded.day_of_month;
    150  st.wHour = exploded.hour;
    151  st.wMinute = exploded.minute;
    152  st.wSecond = exploded.second;
    153  st.wMilliseconds = exploded.millisecond;
    154 
    155  // Convert to FILETIME.
    156  FILETIME ft;
    157  if (!SystemTimeToFileTime(&st, &ft)) {
    158    NOTREACHED() << "Unable to convert time";
    159    return Time(0);
    160  }
    161 
    162  // Ensure that it's in UTC.
    163  if (is_local) {
    164    FILETIME utc_ft;
    165    LocalFileTimeToFileTime(&ft, &utc_ft);
    166    return Time(FileTimeToMicroseconds(utc_ft));
    167  }
    168  return Time(FileTimeToMicroseconds(ft));
    169 }
    170 
    171 void Time::Explode(bool is_local, Exploded* exploded) const {
    172  // FILETIME in UTC.
    173  FILETIME utc_ft;
    174  MicrosecondsToFileTime(us_, &utc_ft);
    175 
    176  // FILETIME in local time if necessary.
    177  BOOL success = TRUE;
    178  FILETIME ft;
    179  if (is_local)
    180    success = FileTimeToLocalFileTime(&utc_ft, &ft);
    181  else
    182    ft = utc_ft;
    183 
    184  // FILETIME in SYSTEMTIME (exploded).
    185  SYSTEMTIME st;
    186  if (!success || !FileTimeToSystemTime(&ft, &st)) {
    187    NOTREACHED() << "Unable to convert time, don't know why";
    188    ZeroMemory(exploded, sizeof(*exploded));
    189    return;
    190  }
    191 
    192  exploded->year = st.wYear;
    193  exploded->month = st.wMonth;
    194  exploded->day_of_week = st.wDayOfWeek;
    195  exploded->day_of_month = st.wDay;
    196  exploded->hour = st.wHour;
    197  exploded->minute = st.wMinute;
    198  exploded->second = st.wSecond;
    199  exploded->millisecond = st.wMilliseconds;
    200 }
    201 
    202 // TimeTicks ------------------------------------------------------------------
    203 namespace {
    204 
    205 // We define a wrapper to adapt between the __stdcall and __cdecl call of the
    206 // mock function, and to avoid a static constructor.  Assigning an import to a
    207 // function pointer directly would require setup code to fetch from the IAT.
    208 DWORD timeGetTimeWrapper() { return timeGetTime(); }
    209 
    210 DWORD (*tick_function)(void) = &timeGetTimeWrapper;
    211 
    212 // This setup is a little gross: the `now` instance lives until libxul is
    213 // unloaded, but leak checking runs prior to that, and would see a Mutex
    214 // instance contained in NowSingleton as still live.  Said instance would
    215 // be reported as a leak...but it's not, really.  To avoid that, we need
    216 // to use StaticMutex (which is not leak-checked), but StaticMutex can't
    217 // be a member variable.  So we have to have this separate static variable.
    218 static mozilla::StaticMutex sNowSingletonLock;
    219 
    220 // We use timeGetTime() to implement TimeTicks::Now().  This can be problematic
    221 // because it returns the number of milliseconds since Windows has started,
    222 // which will roll over the 32-bit value every ~49 days.  We try to track
    223 // rollover ourselves, which works if TimeTicks::Now() is called at least every
    224 // 49 days.
    225 class NowSingleton {
    226 public:
    227  TimeDelta Now() {
    228    mozilla::StaticMutexAutoLock locked(sNowSingletonLock);
    229    // We should hold the lock while calling tick_function to make sure that
    230    // we keep our last_seen_ stay correctly in sync.
    231    DWORD now = tick_function();
    232    if (now < last_seen_)
    233      rollover_ +=
    234          TimeDelta::FromMilliseconds(GG_LONGLONG(0x100000000));  // ~49.7 days.
    235    last_seen_ = now;
    236    return TimeDelta::FromMilliseconds(now) + rollover_;
    237  }
    238 
    239  static NowSingleton& instance() {
    240    static NowSingleton now;
    241    return now;
    242  }
    243 
    244 private:
    245  explicit NowSingleton()
    246      : rollover_(TimeDelta::FromMilliseconds(0)), last_seen_(0) {}
    247  ~NowSingleton() = default;
    248 
    249  TimeDelta rollover_ MOZ_GUARDED_BY(
    250      sNowSingletonLock);  // Accumulation of time lost due to rollover.
    251  DWORD last_seen_
    252      MOZ_GUARDED_BY(sNowSingletonLock);  // The last timeGetTime value we saw,
    253                                          // to detect rollover.
    254 
    255  DISALLOW_COPY_AND_ASSIGN(NowSingleton);
    256 };
    257 
    258 }  // namespace
    259 
    260 // static
    261 TimeTicks TimeTicks::Now() {
    262  return TimeTicks() + NowSingleton::instance().Now();
    263 }