• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SteadyClockNow()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 
KernelTimeout(absl::Time t)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 
KernelTimeout(absl::Duration d)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 
MakeAbsNanos() const99 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 
InNanosecondsFromNow() const126 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 
MakeAbsTimespec() const138 struct timespec KernelTimeout::MakeAbsTimespec() const {
139   return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos()));
140 }
141 
MakeRelativeTimespec() const142 struct timespec KernelTimeout::MakeRelativeTimespec() const {
143   return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow()));
144 }
145 
146 #ifndef _WIN32
MakeClockAbsoluteTimespec(clockid_t c) const147 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 
InMillisecondsFromNow() const173 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>
ToChronoTimePoint() const198 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 
ToChronoDuration() const211 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
221