xref: /aosp_15_r20/external/angle/third_party/abseil-cpp/absl/synchronization/internal/futex.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 // Copyright 2020 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 #ifndef ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
15 #define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
16 
17 #include "absl/base/config.h"
18 
19 #ifndef _WIN32
20 #include <sys/time.h>
21 #include <unistd.h>
22 #endif
23 
24 #ifdef __linux__
25 #include <linux/futex.h>
26 #include <sys/syscall.h>
27 #endif
28 
29 #include <errno.h>
30 #include <stdio.h>
31 #include <time.h>
32 
33 #include <atomic>
34 #include <cstdint>
35 #include <limits>
36 
37 #include "absl/base/optimization.h"
38 #include "absl/synchronization/internal/kernel_timeout.h"
39 
40 #ifdef ABSL_INTERNAL_HAVE_FUTEX
41 #error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line
42 #elif defined(__BIONIC__)
43 // Bionic supports all the futex operations we need even when some of the futex
44 // definitions are missing.
45 #define ABSL_INTERNAL_HAVE_FUTEX
46 #elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME)
47 // FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28.
48 #define ABSL_INTERNAL_HAVE_FUTEX
49 #endif
50 
51 #ifdef ABSL_INTERNAL_HAVE_FUTEX
52 
53 namespace absl {
54 ABSL_NAMESPACE_BEGIN
55 namespace synchronization_internal {
56 
57 // Some Android headers are missing these definitions even though they
58 // support these futex operations.
59 #ifdef __BIONIC__
60 #ifndef SYS_futex
61 #define SYS_futex __NR_futex
62 #endif
63 #ifndef FUTEX_WAIT_BITSET
64 #define FUTEX_WAIT_BITSET 9
65 #endif
66 #ifndef FUTEX_PRIVATE_FLAG
67 #define FUTEX_PRIVATE_FLAG 128
68 #endif
69 #ifndef FUTEX_CLOCK_REALTIME
70 #define FUTEX_CLOCK_REALTIME 256
71 #endif
72 #ifndef FUTEX_BITSET_MATCH_ANY
73 #define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF
74 #endif
75 #endif
76 
77 #if defined(__NR_futex_time64) && !defined(SYS_futex_time64)
78 #define SYS_futex_time64 __NR_futex_time64
79 #endif
80 
81 #if defined(SYS_futex_time64) && !defined(SYS_futex)
82 #define SYS_futex SYS_futex_time64
83 using FutexTimespec = struct timespec;
84 #else
85 // Some libc implementations have switched to an unconditional 64-bit `time_t`
86 // definition. This means that `struct timespec` may not match the layout
87 // expected by the kernel ABI on 32-bit platforms. So we define the
88 // FutexTimespec that matches the kernel timespec definition. It should be safe
89 // to use this struct for 64-bit userspace builds too, since it will use another
90 // SYS_futex kernel call with 64-bit tv_sec inside timespec.
91 struct FutexTimespec {
92   long tv_sec;   // NOLINT
93   long tv_nsec;  // NOLINT
94 };
95 #endif
96 
97 class FutexImpl {
98  public:
99   // Atomically check that `*v == val`, and if it is, then sleep until the until
100   // woken by `Wake()`.
Wait(std::atomic<int32_t> * v,int32_t val)101   static int Wait(std::atomic<int32_t>* v, int32_t val) {
102     return WaitAbsoluteTimeout(v, val, nullptr);
103   }
104 
105   // Atomically check that `*v == val`, and if it is, then sleep until
106   // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
WaitAbsoluteTimeout(std::atomic<int32_t> * v,int32_t val,const struct timespec * abs_timeout)107   static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
108                                  const struct timespec* abs_timeout) {
109     FutexTimespec ts;
110     // https://locklessinc.com/articles/futex_cheat_sheet/
111     // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
112     auto err = syscall(
113         SYS_futex, reinterpret_cast<int32_t*>(v),
114         FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
115         ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY);
116     if (err != 0) {
117       return -errno;
118     }
119     return 0;
120   }
121 
122   // Atomically check that `*v == val`, and if it is, then sleep until
123   // `*rel_timeout` has elapsed, or until woken by `Wake()`.
WaitRelativeTimeout(std::atomic<int32_t> * v,int32_t val,const struct timespec * rel_timeout)124   static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
125                                  const struct timespec* rel_timeout) {
126     FutexTimespec ts;
127     // Atomically check that the futex value is still 0, and if it
128     // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
129     auto err =
130         syscall(SYS_futex, reinterpret_cast<int32_t*>(v), FUTEX_PRIVATE_FLAG,
131                 val, ToFutexTimespec(rel_timeout, &ts));
132     if (err != 0) {
133       return -errno;
134     }
135     return 0;
136   }
137 
138   // Wakes at most `count` waiters that have entered the sleep state on `v`.
Wake(std::atomic<int32_t> * v,int32_t count)139   static int Wake(std::atomic<int32_t>* v, int32_t count) {
140     auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
141                        FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
142     if (ABSL_PREDICT_FALSE(err < 0)) {
143       return -errno;
144     }
145     return 0;
146   }
147 
148  private:
ToFutexTimespec(const struct timespec * userspace_ts,FutexTimespec * futex_ts)149   static FutexTimespec* ToFutexTimespec(const struct timespec* userspace_ts,
150                                         FutexTimespec* futex_ts) {
151     if (userspace_ts == nullptr) {
152       return nullptr;
153     }
154 
155     using FutexSeconds = decltype(futex_ts->tv_sec);
156     using FutexNanoseconds = decltype(futex_ts->tv_nsec);
157 
158     constexpr auto kMaxSeconds{(std::numeric_limits<FutexSeconds>::max)()};
159     if (userspace_ts->tv_sec > kMaxSeconds) {
160       futex_ts->tv_sec = kMaxSeconds;
161     } else {
162       futex_ts->tv_sec = static_cast<FutexSeconds>(userspace_ts->tv_sec);
163     }
164     futex_ts->tv_nsec = static_cast<FutexNanoseconds>(userspace_ts->tv_nsec);
165     return futex_ts;
166   }
167 };
168 
169 class Futex : public FutexImpl {};
170 
171 }  // namespace synchronization_internal
172 ABSL_NAMESPACE_END
173 }  // namespace absl
174 
175 #endif  // ABSL_INTERNAL_HAVE_FUTEX
176 
177 #endif  // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
178