Bug 1699062 - Flatten toolkit/themes/*/global/alerts/. r=desktop-theme-reviewers,dao
[gecko.git] / mozglue / misc / TimeStamp_windows.cpp
blobe4739ced29c74ffc1083a0b68486361fdddc5c04
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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with
8 // values of GetTickCount64().
10 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
11 #include "mozilla/MathAlgorithms.h"
12 #include "mozilla/TimeStamp.h"
13 #include "mozilla/Uptime.h"
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <intrin.h>
18 #include <windows.h>
20 // To enable logging define to your favorite logging API
21 #define LOG(x)
23 class AutoCriticalSection {
24 public:
25 explicit AutoCriticalSection(LPCRITICAL_SECTION aSection)
26 : mSection(aSection) {
27 ::EnterCriticalSection(mSection);
29 ~AutoCriticalSection() { ::LeaveCriticalSection(mSection); }
31 private:
32 LPCRITICAL_SECTION mSection;
35 // Estimate of the smallest duration of time we can measure.
36 static volatile ULONGLONG sResolution;
37 static volatile ULONGLONG sResolutionSigDigs;
38 static const double kNsPerSecd = 1000000000.0;
39 static const LONGLONG kNsPerMillisec = 1000000;
41 // ----------------------------------------------------------------------------
42 // Global constants
43 // ----------------------------------------------------------------------------
45 // Tolerance to failures settings.
47 // What is the interval we want to have failure free.
48 // in [ms]
49 static const uint32_t kFailureFreeInterval = 5000;
50 // How many failures we are willing to tolerate in the interval.
51 static const uint32_t kMaxFailuresPerInterval = 4;
52 // What is the threshold to treat fluctuations as actual failures.
53 // in [ms]
54 static const uint32_t kFailureThreshold = 50;
56 // If we are not able to get the value of GTC time increment, use this value
57 // which is the most usual increment.
58 static const DWORD kDefaultTimeIncrement = 156001;
60 // ----------------------------------------------------------------------------
61 // Global variables, not changing at runtime
62 // ----------------------------------------------------------------------------
64 // Result of QueryPerformanceFrequency
65 // We use default of 1 for the case we can't use QueryPerformanceCounter
66 // to make mt/ms conversions work despite that.
67 static uint64_t sFrequencyPerSec = 1;
69 namespace mozilla {
71 MFBT_API uint64_t GetQueryPerformanceFrequencyPerSec() {
72 return sFrequencyPerSec;
75 } // namespace mozilla
77 // How much we are tolerant to GTC occasional loose of resoltion.
78 // This number says how many multiples of the minimal GTC resolution
79 // detected on the system are acceptable. This number is empirical.
80 static const LONGLONG kGTCTickLeapTolerance = 4;
82 // Base tolerance (more: "inability of detection" range) threshold is calculated
83 // dynamically, and kept in sGTCResolutionThreshold.
85 // Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) -
86 // (QPC_now - QPC_epoch)) was in [-sGTCResolutionThreshold,
87 // sGTCResolutionThreshold] interval every time we'd compared two time stamps.
88 // If not, then we check the overflow behind this basic threshold
89 // is in kFailureThreshold. If not, we condider it as a QPC failure. If too
90 // many failures in short time are detected, QPC is considered faulty and
91 // disabled.
93 // Kept in [mt]
94 static LONGLONG sGTCResolutionThreshold;
96 // If QPC is found faulty for two stamps in this interval, we engage
97 // the fault detection algorithm. For duration larger then this limit
98 // we bypass using durations calculated from QPC when jitter is detected,
99 // but don't touch the sUseQPC flag.
101 // Value is in [ms].
102 static const uint32_t kHardFailureLimit = 2000;
103 // Conversion to [mt]
104 static LONGLONG sHardFailureLimit;
106 // Conversion of kFailureFreeInterval and kFailureThreshold to [mt]
107 static LONGLONG sFailureFreeInterval;
108 static LONGLONG sFailureThreshold;
110 // ----------------------------------------------------------------------------
111 // Systemm status flags
112 // ----------------------------------------------------------------------------
114 // Flag for stable TSC that indicates platform where QPC is stable.
115 static bool sHasStableTSC = false;
117 // ----------------------------------------------------------------------------
118 // Global state variables, changing at runtime
119 // ----------------------------------------------------------------------------
121 // Initially true, set to false when QPC is found unstable and never
122 // returns back to true since that time.
123 static bool volatile sUseQPC = true;
125 // ----------------------------------------------------------------------------
126 // Global lock
127 // ----------------------------------------------------------------------------
129 // Thread spin count before entering the full wait state for sTimeStampLock.
130 // Inspired by Rob Arnold's work on PRMJ_Now().
131 static const DWORD kLockSpinCount = 4096;
133 // Common mutex (thanks the relative complexity of the logic, this is better
134 // then using CMPXCHG8B.)
135 // It is protecting the globals bellow.
136 static CRITICAL_SECTION sTimeStampLock;
138 // ----------------------------------------------------------------------------
139 // Global lock protected variables
140 // ----------------------------------------------------------------------------
142 // Timestamp in future until QPC must behave correctly.
143 // Set to now + kFailureFreeInterval on first QPC failure detection.
144 // Set to now + E * kFailureFreeInterval on following errors,
145 // where E is number of errors detected during last kFailureFreeInterval
146 // milliseconds, calculated simply as:
147 // E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1.
148 // When E > kMaxFailuresPerInterval -> disable QPC.
150 // Kept in [mt]
151 static ULONGLONG sFaultIntoleranceCheckpoint = 0;
153 namespace mozilla {
155 // Result is in [mt]
156 static inline ULONGLONG PerformanceCounter() {
157 LARGE_INTEGER pc;
158 ::QueryPerformanceCounter(&pc);
160 // QueryPerformanceCounter may slightly jitter (not be 100% monotonic.)
161 // This is a simple go-backward protection for such a faulty hardware.
162 AutoCriticalSection lock(&sTimeStampLock);
164 static decltype(LARGE_INTEGER::QuadPart) last;
165 if (last > pc.QuadPart) {
166 return last * 1000ULL;
168 last = pc.QuadPart;
169 return pc.QuadPart * 1000ULL;
172 static void InitThresholds() {
173 DWORD timeAdjustment = 0, timeIncrement = 0;
174 BOOL timeAdjustmentDisabled;
175 GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
176 &timeAdjustmentDisabled);
178 LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement));
180 if (!timeIncrement) {
181 timeIncrement = kDefaultTimeIncrement;
184 // Ceiling to a millisecond
185 // Example values: 156001, 210000
186 DWORD timeIncrementCeil = timeIncrement;
187 // Don't want to round up if already rounded, values will be: 156000, 209999
188 timeIncrementCeil -= 1;
189 // Convert to ms, values will be: 15, 20
190 timeIncrementCeil /= 10000;
191 // Round up, values will be: 16, 21
192 timeIncrementCeil += 1;
193 // Convert back to 100ns, values will be: 160000, 210000
194 timeIncrementCeil *= 10000;
196 // How many milli-ticks has the interval rounded up
197 LONGLONG ticksPerGetTickCountResolutionCeiling =
198 (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL;
200 // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value.
201 sGTCResolutionThreshold =
202 LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling);
204 sHardFailureLimit = ms2mt(kHardFailureLimit);
205 sFailureFreeInterval = ms2mt(kFailureFreeInterval);
206 sFailureThreshold = ms2mt(kFailureThreshold);
209 static void InitResolution() {
210 // 10 total trials is arbitrary: what we're trying to avoid by
211 // looping is getting unlucky and being interrupted by a context
212 // switch or signal, or being bitten by paging/cache effects
214 ULONGLONG minres = ~0ULL;
215 if (sUseQPC) {
216 int loops = 10;
217 do {
218 ULONGLONG start = PerformanceCounter();
219 ULONGLONG end = PerformanceCounter();
221 ULONGLONG candidate = (end - start);
222 if (candidate < minres) {
223 minres = candidate;
225 } while (--loops && minres);
227 if (0 == minres) {
228 minres = 1;
230 } else {
231 // GetTickCount has only ~16ms known resolution
232 minres = ms2mt(16);
235 // Converting minres that is in [mt] to nanosecods, multiplicating
236 // the argument to preserve resolution.
237 ULONGLONG result = mt2ms(minres * kNsPerMillisec);
238 if (0 == result) {
239 result = 1;
242 sResolution = result;
244 // find the number of significant digits in mResolution, for the
245 // sake of ToSecondsSigDigits()
246 ULONGLONG sigDigs;
247 for (sigDigs = 1; !(sigDigs == result || 10 * sigDigs > result);
248 sigDigs *= 10)
251 sResolutionSigDigs = sigDigs;
254 // ----------------------------------------------------------------------------
255 // TimeStampValue implementation
256 // ----------------------------------------------------------------------------
257 MFBT_API
258 TimeStampValue::TimeStampValue(ULONGLONG aGTC, ULONGLONG aQPC, bool aHasQPC,
259 bool aUsedCanonicalNow)
260 : mGTC(aGTC),
261 mQPC(aQPC),
262 mUsedCanonicalNow(aUsedCanonicalNow),
263 mHasQPC(aHasQPC) {
264 mIsNull = aGTC == 0 && aQPC == 0;
267 MFBT_API TimeStampValue& TimeStampValue::operator+=(const int64_t aOther) {
268 mGTC += aOther;
269 mQPC += aOther;
270 return *this;
273 MFBT_API TimeStampValue& TimeStampValue::operator-=(const int64_t aOther) {
274 mGTC -= aOther;
275 mQPC -= aOther;
276 return *this;
279 // If the duration is less then two seconds, perform check of QPC stability
280 // by comparing both GTC and QPC calculated durations of this and aOther.
281 MFBT_API uint64_t TimeStampValue::CheckQPC(const TimeStampValue& aOther) const {
282 uint64_t deltaGTC = mGTC - aOther.mGTC;
284 if (!mHasQPC || !aOther.mHasQPC) { // Both not holding QPC
285 return deltaGTC;
288 uint64_t deltaQPC = mQPC - aOther.mQPC;
290 if (sHasStableTSC) { // For stable TSC there is no need to check
291 return deltaQPC;
294 // Check QPC is sane before using it.
295 int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC));
296 if (diff <= sGTCResolutionThreshold) {
297 return deltaQPC;
300 // Treat absolutely for calibration purposes
301 int64_t duration = DeprecatedAbs(int64_t(deltaGTC));
302 int64_t overflow = diff - sGTCResolutionThreshold;
304 LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms",
305 mt2ms(duration), mt2ms_f(overflow)));
307 if (overflow <= sFailureThreshold) { // We are in the limit, let go.
308 return deltaQPC;
311 // QPC deviates, don't use it, since now this method may only return deltaGTC.
313 if (!sUseQPC) { // QPC already disabled, no need to run the fault tolerance
314 // algorithm.
315 return deltaGTC;
318 LOG(("TimeStamp: QPC jittered over failure threshold"));
320 if (duration < sHardFailureLimit) {
321 // Interval between the two time stamps is very short, consider
322 // QPC as unstable and record a failure.
323 uint64_t now = ms2mt(GetTickCount64());
325 AutoCriticalSection lock(&sTimeStampLock);
327 if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) {
328 // There's already been an error in the last fault intollerant interval.
329 // Time since now to the checkpoint actually holds information on how many
330 // failures there were in the failure free interval we have defined.
331 uint64_t failureCount =
332 (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) /
333 sFailureFreeInterval;
334 if (failureCount > kMaxFailuresPerInterval) {
335 sUseQPC = false;
336 LOG(("TimeStamp: QPC disabled"));
337 } else {
338 // Move the fault intolerance checkpoint more to the future, prolong it
339 // to reflect the number of detected failures.
340 ++failureCount;
341 sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval;
342 LOG(("TimeStamp: recording %dth QPC failure", failureCount));
344 } else {
345 // Setup fault intolerance checkpoint in the future for first detected
346 // error.
347 sFaultIntoleranceCheckpoint = now + sFailureFreeInterval;
348 LOG(("TimeStamp: recording 1st QPC failure"));
352 return deltaGTC;
355 MFBT_API uint64_t
356 TimeStampValue::operator-(const TimeStampValue& aOther) const {
357 if (IsNull() && aOther.IsNull()) {
358 return uint64_t(0);
361 return CheckQPC(aOther);
364 // ----------------------------------------------------------------------------
365 // TimeDuration and TimeStamp implementation
366 // ----------------------------------------------------------------------------
368 MFBT_API double BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks) {
369 // Converting before arithmetic avoids blocked store forward
370 return double(aTicks) / (double(sFrequencyPerSec) * 1000.0);
373 MFBT_API double BaseTimeDurationPlatformUtils::ToSecondsSigDigits(
374 int64_t aTicks) {
375 // don't report a value < mResolution ...
376 LONGLONG resolution = sResolution;
377 LONGLONG resolutionSigDigs = sResolutionSigDigs;
378 LONGLONG valueSigDigs = resolution * (aTicks / resolution);
379 // and chop off insignificant digits
380 valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs);
381 return double(valueSigDigs) / kNsPerSecd;
384 MFBT_API int64_t
385 BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds) {
386 double result = ms2mt(aMilliseconds);
387 if (result > double(INT64_MAX)) {
388 return INT64_MAX;
389 } else if (result < double(INT64_MIN)) {
390 return INT64_MIN;
393 return result;
396 MFBT_API int64_t BaseTimeDurationPlatformUtils::ResolutionInTicks() {
397 return static_cast<int64_t>(sResolution);
400 static bool HasStableTSC() {
401 #if defined(_M_ARM64)
402 // AArch64 defines that its system counter run at a constant rate
403 // regardless of the current clock frequency of the system. See "The
404 // Generic Timer", section D7, in the ARMARM for ARMv8.
405 return true;
406 #else
407 union {
408 int regs[4];
409 struct {
410 int nIds;
411 char cpuString[12];
413 } cpuInfo;
415 __cpuid(cpuInfo.regs, 0);
416 // Only allow Intel or AMD CPUs for now.
417 // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the
418 // string so that we can compare in one go.
419 if (_strnicmp(cpuInfo.cpuString, "GenuntelineI", sizeof(cpuInfo.cpuString)) &&
420 _strnicmp(cpuInfo.cpuString, "AuthcAMDenti", sizeof(cpuInfo.cpuString))) {
421 return false;
424 int regs[4];
426 // detect if the Advanced Power Management feature is supported
427 __cpuid(regs, 0x80000000);
428 if ((unsigned int)regs[0] < 0x80000007) {
429 // XXX should we return true here? If there is no APM there may be
430 // no way how TSC can run out of sync among cores.
431 return false;
434 __cpuid(regs, 0x80000007);
435 // if bit 8 is set than TSC will run at a constant rate
436 // in all ACPI P-states, C-states and T-states
437 return regs[3] & (1 << 8);
438 #endif
441 static bool gInitialized = false;
443 MFBT_API void TimeStamp::Startup() {
444 if (gInitialized) {
445 return;
448 gInitialized = true;
450 // Decide which implementation to use for the high-performance timer.
452 InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
454 bool forceGTC = false;
455 bool forceQPC = false;
457 char* modevar = getenv("MOZ_TIMESTAMP_MODE");
458 if (modevar) {
459 if (!strcmp(modevar, "QPC")) {
460 forceQPC = true;
461 } else if (!strcmp(modevar, "GTC")) {
462 forceGTC = true;
466 LARGE_INTEGER freq;
467 sUseQPC = !forceGTC && ::QueryPerformanceFrequency(&freq);
468 if (!sUseQPC) {
469 // No Performance Counter. Fall back to use GetTickCount64.
470 InitResolution();
472 LOG(("TimeStamp: using GetTickCount64"));
473 return;
476 sHasStableTSC = forceQPC || HasStableTSC();
477 LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC));
479 sFrequencyPerSec = freq.QuadPart;
480 LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec));
482 InitThresholds();
483 InitResolution();
485 return;
488 MFBT_API void TimeStamp::Shutdown() { DeleteCriticalSection(&sTimeStampLock); }
490 TimeStampValue NowInternal(bool aHighResolution) {
491 // sUseQPC is volatile
492 bool useQPC = (aHighResolution && sUseQPC);
494 // Both values are in [mt] units.
495 ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0);
496 ULONGLONG GTC = ms2mt(GetTickCount64());
497 return TimeStampValue(GTC, QPC, useQPC, false);
500 MFBT_API TimeStamp TimeStamp::Now(bool aHighResolution) {
501 return TimeStamp::NowFuzzy(NowInternal(aHighResolution));
504 MFBT_API TimeStamp TimeStamp::NowUnfuzzed(bool aHighResolution) {
505 return TimeStamp(NowInternal(aHighResolution));
508 // Computes and returns the process uptime in microseconds.
509 // Returns 0 if an error was encountered.
511 MFBT_API uint64_t TimeStamp::ComputeProcessUptime() {
512 FILETIME start, foo, bar, baz;
513 bool success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
514 if (!success) {
515 return 0;
518 static const StaticDynamicallyLinkedFunctionPtr<void(WINAPI*)(LPFILETIME)>
519 pGetSystemTimePreciseAsFileTime(L"kernel32.dll",
520 "GetSystemTimePreciseAsFileTime");
522 FILETIME now;
523 if (pGetSystemTimePreciseAsFileTime) {
524 pGetSystemTimePreciseAsFileTime(&now);
525 } else {
526 GetSystemTimeAsFileTime(&now);
529 ULARGE_INTEGER startUsec = {{start.dwLowDateTime, start.dwHighDateTime}};
530 ULARGE_INTEGER nowUsec = {{now.dwLowDateTime, now.dwHighDateTime}};
532 return (nowUsec.QuadPart - startUsec.QuadPart) / 10ULL;
535 } // namespace mozilla