xref: /aosp_15_r20/external/abseil-cpp/absl/synchronization/internal/kernel_timeout.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
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 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
39 constexpr uint64_t KernelTimeout::kNoTimeout;
40 constexpr int64_t KernelTimeout::kMaxNanos;
41 #endif
42 
SteadyClockNow()43 int64_t KernelTimeout::SteadyClockNow() {
44   if (!SupportsSteadyClock()) {
45     return absl::GetCurrentTimeNanos();
46   }
47   return std::chrono::duration_cast<std::chrono::nanoseconds>(
48              std::chrono::steady_clock::now().time_since_epoch())
49       .count();
50 }
51 
KernelTimeout(absl::Time t)52 KernelTimeout::KernelTimeout(absl::Time t) {
53   // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to
54   // compare than convert.
55   if (t == absl::InfiniteFuture()) {
56     rep_ = kNoTimeout;
57     return;
58   }
59 
60   int64_t unix_nanos = absl::ToUnixNanos(t);
61 
62   // A timeout that lands before the unix epoch is converted to 0.
63   // In theory implementations should expire these timeouts immediately.
64   if (unix_nanos < 0) {
65     unix_nanos = 0;
66   }
67 
68   // Values greater than or equal to kMaxNanos are converted to infinite.
69   if (unix_nanos >= kMaxNanos) {
70     rep_ = kNoTimeout;
71     return;
72   }
73 
74   rep_ = static_cast<uint64_t>(unix_nanos) << 1;
75 }
76 
KernelTimeout(absl::Duration d)77 KernelTimeout::KernelTimeout(absl::Duration d) {
78   // `absl::InfiniteDuration()` is a common "no timeout" value and cheaper to
79   // compare than convert.
80   if (d == absl::InfiniteDuration()) {
81     rep_ = kNoTimeout;
82     return;
83   }
84 
85   int64_t nanos = absl::ToInt64Nanoseconds(d);
86 
87   // Negative durations are normalized to 0.
88   // In theory implementations should expire these timeouts immediately.
89   if (nanos < 0) {
90     nanos = 0;
91   }
92 
93   int64_t now = SteadyClockNow();
94   if (nanos > kMaxNanos - now) {
95     // Durations that would be greater than kMaxNanos are converted to infinite.
96     rep_ = kNoTimeout;
97     return;
98   }
99 
100   nanos += now;
101   rep_ = (static_cast<uint64_t>(nanos) << 1) | uint64_t{1};
102 }
103 
MakeAbsNanos() const104 int64_t KernelTimeout::MakeAbsNanos() const {
105   if (!has_timeout()) {
106     return kMaxNanos;
107   }
108 
109   int64_t nanos = RawAbsNanos();
110 
111   if (is_relative_timeout()) {
112     // We need to change epochs, because the relative timeout might be
113     // represented by an absolute timestamp from another clock.
114     nanos = std::max<int64_t>(nanos - SteadyClockNow(), 0);
115     int64_t now = absl::GetCurrentTimeNanos();
116     if (nanos > kMaxNanos - now) {
117       // Overflow.
118       nanos = kMaxNanos;
119     } else {
120       nanos += now;
121     }
122   } else if (nanos == 0) {
123     // Some callers have assumed that 0 means no timeout, so instead we return a
124     // time of 1 nanosecond after the epoch.
125     nanos = 1;
126   }
127 
128   return nanos;
129 }
130 
InNanosecondsFromNow() const131 int64_t KernelTimeout::InNanosecondsFromNow() const {
132   if (!has_timeout()) {
133     return kMaxNanos;
134   }
135 
136   int64_t nanos = RawAbsNanos();
137   if (is_absolute_timeout()) {
138     return std::max<int64_t>(nanos - absl::GetCurrentTimeNanos(), 0);
139   }
140   return std::max<int64_t>(nanos - SteadyClockNow(), 0);
141 }
142 
MakeAbsTimespec() const143 struct timespec KernelTimeout::MakeAbsTimespec() const {
144   return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos()));
145 }
146 
MakeRelativeTimespec() const147 struct timespec KernelTimeout::MakeRelativeTimespec() const {
148   return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow()));
149 }
150 
151 #ifndef _WIN32
MakeClockAbsoluteTimespec(clockid_t c) const152 struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const {
153   if (!has_timeout()) {
154     return absl::ToTimespec(absl::Nanoseconds(kMaxNanos));
155   }
156 
157   int64_t nanos = RawAbsNanos();
158   if (is_absolute_timeout()) {
159     nanos -= absl::GetCurrentTimeNanos();
160   } else {
161     nanos -= SteadyClockNow();
162   }
163 
164   struct timespec now;
165   ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed");
166   absl::Duration from_clock_epoch =
167       absl::DurationFromTimespec(now) + absl::Nanoseconds(nanos);
168   if (from_clock_epoch <= absl::ZeroDuration()) {
169     // Some callers have assumed that 0 means no timeout, so instead we return a
170     // time of 1 nanosecond after the epoch. For safety we also do not return
171     // negative values.
172     return absl::ToTimespec(absl::Nanoseconds(1));
173   }
174   return absl::ToTimespec(from_clock_epoch);
175 }
176 #endif
177 
InMillisecondsFromNow() const178 KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const {
179   constexpr DWord kInfinite = std::numeric_limits<DWord>::max();
180 
181   if (!has_timeout()) {
182     return kInfinite;
183   }
184 
185   constexpr uint64_t kNanosInMillis = uint64_t{1'000'000};
186   constexpr uint64_t kMaxValueNanos =
187       std::numeric_limits<int64_t>::max() - kNanosInMillis + 1;
188 
189   uint64_t ns_from_now = static_cast<uint64_t>(InNanosecondsFromNow());
190   if (ns_from_now >= kMaxValueNanos) {
191     // Rounding up would overflow.
192     return kInfinite;
193   }
194   // Convert to milliseconds, always rounding up.
195   uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis;
196   if (ms_from_now > kInfinite) {
197     return kInfinite;
198   }
199   return static_cast<DWord>(ms_from_now);
200 }
201 
202 std::chrono::time_point<std::chrono::system_clock>
ToChronoTimePoint() const203 KernelTimeout::ToChronoTimePoint() const {
204   if (!has_timeout()) {
205     return std::chrono::time_point<std::chrono::system_clock>::max();
206   }
207 
208   // The cast to std::microseconds is because (on some platforms) the
209   // std::ratio used by std::chrono::steady_clock doesn't convert to
210   // std::nanoseconds, so it doesn't compile.
211   auto micros = std::chrono::duration_cast<std::chrono::microseconds>(
212       std::chrono::nanoseconds(MakeAbsNanos()));
213   return std::chrono::system_clock::from_time_t(0) + micros;
214 }
215 
ToChronoDuration() const216 std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const {
217   if (!has_timeout()) {
218     return std::chrono::nanoseconds::max();
219   }
220   return std::chrono::nanoseconds(InNanosecondsFromNow());
221 }
222 
223 }  // namespace synchronization_internal
224 ABSL_NAMESPACE_END
225 }  // namespace absl
226