Skip to content

Commit 0372af1

Browse files
derekmaurocopybara-github
authored andcommitted
Add KernelTimeout methods that convert the timeout to the
std::chrono methods used by std::condition_variable. A followup change will add an implemention of synchronization_internal::Waiter that can use std:mutex/std::condition_variable to implement the per-thread semaphore that absl::Mutex waits on. This implementation may at some point become the default on platforms such as Windows where there doesn't seem to be an easy way of supporting real absolute timeouts. In this case we can defer to their standard library to implement correct support. PiperOrigin-RevId: 510204786 Change-Id: Icf4d695013fd060abbd53dae23e71ea36f731565
1 parent 2d4c687 commit 0372af1

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

absl/synchronization/internal/kernel_timeout.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "absl/synchronization/internal/kernel_timeout.h"
1616

1717
#include <algorithm>
18+
#include <chrono> // NOLINT(build/c++11)
1819
#include <cstdint>
1920
#include <ctime>
2021
#include <limits>
@@ -163,6 +164,46 @@ KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const {
163164
return DWord{0};
164165
}
165166

167+
std::chrono::time_point<std::chrono::system_clock>
168+
KernelTimeout::ToChronoTimePoint() const {
169+
if (!has_timeout()) {
170+
return std::chrono::time_point<std::chrono::system_clock>::max();
171+
}
172+
173+
// The cast to std::microseconds is because (on some platforms) the
174+
// std::ratio used by std::chrono::steady_clock doesn't convert to
175+
// std::nanoseconds, so it doesn't compile.
176+
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(
177+
std::chrono::nanoseconds(RawNanos()));
178+
if (is_relative_timeout()) {
179+
auto now = std::chrono::system_clock::now();
180+
if (micros >
181+
std::chrono::time_point<std::chrono::system_clock>::max() - now) {
182+
// Overflow.
183+
return std::chrono::time_point<std::chrono::system_clock>::max();
184+
}
185+
return now + micros;
186+
}
187+
return std::chrono::system_clock::from_time_t(0) + micros;
188+
}
189+
190+
std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const {
191+
if (!has_timeout()) {
192+
return std::chrono::nanoseconds::max();
193+
}
194+
if (is_absolute_timeout()) {
195+
auto d = std::chrono::duration_cast<std::chrono::nanoseconds>(
196+
std::chrono::nanoseconds(RawNanos()) -
197+
(std::chrono::system_clock::now() -
198+
std::chrono::system_clock::from_time_t(0)));
199+
if (d < std::chrono::nanoseconds(0)) {
200+
d = std::chrono::nanoseconds(0);
201+
}
202+
return d;
203+
}
204+
return std::chrono::nanoseconds(RawNanos());
205+
}
206+
166207
} // namespace synchronization_internal
167208
ABSL_NAMESPACE_END
168209
} // namespace absl

absl/synchronization/internal/kernel_timeout.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
1717

1818
#include <algorithm>
19+
#include <chrono> // NOLINT(build/c++11)
1920
#include <cstdint>
2021
#include <ctime>
2122
#include <limits>
@@ -95,6 +96,20 @@ class KernelTimeout {
9596
typedef unsigned long DWord; // NOLINT
9697
DWord InMillisecondsFromNow() const;
9798

99+
// Convert to std::chrono::time_point for interfaces that expect an absolute
100+
// timeout, like std::condition_variable::wait_until(). If !has_timeout() or
101+
// is_relative_timeout(), attempts to convert to a reasonable absolute
102+
// timeout, but callers should test has_timeout() and is_relative_timeout()
103+
// and prefer to use a more appropriate interface.
104+
std::chrono::time_point<std::chrono::system_clock> ToChronoTimePoint() const;
105+
106+
// Convert to std::chrono::time_point for interfaces that expect a relative
107+
// timeout, like std::condition_variable::wait_for(). If !has_timeout() or
108+
// is_absolute_timeout(), attempts to convert to a reasonable relative
109+
// timeout, but callers should test has_timeout() and is_absolute_timeout()
110+
// and prefer to use a more appropriate interface.
111+
std::chrono::nanoseconds ToChronoDuration() const;
112+
98113
private:
99114
// Internal representation.
100115
// - If the value is kNoTimeout, then the timeout is infinite, and

absl/synchronization/internal/kernel_timeout_test.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "absl/synchronization/internal/kernel_timeout.h"
1616

17+
#include <chrono> // NOLINT(build/c++11)
1718
#include <limits>
1819

1920
#include "gtest/gtest.h"
@@ -72,6 +73,11 @@ TEST(KernelTimeout, FiniteTimes) {
7273
EXPECT_LE(absl::AbsDuration(absl::Milliseconds(t.InMillisecondsFromNow()) -
7374
std::max(duration, absl::ZeroDuration())),
7475
absl::Milliseconds(5));
76+
EXPECT_LE(absl::AbsDuration(absl::FromChrono(t.ToChronoTimePoint()) - when),
77+
absl::Microseconds(1));
78+
EXPECT_LE(absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) -
79+
std::max(duration, absl::ZeroDuration())),
80+
kTimingBound);
7581
}
7682
}
7783

