Commit 5de0588a authored by Peter Kasting's avatar Peter Kasting Committed by Commit Bot

//base/time cleanups:

* Make more things constexpr and/or make it possible to do so later
* Inline SaturatedAdd/Sub(), FromDouble(), FromProduct()
* Order the FromUnits{,D}() pairs together
* Init members in declaration
* Briefer implementations of various functions
* IWYU
* Fix declared-but-not-defined issue for FromTimeSpec() w/OS_FUSCHIA
* Use more specific DCHECKs
* No else after return
* Omit needless qualifiers
* EXPECT -> static_assert where possible
* <atomic> is legal now
* Don't handle DCHECK failure

The inlines don't hurt size: this saves 4 KB off chrome.dll in my local
release build.

Bug: none
TBR: stevenjb@chromium.org
Change-Id: I269d6426ac1587569e7a4c785250a4dd5e95d5bf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2358997Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarYuri Wiitala <miu@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799459}
parent b68142a1
...@@ -30,7 +30,7 @@ namespace internal { ...@@ -30,7 +30,7 @@ namespace internal {
#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS #if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
template <typename Dst, typename Src> template <typename Dst, typename Src>
struct SaturateFastAsmOp { struct SaturateFastAsmOp {
static const bool is_supported = false; static constexpr bool is_supported = false;
static constexpr Dst Do(Src) { static constexpr Dst Do(Src) {
// Force a compile failure if instantiated. // Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<Dst>(); return CheckOnFailure::template HandleFailure<Dst>();
...@@ -43,7 +43,7 @@ struct SaturateFastAsmOp { ...@@ -43,7 +43,7 @@ struct SaturateFastAsmOp {
// eke out better performance than range checking. // eke out better performance than range checking.
template <typename Dst, typename Src, typename Enable = void> template <typename Dst, typename Src, typename Enable = void>
struct IsValueInRangeFastOp { struct IsValueInRangeFastOp {
static const bool is_supported = false; static constexpr bool is_supported = false;
static constexpr bool Do(Src value) { static constexpr bool Do(Src value) {
// Force a compile failure if instantiated. // Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<bool>(); return CheckOnFailure::template HandleFailure<bool>();
...@@ -59,7 +59,7 @@ struct IsValueInRangeFastOp< ...@@ -59,7 +59,7 @@ struct IsValueInRangeFastOp<
std::is_integral<Dst>::value && std::is_integral<Src>::value && std::is_integral<Dst>::value && std::is_integral<Src>::value &&
std::is_signed<Dst>::value && std::is_signed<Src>::value && std::is_signed<Dst>::value && std::is_signed<Src>::value &&
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> { !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
static const bool is_supported = true; static constexpr bool is_supported = true;
static constexpr bool Do(Src value) { static constexpr bool Do(Src value) {
// Just downcast to the smaller type, sign extend it back to the original // Just downcast to the smaller type, sign extend it back to the original
...@@ -77,7 +77,7 @@ struct IsValueInRangeFastOp< ...@@ -77,7 +77,7 @@ struct IsValueInRangeFastOp<
std::is_integral<Dst>::value && std::is_integral<Src>::value && std::is_integral<Dst>::value && std::is_integral<Src>::value &&
!std::is_signed<Dst>::value && std::is_signed<Src>::value && !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> { !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
static const bool is_supported = true; static constexpr bool is_supported = true;
static constexpr bool Do(Src value) { static constexpr bool Do(Src value) {
// We cast a signed as unsigned to overflow negative values to the top, // We cast a signed as unsigned to overflow negative values to the top,
...@@ -156,7 +156,7 @@ constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { ...@@ -156,7 +156,7 @@ constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
// Arm, we can use the optimized saturation instructions. // Arm, we can use the optimized saturation instructions.
template <typename Dst, typename Src, typename Enable = void> template <typename Dst, typename Src, typename Enable = void>
struct SaturateFastOp { struct SaturateFastOp {
static const bool is_supported = false; static constexpr bool is_supported = false;
static constexpr Dst Do(Src value) { static constexpr Dst Do(Src value) {
// Force a compile failure if instantiated. // Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<Dst>(); return CheckOnFailure::template HandleFailure<Dst>();
...@@ -170,7 +170,7 @@ struct SaturateFastOp< ...@@ -170,7 +170,7 @@ struct SaturateFastOp<
typename std::enable_if<std::is_integral<Src>::value && typename std::enable_if<std::is_integral<Src>::value &&
std::is_integral<Dst>::value && std::is_integral<Dst>::value &&
SaturateFastAsmOp<Dst, Src>::is_supported>::type> { SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
static const bool is_supported = true; static constexpr bool is_supported = true;
static constexpr Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); } static constexpr Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
}; };
...@@ -181,12 +181,12 @@ struct SaturateFastOp< ...@@ -181,12 +181,12 @@ struct SaturateFastOp<
typename std::enable_if<std::is_integral<Src>::value && typename std::enable_if<std::is_integral<Src>::value &&
std::is_integral<Dst>::value && std::is_integral<Dst>::value &&
!SaturateFastAsmOp<Dst, Src>::is_supported>::type> { !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
static const bool is_supported = true; static constexpr bool is_supported = true;
static constexpr Dst Do(Src value) { static constexpr Dst Do(Src value) {
// The exact order of the following is structured to hit the correct // The exact order of the following is structured to hit the correct
// optimization heuristics across compilers. Do not change without // optimization heuristics across compilers. Do not change without
// checking the emitted code. // checking the emitted code.
Dst saturated = CommonMaxOrMin<Dst, Src>( const Dst saturated = CommonMaxOrMin<Dst, Src>(
IsMaxInRangeForNumericType<Dst, Src>() || IsMaxInRangeForNumericType<Dst, Src>() ||
(!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value))); (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value)) return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
...@@ -241,7 +241,7 @@ constexpr Dst strict_cast(Src value) { ...@@ -241,7 +241,7 @@ constexpr Dst strict_cast(Src value) {
// Some wrappers to statically check that a type is in range. // Some wrappers to statically check that a type is in range.
template <typename Dst, typename Src, class Enable = void> template <typename Dst, typename Src, class Enable = void>
struct IsNumericRangeContained { struct IsNumericRangeContained {
static const bool value = false; static constexpr bool value = false;
}; };
template <typename Dst, typename Src> template <typename Dst, typename Src>
...@@ -250,8 +250,9 @@ struct IsNumericRangeContained< ...@@ -250,8 +250,9 @@ struct IsNumericRangeContained<
Src, Src,
typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value && typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
ArithmeticOrUnderlyingEnum<Src>::value>::type> { ArithmeticOrUnderlyingEnum<Src>::value>::type> {
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == static constexpr bool value =
NUMERIC_RANGE_CONTAINED; StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
NUMERIC_RANGE_CONTAINED;
}; };
// StrictNumeric implements compile time range checking between numeric types by // StrictNumeric implements compile time range checking between numeric types by
......
...@@ -5,15 +5,12 @@ ...@@ -5,15 +5,12 @@
#include "base/time/time.h" #include "base/time/time.h"
#include <cmath> #include <cmath>
#include <ios>
#include <limits> #include <limits>
#include <ostream> #include <ostream>
#include <sstream> #include <tuple>
#include <utility>
#include "base/logging.h"
#include "base/macros.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
...@@ -36,7 +33,7 @@ namespace { ...@@ -36,7 +33,7 @@ namespace {
// //
// Adapted from absl::ConsumePrefix(): // Adapted from absl::ConsumePrefix():
// https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/strings/strip.h?l=45&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/strings/strip.h?l=45&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
inline bool ConsumePrefix(StringPiece& str, StringPiece expected) { bool ConsumePrefix(StringPiece& str, StringPiece expected) {
if (!StartsWith(str, expected)) if (!StartsWith(str, expected))
return false; return false;
str.remove_prefix(expected.size()); str.remove_prefix(expected.size());
...@@ -64,7 +61,8 @@ struct ParsedDecimal { ...@@ -64,7 +61,8 @@ struct ParsedDecimal {
// //
// Adapted from absl: // Adapted from absl:
// https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=807&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=807&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
Optional<ParsedDecimal> ConsumeDurationNumber(StringPiece& number_string) { constexpr Optional<ParsedDecimal> ConsumeDurationNumber(
StringPiece& number_string) {
ParsedDecimal res; ParsedDecimal res;
StringPiece::const_iterator orig_start = number_string.begin(); StringPiece::const_iterator orig_start = number_string.begin();
// Parse contiguous digits. // Parse contiguous digits.
...@@ -109,18 +107,16 @@ Optional<ParsedDecimal> ConsumeDurationNumber(StringPiece& number_string) { ...@@ -109,18 +107,16 @@ Optional<ParsedDecimal> ConsumeDurationNumber(StringPiece& number_string) {
// Adapted from absl: // Adapted from absl:
// https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=841&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c // https://cs.chromium.org/chromium/src/third_party/abseil-cpp/absl/time/duration.cc?l=841&rcl=2c22e9135f107a4319582ae52e2e3e6b201b6b7c
Optional<TimeDelta> ConsumeDurationUnit(StringPiece& unit_string) { Optional<TimeDelta> ConsumeDurationUnit(StringPiece& unit_string) {
// Note: "ms" MUST be checked before "m" to ensure that milliseconds are not for (const auto& str_delta : {
// parsed as minutes. std::make_pair("ns", TimeDelta::FromNanoseconds(1)),
static constexpr std::pair<const char*, TimeDelta> kUnits[] = { std::make_pair("us", TimeDelta::FromMicroseconds(1)),
{"ns", TimeDelta::FromNanoseconds(1)}, // Note: "ms" MUST be checked before "m" to ensure that milliseconds
{"us", TimeDelta::FromMicroseconds(1)}, // are not parsed as minutes.
{"ms", TimeDelta::FromMilliseconds(1)}, std::make_pair("ms", TimeDelta::FromMilliseconds(1)),
{"s", TimeDelta::FromSeconds(1)}, std::make_pair("s", TimeDelta::FromSeconds(1)),
{"m", TimeDelta::FromMinutes(1)}, std::make_pair("m", TimeDelta::FromMinutes(1)),
{"h", TimeDelta::FromHours(1)}, std::make_pair("h", TimeDelta::FromHours(1)),
}; }) {
for (const auto& str_delta : kUnits) {
if (ConsumePrefix(unit_string, str_delta.first)) if (ConsumePrefix(unit_string, str_delta.first))
return str_delta.second; return str_delta.second;
} }
...@@ -183,109 +179,63 @@ Optional<TimeDelta> TimeDelta::FromString(StringPiece duration_string) { ...@@ -183,109 +179,63 @@ Optional<TimeDelta> TimeDelta::FromString(StringPiece duration_string) {
} }
int TimeDelta::InDays() const { int TimeDelta::InDays() const {
if (is_max()) { if (!is_inf())
// Preserve max to prevent overflow. return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
return std::numeric_limits<int>::max(); return (delta_ < 0) ? std::numeric_limits<int>::min()
} : std::numeric_limits<int>::max();
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
} }
int TimeDelta::InDaysFloored() const { int TimeDelta::InDaysFloored() const {
if (is_max()) { if (!is_inf()) {
// Preserve max to prevent overflow. const int result = delta_ / Time::kMicrosecondsPerDay;
return std::numeric_limits<int>::max(); // Convert |result| from truncating to flooring.
} return (result * Time::kMicrosecondsPerDay > delta_) ? (result - 1)
if (is_min()) { : result;
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
} }
int result = delta_ / Time::kMicrosecondsPerDay; return (delta_ < 0) ? std::numeric_limits<int>::min()
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay); : std::numeric_limits<int>::max();
if (remainder < 0) {
--result; // Use floor(), not trunc() rounding behavior.
}
return result;
} }
double TimeDelta::InSecondsF() const { double TimeDelta::InSecondsF() const {
if (is_max()) { if (!is_inf())
// Preserve max to prevent overflow. return double{delta_} / Time::kMicrosecondsPerSecond;
return std::numeric_limits<double>::infinity(); return (delta_ < 0) ? -std::numeric_limits<double>::infinity()
} : std::numeric_limits<double>::infinity();
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
} }
int64_t TimeDelta::InSeconds() const { int64_t TimeDelta::InSeconds() const {
if (is_max()) { return is_inf() ? delta_ : (delta_ / Time::kMicrosecondsPerSecond);
// Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerSecond;
} }
double TimeDelta::InMillisecondsF() const { double TimeDelta::InMillisecondsF() const {
if (is_max()) { if (!is_inf())
// Preserve max to prevent overflow. return double{delta_} / Time::kMicrosecondsPerMillisecond;
return std::numeric_limits<double>::infinity(); return (delta_ < 0) ? -std::numeric_limits<double>::infinity()
} : std::numeric_limits<double>::infinity();
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
} }
int64_t TimeDelta::InMilliseconds() const { int64_t TimeDelta::InMilliseconds() const {
if (is_max()) { if (!is_inf())
// Preserve max to prevent overflow. return delta_ / Time::kMicrosecondsPerMillisecond;
return std::numeric_limits<int64_t>::max(); return (delta_ < 0) ? std::numeric_limits<int64_t>::min()
} : std::numeric_limits<int64_t>::max();
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerMillisecond;
} }
int64_t TimeDelta::InMillisecondsRoundedUp() const { int64_t TimeDelta::InMillisecondsRoundedUp() const {
if (is_max()) { if (!is_inf()) {
// Preserve max to prevent overflow. const int64_t result = delta_ / Time::kMicrosecondsPerMillisecond;
return std::numeric_limits<int64_t>::max(); // Convert |result| from truncating to ceiling.
} return (delta_ > result * Time::kMicrosecondsPerMillisecond) ? (result + 1)
if (is_min()) { : result;
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
} }
int64_t result = delta_ / Time::kMicrosecondsPerMillisecond; return delta_;
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerMillisecond);
if (remainder > 0) {
++result; // Use ceil(), not trunc() rounding behavior.
}
return result;
} }
double TimeDelta::InMicrosecondsF() const { double TimeDelta::InMicrosecondsF() const {
if (is_max()) { if (!is_inf())
// Preserve max to prevent overflow. return double{delta_};
return std::numeric_limits<double>::infinity(); return (delta_ < 0) ? -std::numeric_limits<double>::infinity()
} : std::numeric_limits<double>::infinity();
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_);
} }
TimeDelta TimeDelta::CeilToMultiple(TimeDelta interval) const { TimeDelta TimeDelta::CeilToMultiple(TimeDelta interval) const {
...@@ -349,117 +299,82 @@ TimeDelta Time::ToDeltaSinceWindowsEpoch() const { ...@@ -349,117 +299,82 @@ TimeDelta Time::ToDeltaSinceWindowsEpoch() const {
Time Time::FromTimeT(time_t tt) { Time Time::FromTimeT(time_t tt) {
if (tt == 0) if (tt == 0)
return Time(); // Preserve 0 so we can tell it doesn't exist. return Time(); // Preserve 0 so we can tell it doesn't exist.
if (tt == std::numeric_limits<time_t>::max()) return (tt == std::numeric_limits<time_t>::max())
return Max(); ? Max()
return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSeconds(tt); : (UnixEpoch() + TimeDelta::FromSeconds(tt));
} }
time_t Time::ToTimeT() const { time_t Time::ToTimeT() const {
if (is_null()) if (is_null())
return 0; // Preserve 0 so we can tell it doesn't exist. return 0; // Preserve 0 so we can tell it doesn't exist.
if (is_max()) { if (!is_inf() && ((std::numeric_limits<int64_t>::max() -
// Preserve max without offset to prevent overflow. kTimeTToMicrosecondsOffset) > us_))
return std::numeric_limits<time_t>::max(); return (*this - UnixEpoch()).InSeconds();
} return (us_ < 0) ? std::numeric_limits<time_t>::min()
if (is_min()) { : std::numeric_limits<time_t>::max();
// Preserve min without offset to prevent underflow.
return std::numeric_limits<time_t>::min();
}
if (std::numeric_limits<int64_t>::max() - kTimeTToMicrosecondsOffset <= us_) {
DLOG(WARNING) << "Overflow when converting base::Time with internal " <<
"value " << us_ << " to time_t.";
return std::numeric_limits<time_t>::max();
}
return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond;
} }
// static // static
Time Time::FromDoubleT(double dt) { Time Time::FromDoubleT(double dt) {
if (dt == 0 || std::isnan(dt)) // Preserve 0 so we can tell it doesn't exist.
return Time(); // Preserve 0 so we can tell it doesn't exist. return (dt == 0 || std::isnan(dt))
return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSecondsD(dt); ? Time()
: (UnixEpoch() + TimeDelta::FromSecondsD(dt));
} }
double Time::ToDoubleT() const { double Time::ToDoubleT() const {
if (is_null()) if (is_null())
return 0; // Preserve 0 so we can tell it doesn't exist. return 0; // Preserve 0 so we can tell it doesn't exist.
if (is_max()) { if (!is_inf())
// Preserve max without offset to prevent overflow. return (*this - UnixEpoch()).InSecondsF();
return std::numeric_limits<double>::infinity(); return (us_ < 0) ? -std::numeric_limits<double>::infinity()
} : std::numeric_limits<double>::infinity();
if (is_min()) {
// Preserve min without offset to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
static_cast<double>(kMicrosecondsPerSecond));
} }
#if defined(OS_POSIX) #if defined(OS_POSIX) || defined(OS_FUCHSIA)
// static // static
Time Time::FromTimeSpec(const timespec& ts) { Time Time::FromTimeSpec(const timespec& ts) {
return FromDoubleT(ts.tv_sec + return FromDoubleT(ts.tv_sec + double{ts.tv_nsec} / kNanosecondsPerSecond);
static_cast<double>(ts.tv_nsec) /
base::Time::kNanosecondsPerSecond);
} }
#endif #endif
// static // static
Time Time::FromJsTime(double ms_since_epoch) { Time Time::FromJsTime(double ms_since_epoch) {
// The epoch is a valid time, so this constructor doesn't interpret // The epoch is a valid time, so this constructor doesn't interpret 0 as the
// 0 as the null time. // null time.
return Time(kTimeTToMicrosecondsOffset) + return UnixEpoch() + TimeDelta::FromMillisecondsD(ms_since_epoch);
TimeDelta::FromMillisecondsD(ms_since_epoch);
} }
double Time::ToJsTime() const { double Time::ToJsTime() const {
if (is_null()) { // Preserve 0 so the invalid result doesn't depend on the platform.
// Preserve 0 so the invalid result doesn't depend on the platform. return is_null() ? 0 : ToJsTimeIgnoringNull();
return 0;
}
return ToJsTimeIgnoringNull();
} }
double Time::ToJsTimeIgnoringNull() const { double Time::ToJsTimeIgnoringNull() const {
if (is_max()) { // Preserve max and min without offset to prevent over/underflow.
// Preserve max without offset to prevent overflow. if (!is_inf())
return std::numeric_limits<double>::infinity(); return (*this - UnixEpoch()).InMillisecondsF();
} return (us_ < 0) ? -std::numeric_limits<double>::infinity()
if (is_min()) { : std::numeric_limits<double>::infinity();
// Preserve min without offset to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
kMicrosecondsPerMillisecond);
} }
Time Time::FromJavaTime(int64_t ms_since_epoch) { Time Time::FromJavaTime(int64_t ms_since_epoch) {
return base::Time::UnixEpoch() + return UnixEpoch() + TimeDelta::FromMilliseconds(ms_since_epoch);
base::TimeDelta::FromMilliseconds(ms_since_epoch);
} }
int64_t Time::ToJavaTime() const { int64_t Time::ToJavaTime() const {
if (is_null()) { // Preserve 0 so the invalid result doesn't depend on the platform.
// Preserve 0 so the invalid result doesn't depend on the platform. if (is_null())
return 0; return 0;
} if (!is_inf())
if (is_max()) { return (*this - UnixEpoch()).InMilliseconds();
// Preserve max without offset to prevent overflow. return (us_ < 0) ? std::numeric_limits<int64_t>::min()
return std::numeric_limits<int64_t>::max(); : std::numeric_limits<int64_t>::max();
}
if (is_min()) {
// Preserve min without offset to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return ((us_ - kTimeTToMicrosecondsOffset) /
kMicrosecondsPerMillisecond);
} }
// static // static
Time Time::UnixEpoch() { Time Time::UnixEpoch() {
Time time; return Time(kTimeTToMicrosecondsOffset);
time.us_ = kTimeTToMicrosecondsOffset;
return time;
} }
Time Time::Midnight(bool is_local) const { Time Time::Midnight(bool is_local) const {
...@@ -470,27 +385,25 @@ Time Time::Midnight(bool is_local) const { ...@@ -470,27 +385,25 @@ Time Time::Midnight(bool is_local) const {
exploded.second = 0; exploded.second = 0;
exploded.millisecond = 0; exploded.millisecond = 0;
Time out_time; Time out_time;
if (FromExploded(is_local, exploded, &out_time)) { if (FromExploded(is_local, exploded, &out_time))
return out_time; return out_time;
} else if (is_local) {
// Hitting this branch means 00:00:00am of the current day // Reaching here means 00:00:00am of the current day does not exist (due to
// does not exist (due to Daylight Saving Time in some countries // Daylight Saving Time in some countries where clocks are shifted at
// where clocks are shifted at midnight). In this case, midnight // midnight). In this case, midnight should be defined as 01:00:00am.
// should be defined as 01:00:00am. DCHECK(is_local);
exploded.hour = 1; exploded.hour = 1;
if (FromExploded(is_local, exploded, &out_time)) const bool result = FromExploded(is_local, exploded, &out_time);
return out_time; DCHECK(result); // This function must not fail.
} return out_time;
// This function must not fail.
NOTREACHED();
return Time();
} }
// static // static
bool Time::FromStringInternal(const char* time_string, bool Time::FromStringInternal(const char* time_string,
bool is_local, bool is_local,
Time* parsed_time) { Time* parsed_time) {
DCHECK((time_string != nullptr) && (parsed_time != nullptr)); DCHECK(time_string);
DCHECK(parsed_time);
if (time_string[0] == '\0') if (time_string[0] == '\0')
return false; return false;
...@@ -499,20 +412,19 @@ bool Time::FromStringInternal(const char* time_string, ...@@ -499,20 +412,19 @@ bool Time::FromStringInternal(const char* time_string,
PRStatus result = PR_ParseTimeString(time_string, PRStatus result = PR_ParseTimeString(time_string,
is_local ? PR_FALSE : PR_TRUE, is_local ? PR_FALSE : PR_TRUE,
&result_time); &result_time);
if (PR_SUCCESS != result) if (result != PR_SUCCESS)
return false; return false;
result_time += kTimeTToMicrosecondsOffset; *parsed_time = UnixEpoch() + TimeDelta::FromMicroseconds(result_time);
*parsed_time = Time(result_time);
return true; return true;
} }
// static // static
bool Time::ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs) { bool Time::ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs) {
return lhs.year == rhs.year && lhs.month == rhs.month && return std::tie(lhs.year, lhs.month, lhs.day_of_month, lhs.hour, lhs.minute,
lhs.day_of_month == rhs.day_of_month && lhs.hour == rhs.hour && lhs.second, lhs.millisecond) ==
lhs.minute == rhs.minute && lhs.second == rhs.second && std::tie(rhs.year, rhs.month, rhs.day_of_month, rhs.hour, rhs.minute,
lhs.millisecond == rhs.millisecond; rhs.second, rhs.millisecond);
} }
// static // static
...@@ -520,30 +432,20 @@ bool Time::FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds, ...@@ -520,30 +432,20 @@ bool Time::FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds,
Time* time) { Time* time) {
// Adjust the provided time from milliseconds since the Unix epoch (1970) to // Adjust the provided time from milliseconds since the Unix epoch (1970) to
// microseconds since the Windows epoch (1601), avoiding overflows. // microseconds since the Windows epoch (1601), avoiding overflows.
base::CheckedNumeric<int64_t> checked_microseconds_win_epoch = CheckedNumeric<int64_t> checked_microseconds_win_epoch = unix_milliseconds;
unix_milliseconds;
checked_microseconds_win_epoch *= kMicrosecondsPerMillisecond; checked_microseconds_win_epoch *= kMicrosecondsPerMillisecond;
checked_microseconds_win_epoch += kTimeTToMicrosecondsOffset; checked_microseconds_win_epoch += kTimeTToMicrosecondsOffset;
if (!checked_microseconds_win_epoch.IsValid()) { *time = Time(checked_microseconds_win_epoch.ValueOrDefault(0));
*time = base::Time(0); return checked_microseconds_win_epoch.IsValid();
return false;
}
*time = Time(checked_microseconds_win_epoch.ValueOrDie());
return true;
} }
int64_t Time::ToRoundedDownMillisecondsSinceUnixEpoch() const { int64_t Time::ToRoundedDownMillisecondsSinceUnixEpoch() const {
// Adjust from Windows epoch (1601) to Unix epoch (1970). // Adjust from Windows epoch (1601) to Unix epoch (1970).
int64_t microseconds = us_ - kTimeTToMicrosecondsOffset; const int64_t ms = (*this - UnixEpoch()).InMicroseconds();
// Round the microseconds towards -infinity. // Floor rather than truncating.
if (microseconds >= 0) { return (ms >= 0) ? (ms / kMicrosecondsPerMillisecond)
// In this case, rounding towards -infinity means rounding towards 0. : ((ms + 1) / kMicrosecondsPerMillisecond - 1);
return microseconds / kMicrosecondsPerMillisecond;
} else {
return (microseconds + 1) / kMicrosecondsPerMillisecond - 1;
}
} }
std::ostream& operator<<(std::ostream& os, Time time) { std::ostream& operator<<(std::ostream& os, Time time) {
...@@ -569,7 +471,7 @@ TimeTicks TimeTicks::Now() { ...@@ -569,7 +471,7 @@ TimeTicks TimeTicks::Now() {
// static // static
TimeTicks TimeTicks::UnixEpoch() { TimeTicks TimeTicks::UnixEpoch() {
static const base::NoDestructor<base::TimeTicks> epoch([]() { static const NoDestructor<TimeTicks> epoch([]() {
return subtle::TimeTicksNowIgnoringOverride() - return subtle::TimeTicksNowIgnoringOverride() -
(subtle::TimeNowIgnoringOverride() - Time::UnixEpoch()); (subtle::TimeNowIgnoringOverride() - Time::UnixEpoch());
}()); }());
...@@ -613,18 +515,16 @@ std::ostream& operator<<(std::ostream& os, ThreadTicks thread_ticks) { ...@@ -613,18 +515,16 @@ std::ostream& operator<<(std::ostream& os, ThreadTicks thread_ticks) {
// Time::Exploded ------------------------------------------------------------- // Time::Exploded -------------------------------------------------------------
inline bool is_in_range(int value, int lo, int hi) {
return lo <= value && value <= hi;
}
bool Time::Exploded::HasValidValues() const { bool Time::Exploded::HasValidValues() const {
return is_in_range(month, 1, 12) && // clang-format off
is_in_range(day_of_week, 0, 6) && return (1 <= month) && (month <= 12) &&
is_in_range(day_of_month, 1, 31) && (0 <= day_of_week) && (day_of_week <= 6) &&
is_in_range(hour, 0, 23) && (1 <= day_of_month) && (day_of_month <= 31) &&
is_in_range(minute, 0, 59) && (0 <= hour) && (hour <= 23) &&
is_in_range(second, 0, 60) && (0 <= minute) && (minute <= 59) &&
is_in_range(millisecond, 0, 999); (0 <= second) && (second <= 60) &&
(0 <= millisecond) && (millisecond <= 999);
// clang-format on
} }
} // namespace base } // namespace base
...@@ -87,7 +87,6 @@ ...@@ -87,7 +87,6 @@
#if defined(OS_WIN) #if defined(OS_WIN)
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/win/windows_types.h" #include "base/win/windows_types.h"
#endif
namespace ABI { namespace ABI {
namespace Windows { namespace Windows {
...@@ -96,31 +95,17 @@ struct DateTime; ...@@ -96,31 +95,17 @@ struct DateTime;
} // namespace Foundation } // namespace Foundation
} // namespace Windows } // namespace Windows
} // namespace ABI } // namespace ABI
#endif
namespace base { namespace base {
class PlatformThreadHandle; class PlatformThreadHandle;
class TimeDelta;
// The functions in the time_internal namespace are meant to be used only by the
// time classes and functions. Please use the math operators defined in the
// time classes instead.
namespace time_internal {
// Add or subtract a TimeDelta from |value|. TimeDelta::Min()/Max() are treated
// as infinity and will always saturate the return value (infinity math applies
// if |value| also is at either limit of its spectrum). The int64_t argument and
// return value are in terms of a microsecond timebase.
BASE_EXPORT constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta);
BASE_EXPORT constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta);
} // namespace time_internal
// TimeDelta ------------------------------------------------------------------ // TimeDelta ------------------------------------------------------------------
class BASE_EXPORT TimeDelta { class BASE_EXPORT TimeDelta {
public: public:
constexpr TimeDelta() : delta_(0) {} constexpr TimeDelta() = default;
// Converts units of time to TimeDeltas. // Converts units of time to TimeDeltas.
// These conversions treat minimum argument values as min type values or -inf, // These conversions treat minimum argument values as min type values or -inf,
...@@ -131,14 +116,15 @@ class BASE_EXPORT TimeDelta { ...@@ -131,14 +116,15 @@ class BASE_EXPORT TimeDelta {
static constexpr TimeDelta FromDays(int days); static constexpr TimeDelta FromDays(int days);
static constexpr TimeDelta FromHours(int hours); static constexpr TimeDelta FromHours(int hours);
static constexpr TimeDelta FromMinutes(int minutes); static constexpr TimeDelta FromMinutes(int minutes);
static constexpr TimeDelta FromSeconds(int64_t secs);
static constexpr TimeDelta FromMilliseconds(int64_t ms);
static constexpr TimeDelta FromMicroseconds(int64_t us);
static constexpr TimeDelta FromNanoseconds(int64_t ns);
static constexpr TimeDelta FromSecondsD(double secs); static constexpr TimeDelta FromSecondsD(double secs);
static constexpr TimeDelta FromSeconds(int64_t secs);
static constexpr TimeDelta FromMillisecondsD(double ms); static constexpr TimeDelta FromMillisecondsD(double ms);
static constexpr TimeDelta FromMilliseconds(int64_t ms);
static constexpr TimeDelta FromMicrosecondsD(double us); static constexpr TimeDelta FromMicrosecondsD(double us);
static constexpr TimeDelta FromMicroseconds(int64_t us);
static constexpr TimeDelta FromNanosecondsD(double ns); static constexpr TimeDelta FromNanosecondsD(double ns);
static constexpr TimeDelta FromNanoseconds(int64_t ns);
#if defined(OS_WIN) #if defined(OS_WIN)
static TimeDelta FromQPCValue(LONGLONG qpc_value); static TimeDelta FromQPCValue(LONGLONG qpc_value);
// TODO(crbug.com/989694): Avoid base::TimeDelta factory functions // TODO(crbug.com/989694): Avoid base::TimeDelta factory functions
...@@ -217,12 +203,8 @@ class BASE_EXPORT TimeDelta { ...@@ -217,12 +203,8 @@ class BASE_EXPORT TimeDelta {
constexpr bool is_zero() const { return delta_ == 0; } constexpr bool is_zero() const { return delta_ == 0; }
// Returns true if the time delta is the maximum/minimum time delta. // Returns true if the time delta is the maximum/minimum time delta.
constexpr bool is_max() const { constexpr bool is_max() const { return *this == Max(); }
return delta_ == std::numeric_limits<int64_t>::max(); constexpr bool is_min() const { return *this == Min(); }
}
constexpr bool is_min() const {
return delta_ == std::numeric_limits<int64_t>::min();
}
constexpr bool is_inf() const { return is_min() || is_max(); } constexpr bool is_inf() const { return is_min() || is_max(); }
#if defined(OS_POSIX) || defined(OS_FUCHSIA) #if defined(OS_POSIX) || defined(OS_FUCHSIA)
...@@ -263,12 +245,8 @@ class BASE_EXPORT TimeDelta { ...@@ -263,12 +245,8 @@ class BASE_EXPORT TimeDelta {
constexpr int64_t InNanoseconds() const; constexpr int64_t InNanoseconds() const;
// Computations with other deltas. // Computations with other deltas.
constexpr TimeDelta operator+(TimeDelta other) const { constexpr TimeDelta operator+(TimeDelta other) const;
return TimeDelta(time_internal::SaturatedAdd(delta_, other)); constexpr TimeDelta operator-(TimeDelta other) const;
}
constexpr TimeDelta operator-(TimeDelta other) const {
return TimeDelta(time_internal::SaturatedSub(delta_, other));
}
constexpr TimeDelta& operator+=(TimeDelta other) { constexpr TimeDelta& operator+=(TimeDelta other) {
return *this = (*this + other); return *this = (*this + other);
...@@ -366,22 +344,11 @@ class BASE_EXPORT TimeDelta { ...@@ -366,22 +344,11 @@ class BASE_EXPORT TimeDelta {
TimeDelta RoundToMultiple(TimeDelta interval) const; TimeDelta RoundToMultiple(TimeDelta interval) const;
private: private:
friend constexpr int64_t time_internal::SaturatedAdd(int64_t value,
TimeDelta delta);
friend constexpr int64_t time_internal::SaturatedSub(int64_t value,
TimeDelta delta);
// Constructs a delta given the duration in microseconds. This is private // Constructs a delta given the duration in microseconds. This is private
// to avoid confusion by callers with an integer constructor. Use // to avoid confusion by callers with an integer constructor. Use
// FromSeconds, FromMilliseconds, etc. instead. // FromSeconds, FromMilliseconds, etc. instead.
constexpr explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {} constexpr explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {}
static constexpr TimeDelta FromDouble(double value);
// Builds a delta from the product of a user-provided value and a
// known-positive value.
static constexpr TimeDelta FromProduct(int64_t value, int64_t positive_value);
// Returns a double representation of this TimeDelta's tick count. In // Returns a double representation of this TimeDelta's tick count. In
// particular, Max()/Min() are converted to +/-infinity. // particular, Max()/Min() are converted to +/-infinity.
constexpr double ToDouble() const { constexpr double ToDouble() const {
...@@ -392,9 +359,27 @@ class BASE_EXPORT TimeDelta { ...@@ -392,9 +359,27 @@ class BASE_EXPORT TimeDelta {
} }
// Delta in microseconds. // Delta in microseconds.
int64_t delta_; int64_t delta_ = 0;
}; };
constexpr TimeDelta TimeDelta::operator+(TimeDelta other) const {
if (!other.is_inf())
return TimeDelta(int64_t{base::ClampAdd(delta_, other.delta_)});
// Additions involving two infinities are only valid if signs match.
CHECK(!is_inf() || (delta_ == other.delta_));
return other;
}
constexpr TimeDelta TimeDelta::operator-(TimeDelta other) const {
if (!other.is_inf())
return TimeDelta(int64_t{base::ClampSub(delta_, other.delta_)});
// Subtractions involving two infinities are only valid if signs differ.
CHECK_NE(delta_, other.delta_);
return (other.delta_ < 0) ? Max() : Min();
}
template <typename T> template <typename T>
constexpr TimeDelta operator*(T a, TimeDelta td) { constexpr TimeDelta operator*(T a, TimeDelta td) {
return td * a; return td * a;
...@@ -403,32 +388,13 @@ constexpr TimeDelta operator*(T a, TimeDelta td) { ...@@ -403,32 +388,13 @@ constexpr TimeDelta operator*(T a, TimeDelta td) {
// For logging use only. // For logging use only.
BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta); BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta);
// TimeBase--------------------------------------------------------------------
// Do not reference the time_internal::TimeBase template class directly. Please // Do not reference the time_internal::TimeBase template class directly. Please
// use one of the time subclasses instead, and only reference the public // use one of the time subclasses instead, and only reference the public
// TimeBase members via those classes. // TimeBase members via those classes.
namespace time_internal { namespace time_internal {
constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta) {
if (!delta.is_inf())
return base::ClampAdd(value, delta.delta_);
// Additions involving two infinities are only valid if signs match.
CHECK(!TimeDelta(value).is_inf() || (value == delta.delta_));
return delta.delta_;
}
constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta) {
if (!delta.is_inf())
return base::ClampSub(value, delta.delta_);
// Subtractions involving two infinities are only valid if signs differ.
CHECK_NE(value, delta.delta_);
return (delta.delta_ < 0) ? std::numeric_limits<int64_t>::max()
: std::numeric_limits<int64_t>::min();
}
// TimeBase--------------------------------------------------------------------
// Provides value storage and comparison/math operations common to all time // Provides value storage and comparison/math operations common to all time
// classes. Each subclass provides for strong type-checking to ensure // classes. Each subclass provides for strong type-checking to ensure
// semantically meaningful comparison/math of time values from the same clock // semantically meaningful comparison/math of time values from the same clock
...@@ -466,12 +432,9 @@ class TimeBase { ...@@ -466,12 +432,9 @@ class TimeBase {
constexpr bool is_null() const { return us_ == 0; } constexpr bool is_null() const { return us_ == 0; }
// Returns true if this object represents the maximum/minimum time. // Returns true if this object represents the maximum/minimum time.
constexpr bool is_max() const { constexpr bool is_max() const { return *this == Max(); }
return us_ == std::numeric_limits<int64_t>::max(); constexpr bool is_min() const { return *this == Min(); }
} constexpr bool is_inf() const { return is_min() || is_max(); }
constexpr bool is_min() const {
return us_ == std::numeric_limits<int64_t>::min();
}
// Returns the maximum/minimum times, which should be greater/less than than // Returns the maximum/minimum times, which should be greater/less than than
// any reasonable time with which we might compare it. // any reasonable time with which we might compare it.
...@@ -513,10 +476,12 @@ class TimeBase { ...@@ -513,10 +476,12 @@ class TimeBase {
// Return a new time modified by some delta. // Return a new time modified by some delta.
constexpr TimeClass operator+(TimeDelta delta) const { constexpr TimeClass operator+(TimeDelta delta) const {
return TimeClass(time_internal::SaturatedAdd(us_, delta)); return TimeClass(
(TimeDelta::FromMicroseconds(us_) + delta).InMicroseconds());
} }
constexpr TimeClass operator-(TimeDelta delta) const { constexpr TimeClass operator-(TimeDelta delta) const {
return TimeClass(time_internal::SaturatedSub(us_, delta)); return TimeClass(
(TimeDelta::FromMicroseconds(us_) - delta).InMicroseconds());
} }
// Modify by some time delta. // Modify by some time delta.
...@@ -846,65 +811,71 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> { ...@@ -846,65 +811,71 @@ class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
int64_t ToRoundedDownMillisecondsSinceUnixEpoch() const; int64_t ToRoundedDownMillisecondsSinceUnixEpoch() const;
}; };
// TimeDelta functions that must appear below the declarations of Time/TimeDelta
// static // static
constexpr TimeDelta TimeDelta::FromDays(int days) { constexpr TimeDelta TimeDelta::FromDays(int days) {
return days == std::numeric_limits<int>::max() return (days == std::numeric_limits<int>::max())
? Max() ? Max()
: TimeDelta(days * Time::kMicrosecondsPerDay); : TimeDelta(days * Time::kMicrosecondsPerDay);
} }
// static // static
constexpr TimeDelta TimeDelta::FromHours(int hours) { constexpr TimeDelta TimeDelta::FromHours(int hours) {
return hours == std::numeric_limits<int>::max() return (hours == std::numeric_limits<int>::max())
? Max() ? Max()
: TimeDelta(hours * Time::kMicrosecondsPerHour); : TimeDelta(hours * Time::kMicrosecondsPerHour);
} }
// static // static
constexpr TimeDelta TimeDelta::FromMinutes(int minutes) { constexpr TimeDelta TimeDelta::FromMinutes(int minutes) {
return minutes == std::numeric_limits<int>::max() return (minutes == std::numeric_limits<int>::max())
? Max() ? Max()
: TimeDelta(minutes * Time::kMicrosecondsPerMinute); : TimeDelta(minutes * Time::kMicrosecondsPerMinute);
} }
// static // static
constexpr TimeDelta TimeDelta::FromSeconds(int64_t secs) { constexpr TimeDelta TimeDelta::FromSecondsD(double secs) {
return FromProduct(secs, Time::kMicrosecondsPerSecond); return TimeDelta(
saturated_cast<int64_t>(secs * Time::kMicrosecondsPerSecond));
} }
// static // static
constexpr TimeDelta TimeDelta::FromMilliseconds(int64_t ms) { constexpr TimeDelta TimeDelta::FromSeconds(int64_t secs) {
return FromProduct(ms, Time::kMicrosecondsPerMillisecond); return TimeDelta(int64_t{base::ClampMul(secs, Time::kMicrosecondsPerSecond)});
} }
// static // static
constexpr TimeDelta TimeDelta::FromMicroseconds(int64_t us) { constexpr TimeDelta TimeDelta::FromMillisecondsD(double ms) {
return TimeDelta(us); return TimeDelta(
saturated_cast<int64_t>(ms * Time::kMicrosecondsPerMillisecond));
} }
// static // static
constexpr TimeDelta TimeDelta::FromNanoseconds(int64_t ns) { constexpr TimeDelta TimeDelta::FromMilliseconds(int64_t ms) {
return TimeDelta(ns / Time::kNanosecondsPerMicrosecond); return TimeDelta(
int64_t{base::ClampMul(ms, Time::kMicrosecondsPerMillisecond)});
} }
// static // static
constexpr TimeDelta TimeDelta::FromSecondsD(double secs) { constexpr TimeDelta TimeDelta::FromMicrosecondsD(double us) {
return FromDouble(secs * Time::kMicrosecondsPerSecond); return TimeDelta(saturated_cast<int64_t>(us));
} }
// static // static
constexpr TimeDelta TimeDelta::FromMillisecondsD(double ms) { constexpr TimeDelta TimeDelta::FromMicroseconds(int64_t us) {
return FromDouble(ms * Time::kMicrosecondsPerMillisecond); return TimeDelta(us);
} }
// static // static
constexpr TimeDelta TimeDelta::FromMicrosecondsD(double us) { constexpr TimeDelta TimeDelta::FromNanosecondsD(double ns) {
return FromDouble(us); return TimeDelta(
saturated_cast<int64_t>(ns / Time::kNanosecondsPerMicrosecond));
} }
// static // static
constexpr TimeDelta TimeDelta::FromNanosecondsD(double ns) { constexpr TimeDelta TimeDelta::FromNanoseconds(int64_t ns) {
return FromDouble(ns / Time::kNanosecondsPerMicrosecond); return TimeDelta(ns / Time::kNanosecondsPerMicrosecond);
} }
// static // static
...@@ -938,22 +909,6 @@ constexpr TimeDelta TimeDelta::Min() { ...@@ -938,22 +909,6 @@ constexpr TimeDelta TimeDelta::Min() {
return TimeDelta(std::numeric_limits<int64_t>::min()); return TimeDelta(std::numeric_limits<int64_t>::min());
} }
// static
constexpr TimeDelta TimeDelta::FromDouble(double value) {
return TimeDelta(saturated_cast<int64_t>(value));
}
// static
constexpr TimeDelta TimeDelta::FromProduct(int64_t value,
int64_t positive_value) {
DCHECK_GT(positive_value, 0);
if (value > (std::numeric_limits<int64_t>::max() / positive_value))
return Max();
return (value < (std::numeric_limits<int64_t>::min() / positive_value))
? Min()
: TimeDelta(value * positive_value);
}
// For logging use only. // For logging use only.
BASE_EXPORT std::ostream& operator<<(std::ostream& os, Time time); BASE_EXPORT std::ostream& operator<<(std::ostream& os, Time time);
......
...@@ -33,7 +33,7 @@ Time TimeNowFromSystemTimeIgnoringOverride() { ...@@ -33,7 +33,7 @@ Time TimeNowFromSystemTimeIgnoringOverride() {
namespace subtle { namespace subtle {
TimeTicks TimeTicksNowIgnoringOverride() { TimeTicks TimeTicksNowIgnoringOverride() {
const zx_time_t nanos_since_boot = zx_clock_get_monotonic(); const zx_time_t nanos_since_boot = zx_clock_get_monotonic();
CHECK(nanos_since_boot != 0); CHECK_NE(0, nanos_since_boot);
return TimeTicks::FromZxTime(nanos_since_boot); return TimeTicks::FromZxTime(nanos_since_boot);
} }
} // namespace subtle } // namespace subtle
...@@ -49,11 +49,11 @@ zx_duration_t TimeDelta::ToZxDuration() const { ...@@ -49,11 +49,11 @@ zx_duration_t TimeDelta::ToZxDuration() const {
// static // static
Time Time::FromZxTime(zx_time_t nanos_since_unix_epoch) { Time Time::FromZxTime(zx_time_t nanos_since_unix_epoch) {
return Time::UnixEpoch() + TimeDelta::FromNanoseconds(nanos_since_unix_epoch); return UnixEpoch() + TimeDelta::FromNanoseconds(nanos_since_unix_epoch);
} }
zx_time_t Time::ToZxTime() const { zx_time_t Time::ToZxTime() const {
return (*this - Time::UnixEpoch()).InNanoseconds(); return (*this - UnixEpoch()).InNanoseconds();
} }
// static // static
......
...@@ -156,11 +156,10 @@ Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) { ...@@ -156,11 +156,10 @@ Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) {
"CFAbsoluteTime must have an infinity value"); "CFAbsoluteTime must have an infinity value");
if (t == 0) if (t == 0)
return Time(); // Consider 0 as a null Time. return Time(); // Consider 0 as a null Time.
if (t == std::numeric_limits<CFAbsoluteTime>::infinity()) return (t == std::numeric_limits<CFAbsoluteTime>::infinity())
return Max(); ? Max()
return Time(static_cast<int64_t>((t + kCFAbsoluteTimeIntervalSince1970) * : (UnixEpoch() + TimeDelta::FromSecondsD(double{
kMicrosecondsPerSecond) + t + kCFAbsoluteTimeIntervalSince1970}));
kTimeTToMicrosecondsOffset);
} }
CFAbsoluteTime Time::ToCFAbsoluteTime() const { CFAbsoluteTime Time::ToCFAbsoluteTime() const {
...@@ -168,11 +167,9 @@ CFAbsoluteTime Time::ToCFAbsoluteTime() const { ...@@ -168,11 +167,9 @@ CFAbsoluteTime Time::ToCFAbsoluteTime() const {
"CFAbsoluteTime must have an infinity value"); "CFAbsoluteTime must have an infinity value");
if (is_null()) if (is_null())
return 0; // Consider 0 as a null Time. return 0; // Consider 0 as a null Time.
if (is_max()) return is_max() ? std::numeric_limits<CFAbsoluteTime>::infinity()
return std::numeric_limits<CFAbsoluteTime>::infinity(); : (CFAbsoluteTime{(*this - UnixEpoch()).InSecondsF()} -
return (static_cast<CFAbsoluteTime>(us_ - kTimeTToMicrosecondsOffset) / kCFAbsoluteTimeIntervalSince1970);
kMicrosecondsPerSecond) -
kCFAbsoluteTimeIntervalSince1970;
} }
// TimeDelta ------------------------------------------------------------------ // TimeDelta ------------------------------------------------------------------
......
...@@ -1385,8 +1385,8 @@ TEST(TimeDelta, FromAndIn) { ...@@ -1385,8 +1385,8 @@ TEST(TimeDelta, FromAndIn) {
TimeDelta::FromMillisecondsD(2.5) == TimeDelta::FromMicroseconds(2500), TimeDelta::FromMillisecondsD(2.5) == TimeDelta::FromMicroseconds(2500),
""); "");
EXPECT_EQ(TimeDelta::FromDays(13).InDays(), 13); EXPECT_EQ(TimeDelta::FromDays(13).InDays(), 13);
EXPECT_EQ(TimeDelta::FromHours(13).InHours(), 13); static_assert(TimeDelta::FromHours(13).InHours() == 13, "");
EXPECT_EQ(TimeDelta::FromMinutes(13).InMinutes(), 13); static_assert(TimeDelta::FromMinutes(13).InMinutes() == 13, "");
EXPECT_EQ(TimeDelta::FromSeconds(13).InSeconds(), 13); EXPECT_EQ(TimeDelta::FromSeconds(13).InSeconds(), 13);
EXPECT_EQ(TimeDelta::FromSeconds(13).InSecondsF(), 13.0); EXPECT_EQ(TimeDelta::FromSeconds(13).InSecondsF(), 13.0);
EXPECT_EQ(TimeDelta::FromMilliseconds(13).InMilliseconds(), 13); EXPECT_EQ(TimeDelta::FromMilliseconds(13).InMilliseconds(), 13);
...@@ -1395,20 +1395,21 @@ TEST(TimeDelta, FromAndIn) { ...@@ -1395,20 +1395,21 @@ TEST(TimeDelta, FromAndIn) {
EXPECT_EQ(TimeDelta::FromSecondsD(13.1).InSecondsF(), 13.1); EXPECT_EQ(TimeDelta::FromSecondsD(13.1).InSecondsF(), 13.1);
EXPECT_EQ(TimeDelta::FromMillisecondsD(13.3).InMilliseconds(), 13); EXPECT_EQ(TimeDelta::FromMillisecondsD(13.3).InMilliseconds(), 13);
EXPECT_EQ(TimeDelta::FromMillisecondsD(13.3).InMillisecondsF(), 13.3); EXPECT_EQ(TimeDelta::FromMillisecondsD(13.3).InMillisecondsF(), 13.3);
EXPECT_EQ(TimeDelta::FromMicroseconds(13).InMicroseconds(), 13); static_assert(TimeDelta::FromMicroseconds(13).InMicroseconds() == 13, "");
EXPECT_EQ(TimeDelta::FromMicrosecondsD(13.3).InMicroseconds(), 13); static_assert(TimeDelta::FromMicrosecondsD(13.3).InMicroseconds() == 13, "");
EXPECT_EQ(TimeDelta::FromMillisecondsD(3.45678).InMillisecondsF(), 3.456); EXPECT_EQ(TimeDelta::FromMillisecondsD(3.45678).InMillisecondsF(), 3.456);
EXPECT_EQ(TimeDelta::FromNanoseconds(12345).InNanoseconds(), 12000); static_assert(TimeDelta::FromNanoseconds(12345).InNanoseconds() == 12000, "");
EXPECT_EQ(TimeDelta::FromNanosecondsD(12345.678).InNanoseconds(), 12000); static_assert(TimeDelta::FromNanosecondsD(12345.678).InNanoseconds() == 12000,
"");
} }
TEST(TimeDelta, InRoundsTowardsZero) { TEST(TimeDelta, InRoundsTowardsZero) {
EXPECT_EQ(TimeDelta::FromHours(23).InDays(), 0); EXPECT_EQ(TimeDelta::FromHours(23).InDays(), 0);
EXPECT_EQ(TimeDelta::FromHours(-23).InDays(), 0); EXPECT_EQ(TimeDelta::FromHours(-23).InDays(), 0);
EXPECT_EQ(TimeDelta::FromMinutes(59).InHours(), 0); static_assert(TimeDelta::FromMinutes(59).InHours() == 0, "");
EXPECT_EQ(TimeDelta::FromMinutes(-59).InHours(), 0); static_assert(TimeDelta::FromMinutes(-59).InHours() == 0, "");
EXPECT_EQ(TimeDelta::FromSeconds(59).InMinutes(), 0); static_assert(TimeDelta::FromSeconds(59).InMinutes() == 0, "");
EXPECT_EQ(TimeDelta::FromSeconds(-59).InMinutes(), 0); static_assert(TimeDelta::FromSeconds(-59).InMinutes() == 0, "");
EXPECT_EQ(TimeDelta::FromMilliseconds(999).InSeconds(), 0); EXPECT_EQ(TimeDelta::FromMilliseconds(999).InSeconds(), 0);
EXPECT_EQ(TimeDelta::FromMilliseconds(-999).InSeconds(), 0); EXPECT_EQ(TimeDelta::FromMilliseconds(-999).InSeconds(), 0);
EXPECT_EQ(TimeDelta::FromMicroseconds(999).InMilliseconds(), 0); EXPECT_EQ(TimeDelta::FromMicroseconds(999).InMilliseconds(), 0);
...@@ -1602,8 +1603,8 @@ TEST(TimeDelta, MaxConversions) { ...@@ -1602,8 +1603,8 @@ TEST(TimeDelta, MaxConversions) {
static_assert(kMax.ToInternalValue() == std::numeric_limits<int64_t>::max(), static_assert(kMax.ToInternalValue() == std::numeric_limits<int64_t>::max(),
""); "");
EXPECT_EQ(kMax.InDays(), std::numeric_limits<int>::max()); EXPECT_EQ(kMax.InDays(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InHours(), std::numeric_limits<int>::max()); static_assert(kMax.InHours() == std::numeric_limits<int>::max(), "");
EXPECT_EQ(kMax.InMinutes(), std::numeric_limits<int>::max()); static_assert(kMax.InMinutes() == std::numeric_limits<int>::max(), "");
EXPECT_EQ(kMax.InSecondsF(), std::numeric_limits<double>::infinity()); EXPECT_EQ(kMax.InSecondsF(), std::numeric_limits<double>::infinity());
EXPECT_EQ(kMax.InSeconds(), std::numeric_limits<int64_t>::max()); EXPECT_EQ(kMax.InSeconds(), std::numeric_limits<int64_t>::max());
EXPECT_EQ(kMax.InMillisecondsF(), std::numeric_limits<double>::infinity()); EXPECT_EQ(kMax.InMillisecondsF(), std::numeric_limits<double>::infinity());
...@@ -1693,8 +1694,8 @@ TEST(TimeDelta, MinConversions) { ...@@ -1693,8 +1694,8 @@ TEST(TimeDelta, MinConversions) {
constexpr TimeDelta kMin = TimeDelta::Min(); constexpr TimeDelta kMin = TimeDelta::Min();
EXPECT_EQ(kMin.InDays(), std::numeric_limits<int>::min()); EXPECT_EQ(kMin.InDays(), std::numeric_limits<int>::min());
EXPECT_EQ(kMin.InHours(), std::numeric_limits<int>::min()); static_assert(kMin.InHours() == std::numeric_limits<int>::min(), "");
EXPECT_EQ(kMin.InMinutes(), std::numeric_limits<int>::min()); static_assert(kMin.InMinutes() == std::numeric_limits<int>::min(), "");
EXPECT_EQ(kMin.InSecondsF(), -std::numeric_limits<double>::infinity()); EXPECT_EQ(kMin.InSecondsF(), -std::numeric_limits<double>::infinity());
EXPECT_EQ(kMin.InSeconds(), std::numeric_limits<int64_t>::min()); EXPECT_EQ(kMin.InSeconds(), std::numeric_limits<int64_t>::min());
EXPECT_EQ(kMin.InMillisecondsF(), -std::numeric_limits<double>::infinity()); EXPECT_EQ(kMin.InMillisecondsF(), -std::numeric_limits<double>::infinity());
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include <mmsystem.h> #include <mmsystem.h>
#include <stdint.h> #include <stdint.h>
#include <atomic>
#include "base/atomicops.h" #include "base/atomicops.h"
#include "base/bit_cast.h" #include "base/bit_cast.h"
#include "base/check_op.h" #include "base/check_op.h"
...@@ -341,13 +343,8 @@ bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) { ...@@ -341,13 +343,8 @@ bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
success = !!SystemTimeToFileTime(&st, &ft); success = !!SystemTimeToFileTime(&st, &ft);
} }
if (!success) { *time = Time(success ? FileTimeToMicroseconds(ft) : 0);
*time = Time(0); return success;
return false;
}
*time = Time(FileTimeToMicroseconds(ft));
return true;
} }
void Time::Explode(bool is_local, Exploded* exploded) const { void Time::Explode(bool is_local, Exploded* exploded) const {
...@@ -378,7 +375,6 @@ void Time::Explode(bool is_local, Exploded* exploded) const { ...@@ -378,7 +375,6 @@ void Time::Explode(bool is_local, Exploded* exploded) const {
} }
if (!success) { if (!success) {
NOTREACHED() << "Unable to convert time, don't know why";
ZeroMemory(exploded, sizeof(*exploded)); ZeroMemory(exploded, sizeof(*exploded));
return; return;
} }
...@@ -515,15 +511,10 @@ TimeTicksNowFunction g_time_ticks_now_ignoring_override_function = ...@@ -515,15 +511,10 @@ TimeTicksNowFunction g_time_ticks_now_ignoring_override_function =
&InitialNowFunction; &InitialNowFunction;
int64_t g_qpc_ticks_per_second = 0; int64_t g_qpc_ticks_per_second = 0;
// As of January 2015, use of <atomic> is forbidden in Chromium code. This is
// what std::atomic_thread_fence does on Windows on all Intel architectures when
// the memory_order argument is anything but std::memory_order_seq_cst:
#define ATOMIC_THREAD_FENCE(memory_order) _ReadWriteBarrier();
TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) {
// Ensure that the assignment to |g_qpc_ticks_per_second|, made in // Ensure that the assignment to |g_qpc_ticks_per_second|, made in
// InitializeNowFunctionPointer(), has happened by this point. // InitializeNowFunctionPointer(), has happened by this point.
ATOMIC_THREAD_FENCE(memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
DCHECK_GT(g_qpc_ticks_per_second, 0); DCHECK_GT(g_qpc_ticks_per_second, 0);
...@@ -562,13 +553,11 @@ void InitializeNowFunctionPointer() { ...@@ -562,13 +553,11 @@ void InitializeNowFunctionPointer() {
// //
// Otherwise, Now uses the high-resolution QPC clock. As of 21 August 2015, // Otherwise, Now uses the high-resolution QPC clock. As of 21 August 2015,
// ~72% of users fall within this category. // ~72% of users fall within this category.
TimeTicksNowFunction now_function;
CPU cpu; CPU cpu;
if (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter()) { const TimeTicksNowFunction now_function =
now_function = &RolloverProtectedNow; (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter())
} else { ? &RolloverProtectedNow
now_function = &QPCNow; : &QPCNow;
}
// Threading note 1: In an unlikely race condition, it's possible for two or // Threading note 1: In an unlikely race condition, it's possible for two or
// more threads to enter InitializeNowFunctionPointer() in parallel. This is // more threads to enter InitializeNowFunctionPointer() in parallel. This is
...@@ -580,7 +569,7 @@ void InitializeNowFunctionPointer() { ...@@ -580,7 +569,7 @@ void InitializeNowFunctionPointer() {
// assignment to |g_qpc_ticks_per_second| happens before the function pointers // assignment to |g_qpc_ticks_per_second| happens before the function pointers
// are changed. // are changed.
g_qpc_ticks_per_second = ticks_per_sec.QuadPart; g_qpc_ticks_per_second = ticks_per_sec.QuadPart;
ATOMIC_THREAD_FENCE(memory_order_release); std::atomic_thread_fence(std::memory_order_release);
// Also set g_time_ticks_now_function to avoid the additional indirection via // Also set g_time_ticks_now_function to avoid the additional indirection via
// TimeTicksNowIgnoringOverride() for future calls to TimeTicks::Now(). But // TimeTicksNowIgnoringOverride() for future calls to TimeTicks::Now(). But
// g_time_ticks_now_function may have already be overridden. // g_time_ticks_now_function may have already be overridden.
...@@ -642,8 +631,8 @@ bool TimeTicks::IsConsistentAcrossProcesses() { ...@@ -642,8 +631,8 @@ bool TimeTicks::IsConsistentAcrossProcesses() {
// static // static
TimeTicks::Clock TimeTicks::GetClock() { TimeTicks::Clock TimeTicks::GetClock() {
return IsHighResolution() ? return IsHighResolution() ? Clock::WIN_QPC
Clock::WIN_QPC : Clock::WIN_ROLLOVER_PROTECTED_TIME_GET_TIME; : Clock::WIN_ROLLOVER_PROTECTED_TIME_GET_TIME;
} }
// ThreadTicks ---------------------------------------------------------------- // ThreadTicks ----------------------------------------------------------------
...@@ -670,23 +659,24 @@ ThreadTicks ThreadTicks::GetForThread( ...@@ -670,23 +659,24 @@ ThreadTicks ThreadTicks::GetForThread(
::GetThreadTimes(thread_handle.platform_handle(), &creation_time, &exit_time, ::GetThreadTimes(thread_handle.platform_handle(), &creation_time, &exit_time,
&kernel_time, &user_time); &kernel_time, &user_time);
int64_t us = FileTimeToMicroseconds(user_time); const int64_t us = FileTimeToMicroseconds(user_time);
return ThreadTicks(us);
#else #else
// Get the number of TSC ticks used by the current thread. // Get the number of TSC ticks used by the current thread.
ULONG64 thread_cycle_time = 0; ULONG64 thread_cycle_time = 0;
::QueryThreadCycleTime(thread_handle.platform_handle(), &thread_cycle_time); ::QueryThreadCycleTime(thread_handle.platform_handle(), &thread_cycle_time);
// Get the frequency of the TSC. // Get the frequency of the TSC.
double tsc_ticks_per_second = TSCTicksPerSecond(); const double tsc_ticks_per_second = TSCTicksPerSecond();
if (tsc_ticks_per_second == 0) if (tsc_ticks_per_second == 0)
return ThreadTicks(); return ThreadTicks();
// Return the CPU time of the current thread. // Return the CPU time of the current thread.
double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second; const double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second;
return ThreadTicks( const int64_t us =
static_cast<int64_t>(thread_time_seconds * Time::kMicrosecondsPerSecond)); static_cast<int64_t>(thread_time_seconds * Time::kMicrosecondsPerSecond);
#endif #endif
return ThreadTicks(us);
} }
// static // static
...@@ -717,7 +707,7 @@ double ThreadTicks::TSCTicksPerSecond() { ...@@ -717,7 +707,7 @@ double ThreadTicks::TSCTicksPerSecond() {
// Increase the thread priority to reduces the chances of having a context // Increase the thread priority to reduces the chances of having a context
// switch during a reading of the TSC and the performance counter. // switch during a reading of the TSC and the performance counter.
int previous_priority = ::GetThreadPriority(::GetCurrentThread()); const int previous_priority = ::GetThreadPriority(::GetCurrentThread());
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST); ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
// The first time that this function is called, make an initial reading of the // The first time that this function is called, make an initial reading of the
...@@ -728,8 +718,8 @@ double ThreadTicks::TSCTicksPerSecond() { ...@@ -728,8 +718,8 @@ double ThreadTicks::TSCTicksPerSecond() {
// Make a another reading of the TSC and the performance counter every time // Make a another reading of the TSC and the performance counter every time
// that this function is called. // that this function is called.
uint64_t tsc_now = __rdtsc(); const uint64_t tsc_now = __rdtsc();
uint64_t perf_counter_now = QPCNowRaw(); const uint64_t perf_counter_now = QPCNowRaw();
// Reset the thread priority. // Reset the thread priority.
::SetThreadPriority(::GetCurrentThread(), previous_priority); ::SetThreadPriority(::GetCurrentThread(), previous_priority);
...@@ -746,20 +736,18 @@ double ThreadTicks::TSCTicksPerSecond() { ...@@ -746,20 +736,18 @@ double ThreadTicks::TSCTicksPerSecond() {
LARGE_INTEGER perf_counter_frequency = {}; LARGE_INTEGER perf_counter_frequency = {};
::QueryPerformanceFrequency(&perf_counter_frequency); ::QueryPerformanceFrequency(&perf_counter_frequency);
DCHECK_GE(perf_counter_now, perf_counter_initial); DCHECK_GE(perf_counter_now, perf_counter_initial);
uint64_t perf_counter_ticks = perf_counter_now - perf_counter_initial; const uint64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
double elapsed_time_seconds = const double elapsed_time_seconds =
perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart); perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart);
static constexpr double kMinimumEvaluationPeriodSeconds = 0.05; constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds) if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
return 0; return 0;
// Compute the frequency of the TSC. // Compute the frequency of the TSC.
DCHECK_GE(tsc_now, tsc_initial); DCHECK_GE(tsc_now, tsc_initial);
uint64_t tsc_ticks = tsc_now - tsc_initial; const uint64_t tsc_ticks = tsc_now - tsc_initial;
tsc_ticks_per_second = tsc_ticks / elapsed_time_seconds; return tsc_ticks / elapsed_time_seconds;
return tsc_ticks_per_second;
} }
#endif // defined(ARCH_CPU_ARM64) #endif // defined(ARCH_CPU_ARM64)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <stdint.h> #include <stdint.h>
#include <windows.foundation.h> #include <windows.foundation.h>
#include <algorithm>
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <vector> #include <vector>
...@@ -151,23 +152,19 @@ TEST(TimeTicks, SubMillisecondTimers) { ...@@ -151,23 +152,19 @@ TEST(TimeTicks, SubMillisecondTimers) {
if (!TimeTicks::IsHighResolution()) if (!TimeTicks::IsHighResolution())
return; return;
const int kRetries = 1000;
bool saw_submillisecond_timer = false;
// Run kRetries attempts to see a sub-millisecond timer. // Run kRetries attempts to see a sub-millisecond timer.
constexpr int kRetries = 1000;
for (int index = 0; index < kRetries; index++) { for (int index = 0; index < kRetries; index++) {
TimeTicks last_time = TimeTicks::Now(); const TimeTicks start_time = TimeTicks::Now();
TimeDelta delta; TimeDelta delta;
// Spin until the clock has detected a change. // Spin until the clock has detected a change.
do { do {
delta = TimeTicks::Now() - last_time; delta = TimeTicks::Now() - start_time;
} while (delta.InMicroseconds() == 0); } while (delta.is_zero());
if (delta.InMicroseconds() < 1000) { if (!delta.InMilliseconds())
saw_submillisecond_timer = true; return;
break;
}
} }
EXPECT_TRUE(saw_submillisecond_timer); ADD_FAILURE() << "Never saw a sub-millisecond timer.";
} }
TEST(TimeTicks, TimeGetTimeCaps) { TEST(TimeTicks, TimeGetTimeCaps) {
...@@ -324,7 +321,7 @@ TEST(TimeTicks, FromQPCValue) { ...@@ -324,7 +321,7 @@ TEST(TimeTicks, FromQPCValue) {
ticks_per_second; ticks_per_second;
const TimeTicks converted_value = TimeTicks::FromQPCValue(ticks); const TimeTicks converted_value = TimeTicks::FromQPCValue(ticks);
const double converted_microseconds_since_origin = const double converted_microseconds_since_origin =
static_cast<double>((converted_value - TimeTicks()).InMicroseconds()); (converted_value - TimeTicks()).InMicrosecondsF();
// When we test with very large numbers we end up in a range where adjacent // When we test with very large numbers we end up in a range where adjacent
// double values are far apart - 512.0 apart in one test failure. In that // double values are far apart - 512.0 apart in one test failure. In that
// situation it makes no sense for our epsilon to be 1.0 - it should be // situation it makes no sense for our epsilon to be 1.0 - it should be
...@@ -338,9 +335,7 @@ TEST(TimeTicks, FromQPCValue) { ...@@ -338,9 +335,7 @@ TEST(TimeTicks, FromQPCValue) {
// slightly larger than 1.0, even when the converted value is perfect. This // slightly larger than 1.0, even when the converted value is perfect. This
// epsilon value was chosen because it is slightly larger than the error // epsilon value was chosen because it is slightly larger than the error
// seen in a test failure caused by the double rounding. // seen in a test failure caused by the double rounding.
const double min_epsilon = 1.002; epsilon = std::max(epsilon, 1.002);
if (epsilon < min_epsilon)
epsilon = min_epsilon;
EXPECT_NEAR(expected_microseconds_since_origin, EXPECT_NEAR(expected_microseconds_since_origin,
converted_microseconds_since_origin, epsilon) converted_microseconds_since_origin, epsilon)
<< "ticks=" << ticks << ", to be converted via logic path: " << "ticks=" << ticks << ", to be converted via logic path: "
......
...@@ -31,12 +31,10 @@ const base::TimeDelta kSignificantlyAboveThresholdDelayMs = ...@@ -31,12 +31,10 @@ const base::TimeDelta kSignificantlyAboveThresholdDelayMs =
class FakeTickClock : public base::TickClock { class FakeTickClock : public base::TickClock {
public: public:
FakeTickClock() = default;
// The |dns_resolution_delay| fakes the duration of a DNS resolution. // The |dns_resolution_delay| fakes the duration of a DNS resolution.
explicit FakeTickClock(const base::TimeDelta& dns_resolution_delay) explicit FakeTickClock(
: current_time_(base::TimeTicks::Now()), const base::TimeDelta& dns_resolution_delay = base::TimeDelta())
dns_resolution_delay_(dns_resolution_delay) {} : dns_resolution_delay_(dns_resolution_delay) {}
~FakeTickClock() override = default; ~FakeTickClock() override = default;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment