1 | // Copyright 2017 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 | |
16 | // An optional absolute timeout, with nanosecond granularity, |
17 | // compatible with absl::Time. Suitable for in-register |
18 | // parameter-passing (e.g. syscalls.) |
19 | // Constructible from a absl::Time (for a timeout to be respected) or {} |
20 | // (for "no timeout".) |
21 | // This is a private low-level API for use by a handful of low-level |
22 | // components that are friends of this class. Higher-level components |
23 | // should build APIs based on absl::Time and absl::Duration. |
24 | |
25 | #ifndef ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ |
26 | #define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ |
27 | |
28 | #include <time.h> |
29 | #include <algorithm> |
30 | #include <limits> |
31 | |
32 | #include "absl/base/internal/raw_logging.h" |
33 | #include "absl/time/clock.h" |
34 | #include "absl/time/time.h" |
35 | |
36 | namespace absl { |
37 | namespace synchronization_internal { |
38 | |
39 | class Futex; |
40 | class Waiter; |
41 | |
42 | class KernelTimeout { |
43 | public: |
44 | // A timeout that should expire at <t>. Any value, in the full |
45 | // InfinitePast() to InfiniteFuture() range, is valid here and will be |
46 | // respected. |
47 | explicit KernelTimeout(absl::Time t) : ns_(MakeNs(t)) {} |
48 | // No timeout. |
49 | KernelTimeout() : ns_(0) {} |
50 | |
51 | // A more explicit factory for those who prefer it. Equivalent to {}. |
52 | static KernelTimeout Never() { return {}; } |
53 | |
54 | // We explicitly do not support other custom formats: timespec, int64_t nanos. |
55 | // Unify on this and absl::Time, please. |
56 | |
57 | bool has_timeout() const { return ns_ != 0; } |
58 | |
59 | private: |
60 | // internal rep, not user visible: ns after unix epoch. |
61 | // zero = no timeout. |
62 | // Negative we treat as an unlikely (and certainly expired!) but valid |
63 | // timeout. |
64 | int64_t ns_; |
65 | |
66 | static int64_t MakeNs(absl::Time t) { |
67 | // optimization--InfiniteFuture is common "no timeout" value |
68 | // and cheaper to compare than convert. |
69 | if (t == absl::InfiniteFuture()) return 0; |
70 | int64_t x = ToUnixNanos(t); |
71 | |
72 | // A timeout that lands exactly on the epoch (x=0) needs to be respected, |
73 | // so we alter it unnoticably to 1. Negative timeouts are in |
74 | // theory supported, but handled poorly by the kernel (long |
75 | // delays) so push them forward too; since all such times have |
76 | // already passed, it's indistinguishable. |
77 | if (x <= 0) x = 1; |
78 | // A time larger than what can be represented to the kernel is treated |
79 | // as no timeout. |
80 | if (x == (std::numeric_limits<int64_t>::max)()) x = 0; |
81 | return x; |
82 | } |
83 | |
84 | // Convert to parameter for sem_timedwait/futex/similar. Only for approved |
85 | // users. Do not call if !has_timeout. |
86 | struct timespec MakeAbsTimespec() { |
87 | int64_t n = ns_; |
88 | static const int64_t kNanosPerSecond = 1000 * 1000 * 1000; |
89 | if (n == 0) { |
90 | ABSL_RAW_LOG( |
91 | ERROR, |
92 | "Tried to create a timespec from a non-timeout; never do this." ); |
93 | // But we'll try to continue sanely. no-timeout ~= saturated timeout. |
94 | n = (std::numeric_limits<int64_t>::max)(); |
95 | } |
96 | |
97 | // Kernel APIs validate timespecs as being at or after the epoch, |
98 | // despite the kernel time type being signed. However, no one can |
99 | // tell the difference between a timeout at or before the epoch (since |
100 | // all such timeouts have expired!) |
101 | if (n < 0) n = 0; |
102 | |
103 | struct timespec abstime; |
104 | int64_t seconds = (std::min)(n / kNanosPerSecond, |
105 | int64_t{(std::numeric_limits<time_t>::max)()}); |
106 | abstime.tv_sec = static_cast<time_t>(seconds); |
107 | abstime.tv_nsec = |
108 | static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond); |
109 | return abstime; |
110 | } |
111 | |
112 | #ifdef _WIN32 |
113 | // Converts to milliseconds from now, or INFINITE when |
114 | // !has_timeout(). For use by SleepConditionVariableSRW on |
115 | // Windows. Callers should recognize that the return value is a |
116 | // relative duration (it should be recomputed by calling this method |
117 | // in the case of a spurious wakeup). |
118 | // This header file may be included transitively by public header files, |
119 | // so we define our own DWORD and INFINITE instead of getting them from |
120 | // <intsafe.h> and <WinBase.h>. |
121 | typedef unsigned long DWord; // NOLINT |
122 | DWord InMillisecondsFromNow() const { |
123 | constexpr DWord kInfinite = (std::numeric_limits<DWord>::max)(); |
124 | if (!has_timeout()) { |
125 | return kInfinite; |
126 | } |
127 | // The use of absl::Now() to convert from absolute time to |
128 | // relative time means that absl::Now() cannot use anything that |
129 | // depends on KernelTimeout (for example, Mutex) on Windows. |
130 | int64_t now = ToUnixNanos(absl::Now()); |
131 | if (ns_ >= now) { |
132 | // Round up so that Now() + ms_from_now >= ns_. |
133 | constexpr uint64_t max_nanos = |
134 | (std::numeric_limits<int64_t>::max)() - 999999u; |
135 | uint64_t ms_from_now = |
136 | (std::min<uint64_t>(max_nanos, ns_ - now) + 999999u) / 1000000u; |
137 | if (ms_from_now > kInfinite) { |
138 | return kInfinite; |
139 | } |
140 | return static_cast<DWord>(ms_from_now); |
141 | } |
142 | return 0; |
143 | } |
144 | #endif |
145 | |
146 | friend class Futex; |
147 | friend class Waiter; |
148 | }; |
149 | |
150 | } // namespace synchronization_internal |
151 | } // namespace absl |
152 | |
153 | #endif // ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ |
154 | |