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