// Copyright 2023 The Abseil 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 // // https://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 "absl/synchronization/internal/kernel_timeout.h" #ifndef _WIN32 #include #endif #include #include // NOLINT(build/c++11) #include #include #include #include #include #include "absl/base/attributes.h" #include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/time/time.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace synchronization_internal { #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr uint64_t KernelTimeout::kNoTimeout; constexpr int64_t KernelTimeout::kMaxNanos; #endif int64_t KernelTimeout::SteadyClockNow() { if (!SupportsSteadyClock()) { return absl::GetCurrentTimeNanos(); } return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); } KernelTimeout::KernelTimeout(absl::Time t) { // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to // compare than convert. if (t == absl::InfiniteFuture()) { rep_ = kNoTimeout; return; } int64_t unix_nanos = absl::ToUnixNanos(t); // A timeout that lands before the unix epoch is converted to 0. // In theory implementations should expire these timeouts immediately. if (unix_nanos < 0) { unix_nanos = 0; } // Values greater than or equal to kMaxNanos are converted to infinite. if (unix_nanos >= kMaxNanos) { rep_ = kNoTimeout; return; } rep_ = static_cast(unix_nanos) << 1; } KernelTimeout::KernelTimeout(absl::Duration d) { // `absl::InfiniteDuration()` is a common "no timeout" value and cheaper to // compare than convert. if (d == absl::InfiniteDuration()) { rep_ = kNoTimeout; return; } int64_t nanos = absl::ToInt64Nanoseconds(d); // Negative durations are normalized to 0. // In theory implementations should expire these timeouts immediately. if (nanos < 0) { nanos = 0; } int64_t now = SteadyClockNow(); if (nanos > kMaxNanos - now) { // Durations that would be greater than kMaxNanos are converted to infinite. rep_ = kNoTimeout; return; } nanos += now; rep_ = (static_cast(nanos) << 1) | uint64_t{1}; } int64_t KernelTimeout::MakeAbsNanos() const { if (!has_timeout()) { return kMaxNanos; } int64_t nanos = RawAbsNanos(); if (is_relative_timeout()) { // We need to change epochs, because the relative timeout might be // represented by an absolute timestamp from another clock. nanos = std::max(nanos - SteadyClockNow(), 0); int64_t now = absl::GetCurrentTimeNanos(); if (nanos > kMaxNanos - now) { // Overflow. nanos = kMaxNanos; } else { nanos += now; } } else if (nanos == 0) { // Some callers have assumed that 0 means no timeout, so instead we return a // time of 1 nanosecond after the epoch. nanos = 1; } return nanos; } int64_t KernelTimeout::InNanosecondsFromNow() const { if (!has_timeout()) { return kMaxNanos; } int64_t nanos = RawAbsNanos(); if (is_absolute_timeout()) { return std::max(nanos - absl::GetCurrentTimeNanos(), 0); } return std::max(nanos - SteadyClockNow(), 0); } struct timespec KernelTimeout::MakeAbsTimespec() const { return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos())); } struct timespec KernelTimeout::MakeRelativeTimespec() const { return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow())); } #ifndef _WIN32 struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const { if (!has_timeout()) { return absl::ToTimespec(absl::Nanoseconds(kMaxNanos)); } int64_t nanos = RawAbsNanos(); if (is_absolute_timeout()) { nanos -= absl::GetCurrentTimeNanos(); } else { nanos -= SteadyClockNow(); } struct timespec now; ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed"); absl::Duration from_clock_epoch = absl::DurationFromTimespec(now) + absl::Nanoseconds(nanos); if (from_clock_epoch <= absl::ZeroDuration()) { // Some callers have assumed that 0 means no timeout, so instead we return a // time of 1 nanosecond after the epoch. For safety we also do not return // negative values. return absl::ToTimespec(absl::Nanoseconds(1)); } return absl::ToTimespec(from_clock_epoch); } #endif KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const { constexpr DWord kInfinite = std::numeric_limits::max(); if (!has_timeout()) { return kInfinite; } constexpr uint64_t kNanosInMillis = uint64_t{1'000'000}; constexpr uint64_t kMaxValueNanos = std::numeric_limits::max() - kNanosInMillis + 1; uint64_t ns_from_now = static_cast(InNanosecondsFromNow()); if (ns_from_now >= kMaxValueNanos) { // Rounding up would overflow. return kInfinite; } // Convert to milliseconds, always rounding up. uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis; if (ms_from_now > kInfinite) { return kInfinite; } return static_cast(ms_from_now); } std::chrono::time_point KernelTimeout::ToChronoTimePoint() const { if (!has_timeout()) { return std::chrono::time_point::max(); } // The cast to std::microseconds is because (on some platforms) the // std::ratio used by std::chrono::steady_clock doesn't convert to // std::nanoseconds, so it doesn't compile. auto micros = std::chrono::duration_cast( std::chrono::nanoseconds(MakeAbsNanos())); return std::chrono::system_clock::from_time_t(0) + micros; } std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const { if (!has_timeout()) { return std::chrono::nanoseconds::max(); } return std::chrono::nanoseconds(InNanosecondsFromNow()); } } // namespace synchronization_internal ABSL_NAMESPACE_END } // namespace absl