123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // 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 "y_absl/synchronization/internal/kernel_timeout.h"
- #ifndef _WIN32
- #include <sys/types.h>
- #endif
- #include <algorithm>
- #include <chrono> // NOLINT(build/c++11)
- #include <cstdint>
- #include <cstdlib>
- #include <cstring>
- #include <ctime>
- #include <limits>
- #include "y_absl/base/attributes.h"
- #include "y_absl/base/call_once.h"
- #include "y_absl/base/config.h"
- #include "y_absl/time/time.h"
- namespace y_absl {
- Y_ABSL_NAMESPACE_BEGIN
- namespace synchronization_internal {
- #ifdef Y_ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
- constexpr uint64_t KernelTimeout::kNoTimeout;
- constexpr int64_t KernelTimeout::kMaxNanos;
- #endif
- int64_t KernelTimeout::SteadyClockNow() {
- if (!SupportsSteadyClock()) {
- return y_absl::GetCurrentTimeNanos();
- }
- return std::chrono::duration_cast<std::chrono::nanoseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- }
- KernelTimeout::KernelTimeout(y_absl::Time t) {
- // `y_absl::InfiniteFuture()` is a common "no timeout" value and cheaper to
- // compare than convert.
- if (t == y_absl::InfiniteFuture()) {
- rep_ = kNoTimeout;
- return;
- }
- int64_t unix_nanos = y_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<uint64_t>(unix_nanos) << 1;
- }
- KernelTimeout::KernelTimeout(y_absl::Duration d) {
- // `y_absl::InfiniteDuration()` is a common "no timeout" value and cheaper to
- // compare than convert.
- if (d == y_absl::InfiniteDuration()) {
- rep_ = kNoTimeout;
- return;
- }
- int64_t nanos = y_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<uint64_t>(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<int64_t>(nanos - SteadyClockNow(), 0);
- int64_t now = y_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<int64_t>(nanos - y_absl::GetCurrentTimeNanos(), 0);
- }
- return std::max<int64_t>(nanos - SteadyClockNow(), 0);
- }
- struct timespec KernelTimeout::MakeAbsTimespec() const {
- return y_absl::ToTimespec(y_absl::Nanoseconds(MakeAbsNanos()));
- }
- struct timespec KernelTimeout::MakeRelativeTimespec() const {
- return y_absl::ToTimespec(y_absl::Nanoseconds(InNanosecondsFromNow()));
- }
- #ifndef _WIN32
- struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const {
- if (!has_timeout()) {
- return y_absl::ToTimespec(y_absl::Nanoseconds(kMaxNanos));
- }
- int64_t nanos = RawAbsNanos();
- if (is_absolute_timeout()) {
- nanos -= y_absl::GetCurrentTimeNanos();
- } else {
- nanos -= SteadyClockNow();
- }
- struct timespec now;
- Y_ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed");
- y_absl::Duration from_clock_epoch =
- y_absl::DurationFromTimespec(now) + y_absl::Nanoseconds(nanos);
- if (from_clock_epoch <= y_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 y_absl::ToTimespec(y_absl::Nanoseconds(1));
- }
- return y_absl::ToTimespec(from_clock_epoch);
- }
- #endif
- KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const {
- constexpr DWord kInfinite = std::numeric_limits<DWord>::max();
- if (!has_timeout()) {
- return kInfinite;
- }
- constexpr uint64_t kNanosInMillis = uint64_t{1'000'000};
- constexpr uint64_t kMaxValueNanos =
- std::numeric_limits<int64_t>::max() - kNanosInMillis + 1;
- uint64_t ns_from_now = static_cast<uint64_t>(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<DWord>(ms_from_now);
- }
- std::chrono::time_point<std::chrono::system_clock>
- KernelTimeout::ToChronoTimePoint() const {
- if (!has_timeout()) {
- return std::chrono::time_point<std::chrono::system_clock>::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::microseconds>(
- 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
- Y_ABSL_NAMESPACE_END
- } // namespace y_absl
|