/src/boost/boost/atomic/detail/futex.hpp
Line | Count | Source |
1 | | /* |
2 | | * Distributed under the Boost Software License, Version 1.0. |
3 | | * (See accompanying file LICENSE_1_0.txt or copy at |
4 | | * http://www.boost.org/LICENSE_1_0.txt) |
5 | | * |
6 | | * Copyright (c) 2020-2025 Andrey Semashev |
7 | | */ |
8 | | /*! |
9 | | * \file atomic/detail/futex.hpp |
10 | | * |
11 | | * This header defines wrappers around futex syscall. |
12 | | * |
13 | | * http://man7.org/linux/man-pages/man2/futex.2.html |
14 | | * https://man.openbsd.org/futex |
15 | | */ |
16 | | |
17 | | #ifndef BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_ |
18 | | #define BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_ |
19 | | |
20 | | #include <boost/atomic/detail/config.hpp> |
21 | | |
22 | | #ifdef BOOST_HAS_PRAGMA_ONCE |
23 | | #pragma once |
24 | | #endif |
25 | | |
26 | | #if defined(__linux__) |
27 | | |
28 | | #include <sys/syscall.h> |
29 | | |
30 | | #if defined(SYS_futex) |
31 | | #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex |
32 | | #elif defined(SYS_futex_time64) |
33 | | // On some 32-bit targets (e.g. riscv32) SYS_futex is not defined and instead SYS_futex_time64 is implemented, |
34 | | // which is equivalent to SYS_futex but uses 64-bit time_t. |
35 | | #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex_time64 |
36 | | #define BOOST_ATOMIC_DETAIL_FUTEX_TIME64 |
37 | | #elif defined(__NR_futex) |
38 | | // Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex. |
39 | | #define BOOST_ATOMIC_DETAIL_SYS_FUTEX __NR_futex |
40 | | #endif |
41 | | |
42 | | #elif defined(__OpenBSD__) |
43 | | |
44 | | // OpenBSD provides futex(2) function wrapper since OpenBSD 6.2 (https://man.openbsd.org/OpenBSD-6.2/futex.2). |
45 | | // It has also removed syscall(2) interface: |
46 | | // https://github.com/openbsd/src/commit/cafeb892b121ee89c39c2b940e8ccd6950f50009 |
47 | | |
48 | | #include <sys/param.h> |
49 | | #include <cerrno> |
50 | | |
51 | | #if OpenBSD >= 201711 |
52 | | #define BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX |
53 | | #endif // OpenBSD >= 201711 |
54 | | |
55 | | #elif defined(__NETBSD__) || defined(__NetBSD__) |
56 | | |
57 | | #include <sys/syscall.h> |
58 | | |
59 | | #if defined(SYS___futex) |
60 | | // NetBSD defines SYS___futex, which has slightly different parameters. Basically, it has decoupled timeout and val2 parameters: |
61 | | // int __futex(int *addr1, int op, int val1, const struct timespec *timeout, int *addr2, int val2, int val3); |
62 | | // https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/syscall.h |
63 | | // http://bxr.su/NetBSD/sys/kern/sys_futex.c |
64 | | #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS___futex |
65 | | #define BOOST_ATOMIC_DETAIL_NETBSD_FUTEX |
66 | | #endif // defined(SYS___futex) |
67 | | |
68 | | #endif |
69 | | |
70 | | #if defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX) || defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX) |
71 | | |
72 | | #if defined(__linux__) |
73 | | #include <linux/futex.h> |
74 | | #else |
75 | | #include <sys/futex.h> |
76 | | #endif |
77 | | #include <time.h> // timespec |
78 | | #include <cstdint> |
79 | | #include <boost/atomic/detail/intptr.hpp> |
80 | | #include <boost/atomic/detail/header.hpp> |
81 | | |
82 | | #define BOOST_ATOMIC_DETAIL_HAS_FUTEX |
83 | | |
84 | | // Note: On Android, futex.h is lacking many definitions, but the actual Linux kernel supports the API in full. |
85 | | #if defined(FUTEX_WAIT_BITSET) |
86 | | #define BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET FUTEX_WAIT_BITSET |
87 | | #elif defined(__ANDROID__) |
88 | | #define BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET 9 |
89 | | #endif |
90 | | |
91 | | #if defined(FUTEX_PRIVATE_FLAG) |
92 | | #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG FUTEX_PRIVATE_FLAG |
93 | | #elif defined(__ANDROID__) |
94 | | #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 128 |
95 | | #else |
96 | | #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 0 |
97 | | #endif |
98 | | |
99 | | #if defined(FUTEX_CLOCK_REALTIME) |
100 | | #define BOOST_ATOMIC_DETAIL_FUTEX_CLOCK_REALTIME FUTEX_CLOCK_REALTIME |
101 | | #elif defined(__ANDROID__) |
102 | | #define BOOST_ATOMIC_DETAIL_FUTEX_CLOCK_REALTIME 256 |
103 | | #endif |
104 | | |
105 | | namespace boost { |
106 | | namespace atomics { |
107 | | namespace detail { |
108 | | |
109 | | #if defined(BOOST_ATOMIC_DETAIL_FUTEX_TIME64) |
110 | | |
111 | | //! An equivalent of `timespec` that uses 64-bit members when the userland `timespec` is 32-bit |
112 | | struct futex_timespec |
113 | | { |
114 | | std::int64_t tv_sec; |
115 | | std::int64_t tv_nsec; |
116 | | |
117 | | futex_timespec() = default; |
118 | | explicit futex_timespec(::timespec ts) noexcept : |
119 | | tv_sec(ts.tv_sec), tv_nsec(ts.tv_nsec) |
120 | | {} |
121 | | }; |
122 | | |
123 | | #else // defined(BOOST_ATOMIC_DETAIL_FUTEX_TIME64) |
124 | | |
125 | | using futex_timespec = ::timespec; |
126 | | |
127 | | #endif // defined(BOOST_ATOMIC_DETAIL_FUTEX_TIME64) |
128 | | |
129 | | //! Invokes an operation on the futex |
130 | | BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, const futex_timespec* timeout = nullptr, void* addr2 = nullptr, unsigned int val3 = 0u) noexcept |
131 | 0 | { |
132 | 0 | #if defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX) |
133 | 0 | return ::futex |
134 | 0 | ( |
135 | 0 | static_cast< volatile std::uint32_t* >(addr1), |
136 | 0 | op, |
137 | 0 | static_cast< int >(val1), |
138 | 0 | timeout, |
139 | 0 | static_cast< volatile std::uint32_t* >(addr2) |
140 | 0 | ); |
141 | 0 | #elif defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX) |
142 | 0 | // Pass 0 in val2. |
143 | 0 | return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, 0u, val3); |
144 | 0 | #else |
145 | 0 | return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, val3); |
146 | 0 | #endif |
147 | 0 | } |
148 | | |
149 | | //! Invokes an operation on the futex |
150 | | BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, unsigned int val2, void* addr2 = nullptr, unsigned int val3 = 0u) noexcept |
151 | 0 | { |
152 | 0 | #if defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX) |
153 | 0 | return ::futex |
154 | 0 | ( |
155 | 0 | static_cast< volatile std::uint32_t* >(addr1), |
156 | 0 | op, |
157 | 0 | static_cast< int >(val1), |
158 | 0 | reinterpret_cast< const futex_timespec* >(static_cast< atomics::detail::uintptr_t >(val2)), |
159 | 0 | static_cast< volatile std::uint32_t* >(addr2) |
160 | 0 | ); |
161 | 0 | #elif defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX) |
162 | 0 | // Pass nullptr in timeout. |
163 | 0 | return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< void* >(nullptr), addr2, val2, val3); |
164 | 0 | #else |
165 | 0 | return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< atomics::detail::uintptr_t >(val2), addr2, val3); |
166 | 0 | #endif |
167 | 0 | } |
168 | | |
169 | | //! Checks that the value \c pval is \c expected and blocks |
170 | | BOOST_FORCEINLINE int futex_wait(void* pval, unsigned int expected, int flags) noexcept |
171 | 0 | { |
172 | 0 | int res = futex_invoke(pval, FUTEX_WAIT | flags, expected); |
173 | 0 | #if defined(OpenBSD) && (OpenBSD < 202111) |
174 | 0 | // In older OpenBSD versions, futex(2) returned error code directly instead of setting errno and returning -1. |
175 | 0 | // This was fixed in OpenBSD 7.0 (https://github.com/openbsd/src/commit/3288ea8fbfe504db25b57dd18b664a1aa377e4bf). |
176 | 0 | // This primarily affects FUTEX_WAIT. For FUTEX_WAKE and FUTEX_REQUEUE the returned value may be positive |
177 | 0 | // on successful completion of the call and there seem to be no errors that can be returned. Other functions |
178 | 0 | // are not supported on OpenBSD 7.0 and older. |
179 | 0 | if (res > 0) |
180 | 0 | { |
181 | 0 | errno = res; |
182 | 0 | res = -1; |
183 | 0 | } |
184 | 0 | #endif // defined(OpenBSD) && (OpenBSD < 202111) |
185 | 0 | return res; |
186 | 0 | } |
187 | | |
188 | | //! Checks that the value \c pval is \c expected and blocks until timeout |
189 | | BOOST_FORCEINLINE int futex_wait_for(void* pval, unsigned int expected, futex_timespec const& timeout, int flags) noexcept |
190 | 0 | { |
191 | 0 | int res = futex_invoke(pval, FUTEX_WAIT | flags, expected, &timeout); |
192 | 0 | #if defined(OpenBSD) && (OpenBSD < 202111) |
193 | 0 | // See the comment in futex_wait |
194 | 0 | if (res > 0) |
195 | 0 | { |
196 | 0 | errno = res; |
197 | 0 | res = -1; |
198 | 0 | } |
199 | 0 | #endif // defined(OpenBSD) && (OpenBSD < 202111) |
200 | 0 | return res; |
201 | 0 | } |
202 | | |
203 | | #if defined(BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET) |
204 | | |
205 | | //! Checks that the value \c pval is \c expected and blocks until timeout |
206 | | BOOST_FORCEINLINE int futex_wait_until(void* pval, unsigned int expected, futex_timespec const& timeout, int flags) noexcept |
207 | 0 | { |
208 | 0 | return futex_invoke(pval, BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET | flags, expected, &timeout, nullptr, ~static_cast< unsigned int >(0u)); |
209 | 0 | } |
210 | | |
211 | | #endif // defined(BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET) |
212 | | |
213 | | //! Wakes the specified number of threads waiting on the futex |
214 | | BOOST_FORCEINLINE int futex_signal(void* pval, int flags, unsigned int count = 1u) noexcept |
215 | 0 | { |
216 | 0 | return futex_invoke(pval, FUTEX_WAKE | flags, count); |
217 | 0 | } |
218 | | |
219 | | //! Wakes all threads waiting on the futex |
220 | | BOOST_FORCEINLINE int futex_broadcast(void* pval, int flags) noexcept |
221 | 0 | { |
222 | 0 | return futex_signal(pval, flags, (~static_cast< unsigned int >(0u)) >> 1u); |
223 | 0 | } |
224 | | |
225 | | //! Wakes the wake_count threads waiting on the futex pval1 and requeues up to requeue_count of the blocked threads onto another futex pval2 |
226 | | BOOST_FORCEINLINE int futex_requeue |
227 | | ( |
228 | | void* pval1, |
229 | | void* pval2, |
230 | | int flags, |
231 | | unsigned int wake_count = 1u, |
232 | | unsigned int requeue_count = (~static_cast< unsigned int >(0u)) >> 1u |
233 | | ) noexcept |
234 | 0 | { |
235 | 0 | return futex_invoke(pval1, FUTEX_REQUEUE | flags, wake_count, requeue_count, pval2); |
236 | 0 | } |
237 | | |
238 | | } // namespace detail |
239 | | } // namespace atomics |
240 | | } // namespace boost |
241 | | |
242 | | #include <boost/atomic/detail/footer.hpp> |
243 | | |
244 | | #endif // defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX) || defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX) |
245 | | |
246 | | #endif // BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_ |