@@ -90,6 +96,9 @@ TEST(KernelTimeout, InfiniteFuture) {
9096
absl::Now() + absl::Hours(100000));
9197
EXPECT_EQ(t.InMillisecondsFromNow(),
9298
std::numeric_limits<KernelTimeout::DWord>::max());
99+
EXPECT_EQ(t.ToChronoTimePoint(),
100+
std::chrono::time_point<std::chrono::system_clock>::max());
101+
EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
93102
}
94103

95104
TEST(KernelTimeout, DefaultConstructor) {
@@ -108,6 +117,9 @@ TEST(KernelTimeout, DefaultConstructor) {
108117
absl::Now() + absl::Hours(100000));
109118
EXPECT_EQ(t.InMillisecondsFromNow(),
110119
std::numeric_limits<KernelTimeout::DWord>::max());
120+
EXPECT_EQ(t.ToChronoTimePoint(),
121+
std::chrono::time_point<std::chrono::system_clock>::max());
122+
EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
111123
}
112124

113125
TEST(KernelTimeout, TimeMaxNanos) {
@@ -126,6 +138,9 @@ TEST(KernelTimeout, TimeMaxNanos) {
126138
absl::Now() + absl::Hours(100000));
127139
EXPECT_EQ(t.InMillisecondsFromNow(),
128140
std::numeric_limits<KernelTimeout::DWord>::max());
141+
EXPECT_EQ(t.ToChronoTimePoint(),
142+
std::chrono::time_point<std::chrono::system_clock>::max());
143+
EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
129144
}
130145

131146
TEST(KernelTimeout, Never) {
@@ -144,6 +159,9 @@ TEST(KernelTimeout, Never) {
144159
absl::Now() + absl::Hours(100000));
145160
EXPECT_EQ(t.InMillisecondsFromNow(),
146161
std::numeric_limits<KernelTimeout::DWord>::max());
162+
EXPECT_EQ(t.ToChronoTimePoint(),
163+
std::chrono::time_point<std::chrono::system_clock>::max());
164+
EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
147165
}
148166

149167
TEST(KernelTimeout, InfinitePast) {
@@ -157,6 +175,9 @@ TEST(KernelTimeout, InfinitePast) {
157175
absl::ZeroDuration());
158176
EXPECT_LE(absl::FromUnixNanos(t.MakeAbsNanos()), absl::FromUnixNanos(1));
159177
EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{0});
178+
EXPECT_LT(t.ToChronoTimePoint(), std::chrono::system_clock::from_time_t(0) +
179+
std::chrono::seconds(1));
180+
EXPECT_EQ(t.ToChronoDuration(), std::chrono::nanoseconds(0));
160181
}
161182

162183
TEST(KernelTimeout, FiniteDurations) {
@@ -186,6 +207,10 @@ TEST(KernelTimeout, FiniteDurations) {
186207
absl::Milliseconds(5));
187208
EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration,
188209
absl::Milliseconds(5));
210+
EXPECT_LE(absl::AbsDuration(absl::Now() + duration -
211+
absl::FromChrono(t.ToChronoTimePoint())),
212+
kTimingBound);
213+
EXPECT_EQ(absl::FromChrono(t.ToChronoDuration()), duration);
189214
}
190215
}
191216

@@ -218,6 +243,10 @@ TEST(KernelTimeout, NegativeDurations) {
218243
absl::AbsDuration(absl::Now() - absl::FromUnixNanos(t.MakeAbsNanos())),
219244
absl::Milliseconds(5));
220245
EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{0});
246+
EXPECT_LE(absl::AbsDuration(absl::Now() -
247+
absl::FromChrono(t.ToChronoTimePoint())),
248+
absl::Milliseconds(5));
249+
EXPECT_EQ(t.ToChronoDuration(), std::chrono::nanoseconds(0));
221250
}
222251
}
223252

@@ -236,6 +265,9 @@ TEST(KernelTimeout, InfiniteDuration) {
236265
absl::Now() + absl::Hours(100000));
237266
EXPECT_EQ(t.InMillisecondsFromNow(),
238267
std::numeric_limits<KernelTimeout::DWord>::max());
268+
EXPECT_EQ(t.ToChronoTimePoint(),
269+
std::chrono::time_point<std::chrono::system_clock>::max());
270+
EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
239271
}
240272

241273
TEST(KernelTimeout, DurationMaxNanos) {
@@ -254,6 +286,9 @@ TEST(KernelTimeout, DurationMaxNanos) {
254286
absl::Now() + absl::Hours(100000));
255287
EXPECT_EQ(t.InMillisecondsFromNow(),
256288
std::numeric_limits<KernelTimeout::DWord>::max());
289+
EXPECT_EQ(t.ToChronoTimePoint(),
290+
std::chrono::time_point<std::chrono::system_clock>::max());
291+
EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max());
257292
}
258293

259294
TEST(KernelTimeout, OverflowNanos) {
@@ -273,6 +308,9 @@ TEST(KernelTimeout, OverflowNanos) {
273308
absl::Now() + absl::Hours(100000));
274309
EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration,
275310
absl::Milliseconds(5));
311+
EXPECT_GT(t.ToChronoTimePoint(),
312+
std::chrono::system_clock::now() + std::chrono::hours(100000));
313+
EXPECT_GT(t.ToChronoDuration(), std::chrono::hours(100000));
276314
}
277315

278316
} // namespace

0 commit comments

Comments
 (0)