mirror of https://github.com/grpc/grpc.git
grpc_millis -> Timestamp/Duration (#28119)
* wip * Automated change: Fix sanity tests * fixes * progress * progress * grpc compiles * Automated change: Fix sanity tests * fixing tests * x * progress * better code * Automated change: Fix sanity tests * progress * progress * windows fix * Make Duration metadata trivial * better message * fix * Automated change: Fix sanity tests * fix * fix * fix * fix * Automated change: Fix sanity tests * Automated change: Fix sanity tests * fix * progress * fixes * fix * fix * spam * un-disable errantly disabled tests * gain insight * Automated change: Fix sanity tests * fixes * fixes * fix * debug * tweak * fix * fix timeout * fix comment * fixes * x * better test * tests * Automated change: Fix sanity tests * missed file * fix * x * fix * fix * fix * fix * Automated change: Fix sanity tests * fix * merge * Automated change: Fix sanity tests * fix Co-authored-by: ctiller <ctiller@users.noreply.github.com>pull/28968/head^2
parent
0966536dc1
commit
5fc3ff8203
205 changed files with 2285 additions and 1580 deletions
@ -0,0 +1,186 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/gprpp/time.h" |
||||
|
||||
#include <atomic> |
||||
#include <cstdint> |
||||
#include <limits> |
||||
#include <string> |
||||
|
||||
#include <grpc/impl/codegen/gpr_types.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
|
||||
std::atomic<int64_t> g_process_epoch_seconds; |
||||
std::atomic<gpr_cycle_counter> g_process_epoch_cycles; |
||||
|
||||
GPR_ATTRIBUTE_NOINLINE std::pair<int64_t, gpr_cycle_counter> InitTime() { |
||||
gpr_cycle_counter cycles_start; |
||||
gpr_cycle_counter cycles_end; |
||||
int64_t process_epoch_seconds; |
||||
|
||||
// Check the current time... if we end up with zero, try again after 100ms.
|
||||
// If it doesn't advance after sleeping for 1100ms, crash the process.
|
||||
for (int i = 0; i < 11; i++) { |
||||
cycles_start = gpr_get_cycle_counter(); |
||||
gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); |
||||
cycles_end = gpr_get_cycle_counter(); |
||||
process_epoch_seconds = now.tv_sec - 1; |
||||
if (process_epoch_seconds != 0) { |
||||
break; |
||||
} |
||||
gpr_sleep_until(gpr_time_add(now, gpr_time_from_millis(100, GPR_TIMESPAN))); |
||||
} |
||||
|
||||
// Time does not seem to be increasing from zero...
|
||||
GPR_ASSERT(process_epoch_seconds != 0); |
||||
int64_t expected = 0; |
||||
gpr_cycle_counter process_epoch_cycles = (cycles_start + cycles_end) / 2; |
||||
GPR_ASSERT(process_epoch_cycles != 0); |
||||
if (!g_process_epoch_seconds.compare_exchange_strong( |
||||
expected, process_epoch_seconds, std::memory_order_relaxed, |
||||
std::memory_order_relaxed)) { |
||||
process_epoch_seconds = expected; |
||||
do { |
||||
process_epoch_cycles = |
||||
g_process_epoch_cycles.load(std::memory_order_relaxed); |
||||
} while (process_epoch_cycles == 0); |
||||
} else { |
||||
g_process_epoch_cycles.store(process_epoch_cycles, |
||||
std::memory_order_relaxed); |
||||
} |
||||
return std::make_pair(process_epoch_seconds, process_epoch_cycles); |
||||
} |
||||
|
||||
gpr_timespec StartTime() { |
||||
int64_t sec = g_process_epoch_seconds.load(std::memory_order_relaxed); |
||||
if (GPR_UNLIKELY(sec == 0)) sec = InitTime().first; |
||||
return {sec, 0, GPR_CLOCK_MONOTONIC}; |
||||
} |
||||
|
||||
gpr_cycle_counter StartCycleCounter() { |
||||
gpr_cycle_counter cycles = |
||||
g_process_epoch_cycles.load(std::memory_order_relaxed); |
||||
if (GPR_UNLIKELY(cycles == 0)) cycles = InitTime().second; |
||||
return cycles; |
||||
} |
||||
|
||||
gpr_timespec MillisecondsAsTimespec(int64_t millis, gpr_clock_type clock_type) { |
||||
// special-case infinities as Timestamp can be 32bit on some
|
||||
// platforms while gpr_time_from_millis always takes an int64_t.
|
||||
if (millis == std::numeric_limits<int64_t>::max()) { |
||||
return gpr_inf_future(clock_type); |
||||
} |
||||
if (millis == std::numeric_limits<int64_t>::min()) { |
||||
return gpr_inf_past(clock_type); |
||||
} |
||||
|
||||
if (clock_type == GPR_TIMESPAN) { |
||||
return gpr_time_from_millis(millis, GPR_TIMESPAN); |
||||
} |
||||
return gpr_time_add(gpr_convert_clock_type(StartTime(), clock_type), |
||||
gpr_time_from_millis(millis, GPR_TIMESPAN)); |
||||
} |
||||
|
||||
int64_t TimespanToMillisRoundUp(gpr_timespec ts) { |
||||
GPR_ASSERT(ts.clock_type == GPR_TIMESPAN); |
||||
double x = GPR_MS_PER_SEC * static_cast<double>(ts.tv_sec) + |
||||
static_cast<double>(ts.tv_nsec) / GPR_NS_PER_MS + |
||||
static_cast<double>(GPR_NS_PER_SEC - 1) / |
||||
static_cast<double>(GPR_NS_PER_SEC); |
||||
if (x <= static_cast<double>(std::numeric_limits<int64_t>::min())) { |
||||
return std::numeric_limits<int64_t>::min(); |
||||
} |
||||
if (x >= static_cast<double>(std::numeric_limits<int64_t>::max())) { |
||||
return std::numeric_limits<int64_t>::max(); |
||||
} |
||||
return static_cast<int64_t>(x); |
||||
} |
||||
|
||||
int64_t TimespanToMillisRoundDown(gpr_timespec ts) { |
||||
GPR_ASSERT(ts.clock_type == GPR_TIMESPAN); |
||||
double x = GPR_MS_PER_SEC * static_cast<double>(ts.tv_sec) + |
||||
static_cast<double>(ts.tv_nsec) / GPR_NS_PER_MS; |
||||
if (x <= static_cast<double>(std::numeric_limits<int64_t>::min())) { |
||||
return std::numeric_limits<int64_t>::min(); |
||||
} |
||||
if (x >= static_cast<double>(std::numeric_limits<int64_t>::max())) { |
||||
return std::numeric_limits<int64_t>::max(); |
||||
} |
||||
return static_cast<int64_t>(x); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
Timestamp Timestamp::FromTimespecRoundUp(gpr_timespec ts) { |
||||
return FromMillisecondsAfterProcessEpoch(TimespanToMillisRoundUp(gpr_time_sub( |
||||
gpr_convert_clock_type(ts, GPR_CLOCK_MONOTONIC), StartTime()))); |
||||
} |
||||
|
||||
Timestamp Timestamp::FromTimespecRoundDown(gpr_timespec ts) { |
||||
return FromMillisecondsAfterProcessEpoch( |
||||
TimespanToMillisRoundDown(gpr_time_sub( |
||||
gpr_convert_clock_type(ts, GPR_CLOCK_MONOTONIC), StartTime()))); |
||||
} |
||||
|
||||
Timestamp Timestamp::FromCycleCounterRoundUp(gpr_cycle_counter c) { |
||||
return Timestamp::FromMillisecondsAfterProcessEpoch( |
||||
TimespanToMillisRoundUp(gpr_cycle_counter_sub(c, StartCycleCounter()))); |
||||
} |
||||
|
||||
Timestamp Timestamp::FromCycleCounterRoundDown(gpr_cycle_counter c) { |
||||
return Timestamp::FromMillisecondsAfterProcessEpoch( |
||||
TimespanToMillisRoundDown(gpr_cycle_counter_sub(c, StartCycleCounter()))); |
||||
} |
||||
|
||||
gpr_timespec Timestamp::as_timespec(gpr_clock_type clock_type) const { |
||||
return MillisecondsAsTimespec(millis_, clock_type); |
||||
} |
||||
|
||||
std::string Timestamp::ToString() const { |
||||
return "@" + std::to_string(millis_) + "ms"; |
||||
} |
||||
|
||||
gpr_timespec Duration::as_timespec() const { |
||||
return MillisecondsAsTimespec(millis_, GPR_TIMESPAN); |
||||
} |
||||
|
||||
Duration Duration::FromTimespec(gpr_timespec t) { |
||||
return Duration::Milliseconds(TimespanToMillisRoundUp(t)); |
||||
} |
||||
|
||||
std::string Duration::ToString() const { |
||||
return std::to_string(millis_) + "ms"; |
||||
} |
||||
|
||||
void TestOnlySetProcessEpoch(gpr_timespec epoch) { |
||||
g_process_epoch_seconds.store( |
||||
gpr_convert_clock_type(epoch, GPR_CLOCK_MONOTONIC).tv_sec); |
||||
} |
||||
|
||||
std::ostream& operator<<(std::ostream& out, Timestamp timestamp) { |
||||
return out << timestamp.ToString(); |
||||
} |
||||
|
||||
std::ostream& operator<<(std::ostream& out, Duration duration) { |
||||
return out << duration.ToString(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,292 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_CORE_LIB_GPRPP_TIME_H |
||||
#define GRPC_CORE_LIB_GPRPP_TIME_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include <cstdint> |
||||
#include <limits> |
||||
#include <ostream> |
||||
#include <string> |
||||
|
||||
#include <grpc/support/time.h> |
||||
|
||||
#include "src/core/lib/gpr/time_precise.h" |
||||
#include "src/core/lib/gpr/useful.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace time_detail { |
||||
|
||||
inline int64_t MillisAdd(int64_t a, int64_t b) { |
||||
if (a == std::numeric_limits<int64_t>::max() || |
||||
b == std::numeric_limits<int64_t>::max()) { |
||||
return std::numeric_limits<int64_t>::max(); |
||||
} |
||||
if (a == std::numeric_limits<int64_t>::min() || |
||||
b == std::numeric_limits<int64_t>::min()) { |
||||
return std::numeric_limits<int64_t>::min(); |
||||
} |
||||
return SaturatingAdd(a, b); |
||||
} |
||||
|
||||
constexpr inline int64_t MillisMul(int64_t millis, int64_t mul) { |
||||
return millis >= std::numeric_limits<int64_t>::max() / mul |
||||
? std::numeric_limits<int64_t>::max() |
||||
: millis <= std::numeric_limits<int64_t>::min() / mul |
||||
? std::numeric_limits<int64_t>::min() |
||||
: millis * mul; |
||||
} |
||||
|
||||
} // namespace time_detail
|
||||
|
||||
class Duration; |
||||
|
||||
// Timestamp represents a discrete point in time.
|
||||
class Timestamp { |
||||
public: |
||||
constexpr Timestamp() = default; |
||||
// Constructs a Timestamp from a gpr_timespec.
|
||||
static Timestamp FromTimespecRoundDown(gpr_timespec t); |
||||
static Timestamp FromTimespecRoundUp(gpr_timespec t); |
||||
|
||||
// Construct a Timestamp from a gpr_cycle_counter.
|
||||
static Timestamp FromCycleCounterRoundUp(gpr_cycle_counter c); |
||||
static Timestamp FromCycleCounterRoundDown(gpr_cycle_counter c); |
||||
|
||||
static constexpr Timestamp FromMillisecondsAfterProcessEpoch(int64_t millis) { |
||||
return Timestamp(millis); |
||||
} |
||||
|
||||
static constexpr Timestamp ProcessEpoch() { return Timestamp(0); } |
||||
|
||||
static constexpr Timestamp InfFuture() { |
||||
return Timestamp(std::numeric_limits<int64_t>::max()); |
||||
} |
||||
|
||||
static constexpr Timestamp InfPast() { |
||||
return Timestamp(std::numeric_limits<int64_t>::min()); |
||||
} |
||||
|
||||
constexpr bool operator==(Timestamp other) const { |
||||
return millis_ == other.millis_; |
||||
} |
||||
constexpr bool operator!=(Timestamp other) const { |
||||
return millis_ != other.millis_; |
||||
} |
||||
constexpr bool operator<(Timestamp other) const { |
||||
return millis_ < other.millis_; |
||||
} |
||||
constexpr bool operator<=(Timestamp other) const { |
||||
return millis_ <= other.millis_; |
||||
} |
||||
constexpr bool operator>(Timestamp other) const { |
||||
return millis_ > other.millis_; |
||||
} |
||||
constexpr bool operator>=(Timestamp other) const { |
||||
return millis_ >= other.millis_; |
||||
} |
||||
Timestamp& operator+=(Duration duration); |
||||
|
||||
bool is_process_epoch() const { return millis_ == 0; } |
||||
|
||||
uint64_t milliseconds_after_process_epoch() const { return millis_; } |
||||
|
||||
gpr_timespec as_timespec(gpr_clock_type type) const; |
||||
|
||||
std::string ToString() const; |
||||
|
||||
private: |
||||
explicit constexpr Timestamp(int64_t millis) : millis_(millis) {} |
||||
|
||||
int64_t millis_ = 0; |
||||
}; |
||||
|
||||
// Duration represents a span of time.
|
||||
class Duration { |
||||
public: |
||||
constexpr Duration() : millis_(0) {} |
||||
|
||||
static Duration FromTimespec(gpr_timespec t); |
||||
static Duration FromSecondsAndNanoseconds(int64_t seconds, int32_t nanos); |
||||
static Duration FromSecondsAsDouble(double seconds); |
||||
|
||||
static constexpr Duration Zero() { return Duration(0); } |
||||
|
||||
// Smallest representatable positive duration.
|
||||
static constexpr Duration Epsilon() { return Duration(1); } |
||||
|
||||
static constexpr Duration NegativeInfinity() { |
||||
return Duration(std::numeric_limits<int64_t>::min()); |
||||
} |
||||
|
||||
static constexpr Duration Infinity() { |
||||
return Duration(std::numeric_limits<int64_t>::max()); |
||||
} |
||||
|
||||
static constexpr Duration Hours(int64_t hours) { |
||||
return Minutes(time_detail::MillisMul(hours, 60)); |
||||
} |
||||
|
||||
static constexpr Duration Minutes(int64_t minutes) { |
||||
return Seconds(time_detail::MillisMul(minutes, 60)); |
||||
} |
||||
|
||||
static constexpr Duration Seconds(int64_t seconds) { |
||||
return Milliseconds(time_detail::MillisMul(seconds, GPR_MS_PER_SEC)); |
||||
} |
||||
|
||||
static constexpr Duration Milliseconds(int64_t millis) { |
||||
return Duration(millis); |
||||
} |
||||
|
||||
static constexpr Duration MicrosecondsRoundDown(int64_t micros) { |
||||
return Duration(micros / GPR_US_PER_MS); |
||||
} |
||||
|
||||
static constexpr Duration NanosecondsRoundDown(int64_t nanos) { |
||||
return Duration(nanos / GPR_NS_PER_MS); |
||||
} |
||||
|
||||
static constexpr Duration MicrosecondsRoundUp(int64_t micros) { |
||||
return Duration(micros / GPR_US_PER_MS + (micros % GPR_US_PER_MS != 0)); |
||||
} |
||||
|
||||
static constexpr Duration NanosecondsRoundUp(int64_t nanos) { |
||||
return Duration(nanos / GPR_NS_PER_MS + (nanos % GPR_NS_PER_MS != 0)); |
||||
} |
||||
|
||||
constexpr bool operator==(Duration other) const { |
||||
return millis_ == other.millis_; |
||||
} |
||||
constexpr bool operator!=(Duration other) const { |
||||
return millis_ != other.millis_; |
||||
} |
||||
constexpr bool operator<(Duration other) const { |
||||
return millis_ < other.millis_; |
||||
} |
||||
constexpr bool operator<=(Duration other) const { |
||||
return millis_ <= other.millis_; |
||||
} |
||||
constexpr bool operator>(Duration other) const { |
||||
return millis_ > other.millis_; |
||||
} |
||||
constexpr bool operator>=(Duration other) const { |
||||
return millis_ >= other.millis_; |
||||
} |
||||
Duration& operator/=(int64_t divisor) { |
||||
if (millis_ == std::numeric_limits<int64_t>::max()) { |
||||
*this = divisor < 0 ? NegativeInfinity() : Infinity(); |
||||
} else if (millis_ == std::numeric_limits<int64_t>::min()) { |
||||
*this = divisor < 0 ? Infinity() : NegativeInfinity(); |
||||
} else { |
||||
millis_ /= divisor; |
||||
} |
||||
return *this; |
||||
} |
||||
Duration& operator+=(Duration other) { |
||||
millis_ += other.millis_; |
||||
return *this; |
||||
} |
||||
|
||||
constexpr int64_t millis() const { return millis_; } |
||||
double seconds() const { return static_cast<double>(millis_) / 1000.0; } |
||||
|
||||
gpr_timespec as_timespec() const; |
||||
|
||||
std::string ToString() const; |
||||
|
||||
private: |
||||
explicit constexpr Duration(int64_t millis) : millis_(millis) {} |
||||
|
||||
int64_t millis_; |
||||
}; |
||||
|
||||
inline Duration operator+(Duration lhs, Duration rhs) { |
||||
return Duration::Milliseconds( |
||||
time_detail::MillisAdd(lhs.millis(), rhs.millis())); |
||||
} |
||||
|
||||
inline Duration operator-(Duration lhs, Duration rhs) { |
||||
return Duration::Milliseconds( |
||||
time_detail::MillisAdd(lhs.millis(), -rhs.millis())); |
||||
} |
||||
|
||||
inline Timestamp operator+(Timestamp lhs, Duration rhs) { |
||||
return Timestamp::FromMillisecondsAfterProcessEpoch(time_detail::MillisAdd( |
||||
lhs.milliseconds_after_process_epoch(), rhs.millis())); |
||||
} |
||||
|
||||
inline Timestamp operator-(Timestamp lhs, Duration rhs) { |
||||
return Timestamp::FromMillisecondsAfterProcessEpoch(time_detail::MillisAdd( |
||||
lhs.milliseconds_after_process_epoch(), -rhs.millis())); |
||||
} |
||||
|
||||
inline Timestamp operator+(Duration lhs, Timestamp rhs) { return rhs + lhs; } |
||||
|
||||
inline Duration operator-(Timestamp lhs, Timestamp rhs) { |
||||
return Duration::Milliseconds( |
||||
time_detail::MillisAdd(lhs.milliseconds_after_process_epoch(), |
||||
-rhs.milliseconds_after_process_epoch())); |
||||
} |
||||
|
||||
inline Duration operator*(Duration lhs, double rhs) { |
||||
if (lhs == Duration::Infinity()) { |
||||
return rhs < 0 ? Duration::NegativeInfinity() : Duration::Infinity(); |
||||
} |
||||
if (lhs == Duration::NegativeInfinity()) { |
||||
return rhs < 0 ? Duration::Infinity() : Duration::NegativeInfinity(); |
||||
} |
||||
return Duration::FromSecondsAsDouble(lhs.millis() * rhs / 1000.0); |
||||
} |
||||
|
||||
inline Duration operator*(double lhs, Duration rhs) { return rhs * lhs; } |
||||
|
||||
inline Duration operator/(Duration lhs, int64_t rhs) { |
||||
lhs /= rhs; |
||||
return lhs; |
||||
} |
||||
|
||||
inline Duration Duration::FromSecondsAndNanoseconds(int64_t seconds, |
||||
int32_t nanos) { |
||||
return Seconds(seconds) + NanosecondsRoundDown(nanos); |
||||
} |
||||
|
||||
inline Duration Duration::FromSecondsAsDouble(double seconds) { |
||||
double millis = seconds * 1000.0; |
||||
if (millis >= static_cast<double>(std::numeric_limits<int64_t>::max())) { |
||||
return Infinity(); |
||||
} |
||||
if (millis <= static_cast<double>(std::numeric_limits<int64_t>::min())) { |
||||
return NegativeInfinity(); |
||||
} |
||||
return Milliseconds(static_cast<int64_t>(millis)); |
||||
} |
||||
|
||||
inline Timestamp& Timestamp::operator+=(Duration duration) { |
||||
return *this = (*this + duration); |
||||
} |
||||
|
||||
void TestOnlySetProcessEpoch(gpr_timespec epoch); |
||||
|
||||
std::ostream& operator<<(std::ostream& out, Timestamp timestamp); |
||||
std::ostream& operator<<(std::ostream& out, Duration duration); |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_CORE_LIB_GPRPP_TIME_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue