blob: cddc4da2563570637d9027ca8a6666fca26618b3 [file] [log] [blame]
// 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 <chrono>
#include <cstdint>
#include <limits>
#include <string>
#include <utility>
#include "absl/strings/str_format.h"
#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 = 0;
gpr_cycle_counter cycles_end = 0;
int64_t process_epoch_seconds = 0;
// 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 {
if (millis_ == std::numeric_limits<int64_t>::max()) {
return "@∞";
}
if (millis_ == std::numeric_limits<int64_t>::min()) {
return "@-∞";
}
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 {
if (millis_ == std::numeric_limits<int64_t>::max()) {
return "∞";
}
if (millis_ == std::numeric_limits<int64_t>::min()) {
return "-∞";
}
return std::to_string(millis_) + "ms";
}
std::string Duration::ToJsonString() const {
gpr_timespec ts = as_timespec();
return absl::StrFormat("%d.%09ds", ts.tv_sec, ts.tv_nsec);
}
Duration::operator grpc_event_engine::experimental::EventEngine::Duration()
const {
return std::chrono::milliseconds(
Clamp(millis_, std::numeric_limits<int64_t>::min() / GPR_NS_PER_MS,
std::numeric_limits<int64_t>::max() / GPR_NS_PER_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