// If the duration is less then one second, perform check of QPC stability
// by comparing both 'epoch' and 'now' skew (=GTC - QPC) values.
bool
TimeStampValue::CheckQPC(int64_t aDuration, const TimeStampValue &aOther) const
{
  if (!mHasQPC || !aOther.mHasQPC) // Not both holding QPC
    return false;

  if (sHasStableTSC) // For stable TSC there is no need to check
    return true;

  if (!sUseQPC) // QPC globally disabled
    return false;

  // Treat absolutely for calibration purposes
  aDuration = Abs(aDuration);

  // Check QPC is sane before using it.

  LONGLONG skew1 = mGTC - mQPC;
  LONGLONG skew2 = aOther.mGTC - aOther.mQPC;

  LONGLONG diff = skew1 - skew2;
  LONGLONG overflow;

  if (diff < sUnderrunThreshold)
    overflow = sUnderrunThreshold - diff;
  else if (diff > sOverrunThreshold)
    overflow = diff - sOverrunThreshold;
  else
    return true;

  ULONGLONG trend;
  if (aDuration)
    trend = LONGLONG(overflow * (double(sQPCHardFailureDetectionInterval) / aDuration));
  else
    trend = overflow;

  LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms"
       ", adjusted trend per interval is %1.4fms",
       mt2ms(aDuration),
       mt2ms_f(overflow),
       mt2ms_f(trend)));

  if (trend <= ms2mt(kOverflowLimit)) {
    // We are in the limit, let go.
    return true;
  }

  // QPC deviates, don't use it.
  LOG(("TimeStamp: QPC found highly jittering"));

  if (aDuration < sQPCHardFailureDetectionInterval) {
    // Interval between the two time stamps is very short, consider
    // QPC as unstable and disable it completely.
    sUseQPC = false;
    LOG(("TimeStamp: QPC disabled"));
  }

  return false;
}
// 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;
  }

  // Treat absolutely for calibration purposes
  int64_t duration = DeprecatedAbs(int64_t(deltaGTC));
  int64_t overflow = diff - sGTCResolutionThreshold;

  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(sGetTickCount64());

    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"));
    }
  }

  return deltaGTC;
}