|
16 | 16 |
|
17 | 17 | #include "absl/base/config.h"
|
18 | 18 |
|
19 |
| -#ifdef _WIN32 |
20 |
| -#include <windows.h> |
21 |
| -#else |
| 19 | +#ifndef _WIN32 |
22 | 20 | #include <sys/time.h>
|
23 | 21 | #include <unistd.h>
|
24 | 22 | #endif
|
@@ -85,34 +83,70 @@ namespace synchronization_internal {
|
85 | 83 |
|
86 | 84 | class FutexImpl {
|
87 | 85 | public:
|
88 |
| - static int WaitUntil(std::atomic<int32_t> *v, int32_t val, |
| 86 | + // Atomically check that `*v == val`, and if it is, then sleep until the |
| 87 | + // timeout `t` has been reached, or until woken by `Wake()`. |
| 88 | + static int WaitUntil(std::atomic<int32_t>* v, int32_t val, |
89 | 89 | KernelTimeout t) {
|
90 |
| - long err = 0; // NOLINT(runtime/int) |
91 |
| - if (t.has_timeout()) { |
92 |
| - // https://locklessinc.com/articles/futex_cheat_sheet/ |
93 |
| - // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. |
94 |
| - struct timespec abs_timeout = t.MakeAbsTimespec(); |
95 |
| - // Atomically check that the futex value is still 0, and if it |
96 |
| - // is, sleep until abs_timeout or until woken by FUTEX_WAKE. |
97 |
| - err = syscall( |
98 |
| - SYS_futex, reinterpret_cast<int32_t *>(v), |
99 |
| - FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val, |
100 |
| - &abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY); |
| 90 | + // Monotonic waits are disabled for production builds because go/btm |
| 91 | + // requires synchronized clocks. |
| 92 | + // TODO(b/160682823): Find a way to enable this when BTM is not enabled |
| 93 | + // since production builds don't always run on Borg. |
| 94 | +#if defined(CLOCK_MONOTONIC) && !defined(__GOOGLE_GRTE_VERSION__) |
| 95 | + constexpr bool kRelativeTimeoutSupported = true; |
| 96 | +#else |
| 97 | + constexpr bool kRelativeTimeoutSupported = false; |
| 98 | +#endif |
| 99 | + |
| 100 | + if (!t.has_timeout()) { |
| 101 | + return Wait(v, val); |
| 102 | + } else if (kRelativeTimeoutSupported && t.is_relative_timeout()) { |
| 103 | + auto rel_timespec = t.MakeRelativeTimespec(); |
| 104 | + return WaitRelativeTimeout(v, val, &rel_timespec); |
101 | 105 | } else {
|
102 |
| - // Atomically check that the futex value is still 0, and if it |
103 |
| - // is, sleep until woken by FUTEX_WAKE. |
104 |
| - err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), |
105 |
| - FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr); |
| 106 | + auto abs_timespec = t.MakeAbsTimespec(); |
| 107 | + return WaitAbsoluteTimeout(v, val, &abs_timespec); |
106 | 108 | }
|
107 |
| - if (ABSL_PREDICT_FALSE(err != 0)) { |
| 109 | + } |
| 110 | + |
| 111 | + // Atomically check that `*v == val`, and if it is, then sleep until the until |
| 112 | + // woken by `Wake()`. |
| 113 | + static int Wait(std::atomic<int32_t>* v, int32_t val) { |
| 114 | + return WaitAbsoluteTimeout(v, val, nullptr); |
| 115 | + } |
| 116 | + |
| 117 | + // Atomically check that `*v == val`, and if it is, then sleep until |
| 118 | + // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`. |
| 119 | + static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, |
| 120 | + const struct timespec* abs_timeout) { |
| 121 | + // https://locklessinc.com/articles/futex_cheat_sheet/ |
| 122 | + // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. |
| 123 | + auto err = |
| 124 | + syscall(SYS_futex, reinterpret_cast<int32_t*>(v), |
| 125 | + FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, |
| 126 | + val, abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY); |
| 127 | + if (err != 0) { |
| 128 | + return -errno; |
| 129 | + } |
| 130 | + return 0; |
| 131 | + } |
| 132 | + |
| 133 | + // Atomically check that `*v == val`, and if it is, then sleep until |
| 134 | + // `*rel_timeout` has elapsed, or until woken by `Wake()`. |
| 135 | + static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val, |
| 136 | + const struct timespec* rel_timeout) { |
| 137 | + // Atomically check that the futex value is still 0, and if it |
| 138 | + // is, sleep until abs_timeout or until woken by FUTEX_WAKE. |
| 139 | + auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), |
| 140 | + FUTEX_PRIVATE_FLAG, val, rel_timeout); |
| 141 | + if (err != 0) { |
108 | 142 | return -errno;
|
109 | 143 | }
|
110 | 144 | return 0;
|
111 | 145 | }
|
112 | 146 |
|
113 |
| - static int Wake(std::atomic<int32_t> *v, int32_t count) { |
114 |
| - // NOLINTNEXTLINE(runtime/int) |
115 |
| - long err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), |
| 147 | + // Wakes at most `count` waiters that have entered the sleep state on `v`. |
| 148 | + static int Wake(std::atomic<int32_t>* v, int32_t count) { |
| 149 | + auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), |
116 | 150 | FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
|
117 | 151 | if (ABSL_PREDICT_FALSE(err < 0)) {
|
118 | 152 | return -errno;
|
|
0 commit comments