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