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 }