/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with // values of GetTickCount64().
// Estimate of the smallest duration of time we can measure. staticvolatile ULONGLONG sResolution; staticvolatile ULONGLONG sResolutionSigDigs; staticconstdouble kNsPerSecd = 1000000000.0; staticconst LONGLONG kNsPerMillisec = 1000000;
// ---------------------------------------------------------------------------- // Global constants // ----------------------------------------------------------------------------
// Tolerance to failures settings. // // What is the interval we want to have failure free. // in [ms] staticconst uint32_t kFailureFreeInterval = 5000; // How many failures we are willing to tolerate in the interval. staticconst uint32_t kMaxFailuresPerInterval = 4; // What is the threshold to treat fluctuations as actual failures. // in [ms] staticconst uint32_t kFailureThreshold = 50;
// If we are not able to get the value of GTC time increment, use this value // which is the most usual increment. staticconst DWORD kDefaultTimeIncrement = 156001;
// ---------------------------------------------------------------------------- // Global variables, not changing at runtime // ----------------------------------------------------------------------------
// Result of QueryPerformanceFrequency // We use default of 1 for the case we can't use QueryPerformanceCounter // to make mt/ms conversions work despite that. static uint64_t sFrequencyPerSec = 1;
// How much we are tolerant to GTC occasional loose of resoltion. // This number says how many multiples of the minimal GTC resolution // detected on the system are acceptable. This number is empirical. staticconst LONGLONG kGTCTickLeapTolerance = 4;
// Base tolerance (more: "inability of detection" range) threshold is calculated // dynamically, and kept in sGTCResolutionThreshold. // // Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) - // (QPC_now - QPC_epoch)) was in [-sGTCResolutionThreshold, // sGTCResolutionThreshold] interval every time we'd compared two time stamps. // If not, then we check the overflow behind this basic threshold // is in kFailureThreshold. If not, we condider it as a QPC failure. If too // many failures in short time are detected, QPC is considered faulty and // disabled. // // Kept in [mt] static LONGLONG sGTCResolutionThreshold;
// If QPC is found faulty for two stamps in this interval, we engage // the fault detection algorithm. For duration larger then this limit // we bypass using durations calculated from QPC when jitter is detected, // but don't touch the sUseQPC flag. // // Value is in [ms]. staticconst uint32_t kHardFailureLimit = 2000; // Conversion to [mt] static LONGLONG sHardFailureLimit;
// Conversion of kFailureFreeInterval and kFailureThreshold to [mt] static LONGLONG sFailureFreeInterval; static LONGLONG sFailureThreshold;
// ---------------------------------------------------------------------------- // Systemm status flags // ----------------------------------------------------------------------------
// Flag for stable TSC that indicates platform where QPC is stable. staticbool sHasStableTSC = false;
// ---------------------------------------------------------------------------- // Global state variables, changing at runtime // ----------------------------------------------------------------------------
// Initially true, set to false when QPC is found unstable and never // returns back to true since that time. staticboolvolatile sUseQPC = true;
// ---------------------------------------------------------------------------- // Global lock // ----------------------------------------------------------------------------
// Thread spin count before entering the full wait state for sTimeStampLock. // Inspired by Rob Arnold's work on PRMJ_Now(). staticconst DWORD kLockSpinCount = 4096;
// Common mutex (thanks the relative complexity of the logic, this is better // then using CMPXCHG8B.) // It is protecting the globals bellow. static CRITICAL_SECTION sTimeStampLock;
// ---------------------------------------------------------------------------- // Global lock protected variables // ----------------------------------------------------------------------------
// Timestamp in future until QPC must behave correctly. // Set to now + kFailureFreeInterval on first QPC failure detection. // Set to now + E * kFailureFreeInterval on following errors, // where E is number of errors detected during last kFailureFreeInterval // milliseconds, calculated simply as: // E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1. // When E > kMaxFailuresPerInterval -> disable QPC. // // Kept in [mt] static ULONGLONG sFaultIntoleranceCheckpoint = 0;
namespace mozilla {
// Result is in [mt] staticinline ULONGLONG PerformanceCounter() {
LARGE_INTEGER pc;
::QueryPerformanceCounter(&pc);
// QueryPerformanceCounter may slightly jitter (not be 100% monotonic.) // This is a simple go-backward protection for such a faulty hardware.
AutoCriticalSection lock(&sTimeStampLock);
static decltype(LARGE_INTEGER::QuadPart) last; if (last > pc.QuadPart) { return last * 1000ULL;
}
last = pc.QuadPart; return pc.QuadPart * 1000ULL;
}
if (!timeIncrement) {
timeIncrement = kDefaultTimeIncrement;
}
// Ceiling to a millisecond // Example values: 156001, 210000
DWORD timeIncrementCeil = timeIncrement; // Don't want to round up if already rounded, values will be: 156000, 209999
timeIncrementCeil -= 1; // Convert to ms, values will be: 15, 20
timeIncrementCeil /= 10000; // Round up, values will be: 16, 21
timeIncrementCeil += 1; // Convert back to 100ns, values will be: 160000, 210000
timeIncrementCeil *= 10000;
// How many milli-ticks has the interval rounded up
LONGLONG ticksPerGetTickCountResolutionCeiling =
(int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL;
// GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value.
sGTCResolutionThreshold =
LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling);
staticvoid InitResolution() { // 10 total trials is arbitrary: what we're trying to avoid by // looping is getting unlucky and being interrupted by a context // switch or signal, or being bitten by paging/cache effects
ULONGLONG minres = ~0ULL; if (sUseQPC) { int loops = 10; do {
ULONGLONG start = PerformanceCounter();
ULONGLONG end = PerformanceCounter();
ULONGLONG candidate = (end - start); if (candidate < minres) {
minres = candidate;
}
} while (--loops && minres);
if (0 == minres) {
minres = 1;
}
} else { // GetTickCount has only ~16ms known resolution
minres = ms2mt(16);
}
// Converting minres that is in [mt] to nanosecods, multiplicating // the argument to preserve resolution.
ULONGLONG result = mt2ms(minres * kNsPerMillisec); if (0 == result) {
result = 1;
}
sResolution = result;
// find the number of significant digits in mResolution, for the // sake of ToSecondsSigDigits()
ULONGLONG sigDigs; for (sigDigs = 1; !(sigDigs == result || 10 * sigDigs > result);
sigDigs *= 10);
// If the duration is less then two seconds, perform check of QPC stability // by comparing both GTC and QPC calculated durations of this and aOther.
MFBT_API uint64_t TimeStampValue::CheckQPC(const TimeStampValue& aOther) const {
uint64_t deltaGTC = mGTC - aOther.mGTC;
if (!mHasQPC || !aOther.mHasQPC) { // Both not holding QPC return deltaGTC;
}
uint64_t deltaQPC = mQPC - aOther.mQPC;
if (sHasStableTSC) { // For stable TSC there is no need to check return deltaQPC;
}
// Check QPC is sane before using it.
int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC)); if (diff <= sGTCResolutionThreshold) { return deltaQPC;
}
LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms",
mt2ms(duration), mt2ms_f(overflow)));
if (overflow <= sFailureThreshold) { // We are in the limit, let go. return deltaQPC;
}
// QPC deviates, don't use it, since now this method may only return deltaGTC.
if (!sUseQPC) { // QPC already disabled, no need to run the fault tolerance // algorithm. return deltaGTC;
}
LOG(("TimeStamp: QPC jittered over failure threshold"));
if (duration < sHardFailureLimit) { // Interval between the two time stamps is very short, consider // QPC as unstable and record a failure.
uint64_t now = ms2mt(GetTickCount64());
AutoCriticalSection lock(&sTimeStampLock);
if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) { // There's already been an error in the last fault intollerant interval. // Time since now to the checkpoint actually holds information on how many // failures there were in the failure free interval we have defined.
uint64_t failureCount =
(sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) /
sFailureFreeInterval; if (failureCount > kMaxFailuresPerInterval) {
sUseQPC = false;
LOG(("TimeStamp: QPC disabled"));
} else { // Move the fault intolerance checkpoint more to the future, prolong it // to reflect the number of detected failures.
++failureCount;
sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval;
LOG(("TimeStamp: recording %dth QPC failure", failureCount));
}
} else { // Setup fault intolerance checkpoint in the future for first detected // error.
sFaultIntoleranceCheckpoint = now + sFailureFreeInterval;
LOG(("TimeStamp: recording 1st QPC failure"));
}
}
class TimeStampValueTests { // Check that nullity is set/not set correctly.
static_assert(TimeStampValue{0}.IsNull());
static_assert(!TimeStampValue{1}.IsNull());
// Check that we ignore GTC when both TimeStampValues have QPC. (In each of // these tests, looking at GTC would give a different result.)
static_assert(TimeStampValue{1, 2, true} < TimeStampValue{1, 3, true});
static_assert(!(TimeStampValue{1, 2, true} == TimeStampValue{1, 3, true}));
// Check that, if either TimeStampValue doesn't have QPC, we only look at the // GTC values. These are the same cases as above, except that we accept the // opposite results because we turn off QPC on one or both of the // TimeStampValue's.
static_assert(TimeStampValue{1, 2, false} == TimeStampValue{1, 3, true});
static_assert(TimeStampValue{1, 2, true} == TimeStampValue{1, 3, false});
static_assert(TimeStampValue{1, 2, false} == TimeStampValue{1, 3, false});
staticbool HasStableTSC() { #ifdefined(_M_ARM64) // AArch64 defines that its system counter run at a constant rate // regardless of the current clock frequency of the system. See "The // Generic Timer", section D7, in the ARMARM for ARMv8. returntrue; #else union { int regs[4]; struct { int nIds; char cpuString[12];
};
} cpuInfo;
__cpuid(cpuInfo.regs, 0); // Only allow Intel or AMD CPUs for now. // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the // string so that we can compare in one go. if (_strnicmp(cpuInfo.cpuString, "GenuntelineI", sizeof(cpuInfo.cpuString)) &&
_strnicmp(cpuInfo.cpuString, "AuthcAMDenti", sizeof(cpuInfo.cpuString))) { returnfalse;
}
int regs[4];
// detect if the Advanced Power Management feature is supported
__cpuid(regs, 0x80000000); if ((unsignedint)regs[0] < 0x80000007) { // XXX should we return true here? If there is no APM there may be // no way how TSC can run out of sync among cores. returnfalse;
}
__cpuid(regs, 0x80000007); // if bit 8 is set than TSC will run at a constant rate // in all ACPI P-states, C-states and T-states return regs[3] & (1 << 8); #endif
}
staticbool gInitialized = false;
MFBT_API void TimeStamp::Startup() { if (gInitialized) { return;
}
gInitialized = true;
// Decide which implementation to use for the high-performance timer.
LARGE_INTEGER freq;
sUseQPC = !forceGTC && ::QueryPerformanceFrequency(&freq); if (!sUseQPC) { // No Performance Counter. Fall back to use GetTickCount64.
InitResolution();
LOG(("TimeStamp: using GetTickCount64")); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.