libstdc++
atomic_wait.h
Go to the documentation of this file.
1// -*- C++ -*- header.
2
3// Copyright (C) 2020-2025 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25/** @file bits/atomic_wait.h
26 * This is an internal header file, included by other library headers.
27 * Do not attempt to use it directly. @headername{atomic}
28 */
29
30#ifndef _GLIBCXX_ATOMIC_WAIT_H
31#define _GLIBCXX_ATOMIC_WAIT_H 1
32
33#ifdef _GLIBCXX_SYSHDR
34#pragma GCC system_header
35#endif
36
37#include <bits/version.h>
38
39#if __glibcxx_atomic_wait
40#include <bits/gthr.h>
41#include <ext/numeric_traits.h>
42
43#include <bits/stl_pair.h>
44
45namespace std _GLIBCXX_VISIBILITY(default)
46{
47_GLIBCXX_BEGIN_NAMESPACE_VERSION
48
49 namespace __detail
50 {
51 // TODO: this needs to be false for types with padding, e.g. __int20.
52 // TODO: should this be true only for integral, enum, and pointer types?
53 template<typename _Tp>
54 concept __waitable
55 = is_scalar_v<_Tp> && __builtin_popcountg(sizeof(_Tp)) == 1
56 && (sizeof(_Tp) <= sizeof(__UINT64_TYPE__));
57 }
58
59#if defined _GLIBCXX_HAVE_LINUX_FUTEX
60 namespace __detail
61 {
62 // Use futex syscall on int objects.
63 using __platform_wait_t = int;
64 inline constexpr size_t __platform_wait_alignment = 4;
65 }
66 // Defined to true for a subset of __waitable types which are statically
67 // known to definitely be able to use futex, not a proxy wait.
68 template<typename _Tp>
69 inline constexpr bool __platform_wait_uses_type
70 = __detail::__waitable<_Tp>
71 && sizeof(_Tp) == sizeof(int) && alignof(_Tp) >= 4;
72#else
73// define _GLIBCX_HAVE_PLATFORM_WAIT and implement __platform_wait()
74// and __platform_notify() if there is a more efficient primitive supported
75// by the platform (e.g. __ulock_wait()/__ulock_wake()) which is better than
76// a mutex/condvar based wait.
77 namespace __detail
78 {
79# if ATOMIC_LONG_LOCK_FREE == 2
80 using __platform_wait_t = unsigned long;
81# else
82 using __platform_wait_t = unsigned int;
83# endif
84 inline constexpr size_t __platform_wait_alignment
85 = sizeof(__platform_wait_t) < __alignof__(__platform_wait_t)
86 ? __alignof__(__platform_wait_t) : sizeof(__platform_wait_t);
87 } // namespace __detail
88
89 // This must be false for the general case where we don't know of any
90 // futex-like syscall.
91 template<typename>
92 inline constexpr bool __platform_wait_uses_type = false;
93#endif
94
95 namespace __detail
96 {
97 inline void
98 __thread_yield() noexcept
99 {
100#if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD
101 __gthread_yield();
102#endif
103 }
104
105 inline void
106 __thread_relax() noexcept
107 {
108#if defined __i386__ || defined __x86_64__
109 __builtin_ia32_pause();
110#else
111 __thread_yield();
112#endif
113 }
114
115 // return true if equal
116 template<typename _Tp>
117 inline bool
118 __atomic_eq(const _Tp& __a, const _Tp& __b)
119 {
120 // TODO make this do the correct padding bit ignoring comparison
121 return __builtin_memcmp(&__a, &__b, sizeof(_Tp)) == 0;
122 }
123
124 // Storage for up to 64 bits of value, should be considered opaque bits.
125 using __wait_value_type = __UINT64_TYPE__;
126
127 // lightweight std::optional<__wait_value_type>
128 struct __wait_result_type
129 {
130 __wait_value_type _M_val;
131 unsigned char _M_has_val : 1; // _M_val value was loaded before return.
132 unsigned char _M_timeout : 1; // Waiting function ended with timeout.
133 unsigned char _M_unused : 6; // padding
134 };
135
136 enum class __wait_flags : __UINT_LEAST32_TYPE__
137 {
138 __abi_version = 0x00000000,
139 // currently unused = 1,
140 __track_contention = 2,
141 __do_spin = 4,
142 __spin_only = 8, // Ignored unless __do_spin is also set.
143 // __abi_version_mask = 0xff000000,
144 };
145
146 [[__gnu__::__always_inline__]]
147 constexpr __wait_flags
148 operator|(__wait_flags __l, __wait_flags __r) noexcept
149 {
151 return static_cast<__wait_flags>(static_cast<_Ut>(__l)
152 | static_cast<_Ut>(__r));
153 }
154
155 [[__gnu__::__always_inline__]]
156 constexpr __wait_flags&
157 operator|=(__wait_flags& __l, __wait_flags __r) noexcept
158 { return __l = __l | __r; }
159
160 // Simple aggregate containing arguments used by implementation details.
161 struct __wait_args_base
162 {
163 __wait_flags _M_flags;
164 int _M_order = __ATOMIC_ACQUIRE;
165 __wait_value_type _M_old = 0;
166 void* _M_wait_state = nullptr;
167 const void* _M_obj = nullptr; // The address of the object to wait on.
168 unsigned char _M_obj_size = 0; // The size of that object.
169
170 // Test whether _M_flags & __flags is non-zero.
171 bool
172 operator&(__wait_flags __flags) const noexcept
173 {
174 using _Ut = underlying_type_t<__wait_flags>;
175 return static_cast<_Ut>(_M_flags) & static_cast<_Ut>(__flags);
176 }
177 };
178
179 // Utility for populating a __wait_args_base structure.
180 struct __wait_args : __wait_args_base
181 {
182 template<typename _Tp> requires (!is_same_v<_Tp, __wait_args>)
183 explicit
184 __wait_args(const _Tp* __addr, bool __bare_wait = false) noexcept
185 : __wait_args_base{ _S_flags_for(__addr, __bare_wait) }
186 {
187 _M_obj = __addr; // Might be replaced by _M_setup_wait
188 if constexpr (__waitable<_Tp>)
189 // __wait_impl might be able to wait directly on __addr
190 // instead of using a proxy, depending on its size.
191 _M_obj_size = sizeof(_Tp);
192 }
193
194 __wait_args(const __platform_wait_t* __addr, __platform_wait_t __old,
195 int __order, bool __bare_wait = false) noexcept
196 : __wait_args(__addr, __bare_wait)
197 {
198 _M_order = __order;
199 _M_old = __old;
200 }
201
202 __wait_args(const __wait_args&) noexcept = default;
203 __wait_args& operator=(const __wait_args&) noexcept = default;
204
205 template<typename _Tp, typename _ValFn>
206 _Tp
207 _M_setup_wait(const _Tp* __addr, _ValFn __vfn,
208 __wait_result_type __res = {})
209 {
210 static_assert(is_same_v<_Tp, decay_t<decltype(__vfn())>>);
211
212 if (__res._M_has_val) // A previous wait loaded a recent value.
213 {
214 _M_old = __res._M_val;
215 if constexpr (!__platform_wait_uses_type<_Tp>)
216 {
217 // __res._M_val might be the value of a proxy wait object,
218 // not the value of *__addr. Call __vfn() to get new value.
219 return __vfn();
220 }
221 // Not a proxy wait, so the value in __res._M_val was loaded
222 // from *__addr and we don't need to call __vfn().
223 else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__))
224 return __builtin_bit_cast(_Tp, (__UINT32_TYPE__)_M_old);
225 else if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
226 return __builtin_bit_cast(_Tp, (__UINT64_TYPE__)_M_old);
227 else
228 {
229 static_assert(false); // Unsupported size
230 return {};
231 }
232 }
233
234 if constexpr (!__platform_wait_uses_type<_Tp>)
235 if (_M_setup_proxy_wait(__addr))
236 {
237 // We will use a proxy wait for this object.
238 // The library has set _M_obj and _M_obj_size and _M_old.
239 // Call __vfn to load the current value from *__addr
240 // (which must happen after the call to _M_setup_proxy_wait).
241 return __vfn();
242 }
243
244 // We will use a futex-like operation to wait on this object,
245 // and so can just load the value and store it into _M_old.
246 auto __val = __vfn();
247 // We have to consider various sizes, because a future libstdc++.so
248 // might enable non-proxy waits for additional sizes.
249 if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
250 _M_old = __builtin_bit_cast(__UINT64_TYPE__, __val);
251 else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__))
252 _M_old = __builtin_bit_cast(__UINT32_TYPE__, __val);
253 else if constexpr (sizeof(_Tp) == sizeof(__UINT16_TYPE__))
254 _M_old = __builtin_bit_cast(__UINT16_TYPE__, __val);
255 else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__))
256 _M_old = __builtin_bit_cast(__UINT8_TYPE__, __val);
257 else // _M_setup_proxy_wait should have returned true for this type!
258 __glibcxx_assert(false);
259 return __val;
260 }
261
262 private:
263 // Returns true if a proxy wait will be used for __addr, false otherwise.
264 // If true, _M_wait_state, _M_obj, _M_obj_size, and _M_old are set.
265 // If false, data members are unchanged.
266 bool
267 _M_setup_proxy_wait(const void* __addr);
268
269 template<typename _Tp>
270 static constexpr __wait_flags
271 _S_flags_for(const _Tp*, bool __bare_wait) noexcept
272 {
273 using enum __wait_flags;
274 __wait_flags __res = __abi_version | __do_spin;
275 if (!__bare_wait)
276 __res |= __track_contention;
277 return __res;
278 }
279 };
280
281 __wait_result_type
282 __wait_impl(const void* __addr, __wait_args_base&);
283
284 void
285 __notify_impl(const void* __addr, bool __all, const __wait_args_base&);
286 } // namespace __detail
287
288 // Wait on __addr while __pred(__vfn()) is false.
289 // If __bare_wait is false, increment a counter while waiting.
290 // For callers that keep their own count of waiters, use __bare_wait=true.
291 // The effect of __vfn() must be an atomic load from __addr and nothing else.
292 template<typename _Tp, typename _Pred, typename _ValFn>
293 void
294 __atomic_wait_address(const _Tp* __addr, _Pred&& __pred, _ValFn&& __vfn,
295 bool __bare_wait = false) noexcept
296 {
297 __detail::__wait_args __args{ __addr, __bare_wait };
298 _Tp __val = __args._M_setup_wait(__addr, __vfn);
299 while (!__pred(__val))
300 {
301 auto __res = __detail::__wait_impl(__addr, __args);
302 __val = __args._M_setup_wait(__addr, __vfn, __res);
303 }
304 // C++26 will return __val
305 }
306
307 // Wait on __addr while *__addr == __old is true.
308 inline void
309 __atomic_wait_address_v(const __detail::__platform_wait_t* __addr,
310 __detail::__platform_wait_t __old,
311 int __order, bool __bare_wait = false)
312 {
313 // This function must not be used if __wait_impl might use a proxy wait:
314 __glibcxx_assert(__platform_wait_uses_type<__detail::__platform_wait_t>);
315
316 __detail::__wait_args __args{ __addr, __old, __order, __bare_wait };
317 // C++26 will not ignore the return value here
318 __detail::__wait_impl(__addr, __args);
319 }
320
321 // Wait on __addr while __vfn() == __old is true.
322 template<typename _Tp, typename _ValFn>
323 void
324 __atomic_wait_address_v(const _Tp* __addr, _Tp __old,
325 _ValFn __vfn) noexcept
326 {
327 auto __pfn = [&](const _Tp& __val)
328 { return !__detail::__atomic_eq(__old, __val); };
329 std::__atomic_wait_address(__addr, __pfn, forward<_ValFn>(__vfn));
330 }
331
332 template<typename _Tp>
333 void
334 __atomic_notify_address(const _Tp* __addr, bool __all,
335 bool __bare_wait = false) noexcept
336 {
337 __detail::__wait_args __args{ __addr, __bare_wait };
338 __detail::__notify_impl(__addr, __all, __args);
339 }
340
341_GLIBCXX_END_NAMESPACE_VERSION
342} // namespace std
343#endif // __glibcxx_atomic_wait
344#endif // _GLIBCXX_ATOMIC_WAIT_H
typename underlying_type< _Tp >::type underlying_type_t
Alias template for underlying_type.
Definition type_traits:2910
typename decay< _Tp >::type decay_t
Alias template for decay.
Definition type_traits:2894
constexpr _Tp && forward(typename std::remove_reference< _Tp >::type &__t) noexcept
Forward an lvalue.
Definition move.h:72
ISO C++ entities toplevel namespace is std.
constexpr bitset< _Nb > operator|(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition bitset:1614
constexpr bitset< _Nb > operator&(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition bitset:1604
Implementation details not part of the namespace std interface.