tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

kernel_timeout.cc (6434B)


      1 // Copyright 2023 The Abseil Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     https://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "absl/synchronization/internal/kernel_timeout.h"
     16 
     17 #ifndef _WIN32
     18 #include <sys/types.h>
     19 #endif
     20 
     21 #include <algorithm>
     22 #include <chrono>  // NOLINT(build/c++11)
     23 #include <cstdint>
     24 #include <cstdlib>
     25 #include <cstring>
     26 #include <ctime>
     27 #include <limits>
     28 
     29 #include "absl/base/attributes.h"
     30 #include "absl/base/call_once.h"
     31 #include "absl/base/config.h"
     32 #include "absl/time/time.h"
     33 
     34 namespace absl {
     35 ABSL_NAMESPACE_BEGIN
     36 namespace synchronization_internal {
     37 
     38 int64_t KernelTimeout::SteadyClockNow() {
     39  if (!SupportsSteadyClock()) {
     40    return absl::GetCurrentTimeNanos();
     41  }
     42  return std::chrono::duration_cast<std::chrono::nanoseconds>(
     43             std::chrono::steady_clock::now().time_since_epoch())
     44      .count();
     45 }
     46 
     47 KernelTimeout::KernelTimeout(absl::Time t) {
     48  // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to
     49  // compare than convert.
     50  if (t == absl::InfiniteFuture()) {
     51    rep_ = kNoTimeout;
     52    return;
     53  }
     54 
     55  int64_t unix_nanos = absl::ToUnixNanos(t);
     56 
     57  // A timeout that lands before the unix epoch is converted to 0.
     58  // In theory implementations should expire these timeouts immediately.
     59  if (unix_nanos < 0) {
     60    unix_nanos = 0;
     61  }
     62 
     63  // Values greater than or equal to kMaxNanos are converted to infinite.
     64  if (unix_nanos >= kMaxNanos) {
     65    rep_ = kNoTimeout;
     66    return;
     67  }
     68 
     69  rep_ = static_cast<uint64_t>(unix_nanos) << 1;
     70 }
     71 
     72 KernelTimeout::KernelTimeout(absl::Duration d) {
     73  // `absl::InfiniteDuration()` is a common "no timeout" value and cheaper to
     74  // compare than convert.
     75  if (d == absl::InfiniteDuration()) {
     76    rep_ = kNoTimeout;
     77    return;
     78  }
     79 
     80  int64_t nanos = absl::ToInt64Nanoseconds(d);
     81 
     82  // Negative durations are normalized to 0.
     83  // In theory implementations should expire these timeouts immediately.
     84  if (nanos < 0) {
     85    nanos = 0;
     86  }
     87 
     88  int64_t now = SteadyClockNow();
     89  if (nanos > kMaxNanos - now) {
     90    // Durations that would be greater than kMaxNanos are converted to infinite.
     91    rep_ = kNoTimeout;
     92    return;
     93  }
     94 
     95  nanos += now;
     96  rep_ = (static_cast<uint64_t>(nanos) << 1) | uint64_t{1};
     97 }
     98 
     99 int64_t KernelTimeout::MakeAbsNanos() const {
    100  if (!has_timeout()) {
    101    return kMaxNanos;
    102  }
    103 
    104  int64_t nanos = RawAbsNanos();
    105 
    106  if (is_relative_timeout()) {
    107    // We need to change epochs, because the relative timeout might be
    108    // represented by an absolute timestamp from another clock.
    109    nanos = std::max<int64_t>(nanos - SteadyClockNow(), 0);
    110    int64_t now = absl::GetCurrentTimeNanos();
    111    if (nanos > kMaxNanos - now) {
    112      // Overflow.
    113      nanos = kMaxNanos;
    114    } else {
    115      nanos += now;
    116    }
    117  } else if (nanos == 0) {
    118    // Some callers have assumed that 0 means no timeout, so instead we return a
    119    // time of 1 nanosecond after the epoch.
    120    nanos = 1;
    121  }
    122 
    123  return nanos;
    124 }
    125 
    126 int64_t KernelTimeout::InNanosecondsFromNow() const {
    127  if (!has_timeout()) {
    128    return kMaxNanos;
    129  }
    130 
    131  int64_t nanos = RawAbsNanos();
    132  if (is_absolute_timeout()) {
    133    return std::max<int64_t>(nanos - absl::GetCurrentTimeNanos(), 0);
    134  }
    135  return std::max<int64_t>(nanos - SteadyClockNow(), 0);
    136 }
    137 
    138 struct timespec KernelTimeout::MakeAbsTimespec() const {
    139  return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos()));
    140 }
    141 
    142 struct timespec KernelTimeout::MakeRelativeTimespec() const {
    143  return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow()));
    144 }
    145 
    146 #ifndef _WIN32
    147 struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const {
    148  if (!has_timeout()) {
    149    return absl::ToTimespec(absl::Nanoseconds(kMaxNanos));
    150  }
    151 
    152  int64_t nanos = RawAbsNanos();
    153  if (is_absolute_timeout()) {
    154    nanos -= absl::GetCurrentTimeNanos();
    155  } else {
    156    nanos -= SteadyClockNow();
    157  }
    158 
    159  struct timespec now;
    160  ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed");
    161  absl::Duration from_clock_epoch =
    162      absl::DurationFromTimespec(now) + absl::Nanoseconds(nanos);
    163  if (from_clock_epoch <= absl::ZeroDuration()) {
    164    // Some callers have assumed that 0 means no timeout, so instead we return a
    165    // time of 1 nanosecond after the epoch. For safety we also do not return
    166    // negative values.
    167    return absl::ToTimespec(absl::Nanoseconds(1));
    168  }
    169  return absl::ToTimespec(from_clock_epoch);
    170 }
    171 #endif
    172 
    173 KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const {
    174  constexpr DWord kInfinite = std::numeric_limits<DWord>::max();
    175 
    176  if (!has_timeout()) {
    177    return kInfinite;
    178  }
    179 
    180  constexpr uint64_t kNanosInMillis = uint64_t{1'000'000};
    181  constexpr uint64_t kMaxValueNanos =
    182      std::numeric_limits<int64_t>::max() - kNanosInMillis + 1;
    183 
    184  uint64_t ns_from_now = static_cast<uint64_t>(InNanosecondsFromNow());
    185  if (ns_from_now >= kMaxValueNanos) {
    186    // Rounding up would overflow.
    187    return kInfinite;
    188  }
    189  // Convert to milliseconds, always rounding up.
    190  uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis;
    191  if (ms_from_now > kInfinite) {
    192    return kInfinite;
    193  }
    194  return static_cast<DWord>(ms_from_now);
    195 }
    196 
    197 std::chrono::time_point<std::chrono::system_clock>
    198 KernelTimeout::ToChronoTimePoint() const {
    199  if (!has_timeout()) {
    200    return std::chrono::time_point<std::chrono::system_clock>::max();
    201  }
    202 
    203  // The cast to std::microseconds is because (on some platforms) the
    204  // std::ratio used by std::chrono::steady_clock doesn't convert to
    205  // std::nanoseconds, so it doesn't compile.
    206  auto micros = std::chrono::duration_cast<std::chrono::microseconds>(
    207      std::chrono::nanoseconds(MakeAbsNanos()));
    208  return std::chrono::system_clock::from_time_t(0) + micros;
    209 }
    210 
    211 std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const {
    212  if (!has_timeout()) {
    213    return std::chrono::nanoseconds::max();
    214  }
    215  return std::chrono::nanoseconds(InNanosecondsFromNow());
    216 }
    217 
    218 }  // namespace synchronization_internal
    219 ABSL_NAMESPACE_END
    220 }  // namespace absl