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 namespace __detail
49 {
50#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
51#define _GLIBCXX_HAVE_PLATFORM_WAIT 1
52 using __platform_wait_t = int;
53 inline constexpr size_t __platform_wait_alignment = 4;
54#else
55// define _GLIBCX_HAVE_PLATFORM_WAIT and implement __platform_wait()
56// and __platform_notify() if there is a more efficient primitive supported
57// by the platform (e.g. __ulock_wait()/__ulock_wake()) which is better than
58// a mutex/condvar based wait.
59# if ATOMIC_LONG_LOCK_FREE == 2
60 using __platform_wait_t = unsigned long;
61# else
62 using __platform_wait_t = unsigned int;
63# endif
64 inline constexpr size_t __platform_wait_alignment
65 = __alignof__(__platform_wait_t);
66#endif
67 } // namespace __detail
68
69 template<typename _Tp>
70 inline constexpr bool __platform_wait_uses_type
71#ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
72 = is_scalar_v<_Tp>
73 && ((sizeof(_Tp) == sizeof(__detail::__platform_wait_t))
74 && (alignof(_Tp) >= __detail::__platform_wait_alignment));
75#else
76 = false;
77#endif
78
79 namespace __detail
80 {
81 inline void
82 __thread_yield() noexcept
83 {
84#if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD
85 __gthread_yield();
86#endif
87 }
88
89 inline void
90 __thread_relax() noexcept
91 {
92#if defined __i386__ || defined __x86_64__
93 __builtin_ia32_pause();
94#else
95 __thread_yield();
96#endif
97 }
98
99 // return true if equal
100 template<typename _Tp>
101 inline bool
102 __atomic_eq(const _Tp& __a, const _Tp& __b)
103 {
104 // TODO make this do the correct padding bit ignoring comparison
105 return __builtin_memcmp(&__a, &__b, sizeof(_Tp)) == 0;
106 }
107
108 // lightweight std::optional<__platform_wait_t>
109 struct __wait_result_type
110 {
111 __platform_wait_t _M_val;
112 unsigned char _M_has_val : 1; // _M_val value was loaded before return.
113 unsigned char _M_timeout : 1; // Waiting function ended with timeout.
114 unsigned char _M_unused : 6; // padding
115 };
116
117 enum class __wait_flags : __UINT_LEAST32_TYPE__
118 {
119 __abi_version = 0,
120 __proxy_wait = 1,
121 __track_contention = 2,
122 __do_spin = 4,
123 __spin_only = 8, // Ignored unless __do_spin is also set.
124 // __abi_version_mask = 0xffff0000,
125 };
126
127 [[__gnu__::__always_inline__]]
128 constexpr __wait_flags
129 operator|(__wait_flags __l, __wait_flags __r) noexcept
130 {
132 return static_cast<__wait_flags>(static_cast<_Ut>(__l)
133 | static_cast<_Ut>(__r));
134 }
135
136 [[__gnu__::__always_inline__]]
137 constexpr __wait_flags&
138 operator|=(__wait_flags& __l, __wait_flags __r) noexcept
139 { return __l = __l | __r; }
140
141 // Simple aggregate containing arguments used by implementation details.
142 struct __wait_args_base
143 {
144 __wait_flags _M_flags;
145 int _M_order = __ATOMIC_ACQUIRE;
146 __platform_wait_t _M_old = 0;
147 void* _M_wait_state = nullptr;
148
149 // Test whether _M_flags & __flags is non-zero.
150 bool
151 operator&(__wait_flags __flags) const noexcept
152 {
153 using _Ut = underlying_type_t<__wait_flags>;
154 return static_cast<_Ut>(_M_flags) & static_cast<_Ut>(__flags);
155 }
156 };
157
158 // Utility for populating a __wait_args_base structure.
159 struct __wait_args : __wait_args_base
160 {
161 template<typename _Tp> requires (!is_same_v<_Tp, __wait_args>)
162 explicit
163 __wait_args(const _Tp* __addr, bool __bare_wait = false) noexcept
164 : __wait_args_base{ _S_flags_for(__addr, __bare_wait) }
165 { }
166
167 __wait_args(const __platform_wait_t* __addr, __platform_wait_t __old,
168 int __order, bool __bare_wait = false) noexcept
169 : __wait_args_base{ _S_flags_for(__addr, __bare_wait), __order, __old }
170 { }
171
172 __wait_args(const __wait_args&) noexcept = default;
173 __wait_args& operator=(const __wait_args&) noexcept = default;
174
175 template<typename _ValFn,
176 typename _Tp = decay_t<decltype(std::declval<_ValFn&>()())>>
177 _Tp
178 _M_setup_wait(const void* __addr, _ValFn __vfn,
179 __wait_result_type __res = {})
180 {
181 if constexpr (__platform_wait_uses_type<_Tp>)
182 {
183 // If the wait is not proxied, the value we check when waiting
184 // is the value of the atomic variable itself.
185
186 if (__res._M_has_val) // The previous wait loaded a recent value.
187 {
188 _M_old = __res._M_val;
189 return __builtin_bit_cast(_Tp, __res._M_val);
190 }
191 else // Load the value from __vfn
192 {
193 _Tp __val = __vfn();
194 _M_old = __builtin_bit_cast(__platform_wait_t, __val);
195 return __val;
196 }
197 }
198 else // It's a proxy wait and the proxy's _M_ver is used.
199 {
200 if (__res._M_has_val) // The previous wait loaded a recent value.
201 _M_old = __res._M_val;
202 else // Load _M_ver from the proxy (must happen before __vfn()).
203 _M_load_proxy_wait_val(__addr);
204 return __vfn();
205 }
206 }
207
208 private:
209 // Populates _M_wait_state and _M_old from the proxy for __addr.
210 void
211 _M_load_proxy_wait_val(const void* __addr);
212
213 template<typename _Tp>
214 static constexpr __wait_flags
215 _S_flags_for(const _Tp*, bool __bare_wait) noexcept
216 {
217 using enum __wait_flags;
218 __wait_flags __res = __abi_version | __do_spin;
219 if (!__bare_wait)
220 __res |= __track_contention;
221 if constexpr (!__platform_wait_uses_type<_Tp>)
222 __res |= __proxy_wait;
223 return __res;
224 }
225 };
226
227 __wait_result_type
228 __wait_impl(const void* __addr, __wait_args_base&);
229
230 void
231 __notify_impl(const void* __addr, bool __all, const __wait_args_base&);
232 } // namespace __detail
233
234 // Wait on __addr while __pred(__vfn()) is false.
235 // If __bare_wait is false, increment a counter while waiting.
236 // For callers that keep their own count of waiters, use __bare_wait=true.
237 template<typename _Tp, typename _Pred, typename _ValFn>
238 void
239 __atomic_wait_address(const _Tp* __addr, _Pred&& __pred, _ValFn&& __vfn,
240 bool __bare_wait = false) noexcept
241 {
242 __detail::__wait_args __args{ __addr, __bare_wait };
243 _Tp __val = __args._M_setup_wait(__addr, __vfn);
244 while (!__pred(__val))
245 {
246 auto __res = __detail::__wait_impl(__addr, __args);
247 __val = __args._M_setup_wait(__addr, __vfn, __res);
248 }
249 // C++26 will return __val
250 }
251
252 // Wait on __addr while *__addr == __old is true.
253 inline void
254 __atomic_wait_address_v(const __detail::__platform_wait_t* __addr,
255 __detail::__platform_wait_t __old,
256 int __order, bool __bare_wait = false)
257 {
258#ifndef _GLIBCXX_HAVE_PLATFORM_WAIT
259 __glibcxx_assert(false); // This function can't be used for proxy wait.
260#endif
261 __detail::__wait_args __args{ __addr, __old, __order, __bare_wait };
262 // C++26 will not ignore the return value here
263 __detail::__wait_impl(__addr, __args);
264 }
265
266 // Wait on __addr while __vfn() == __old is true.
267 template<typename _Tp, typename _ValFn>
268 void
269 __atomic_wait_address_v(const _Tp* __addr, _Tp __old,
270 _ValFn __vfn) noexcept
271 {
272 auto __pfn = [&](const _Tp& __val)
273 { return !__detail::__atomic_eq(__old, __val); };
274 std::__atomic_wait_address(__addr, __pfn, forward<_ValFn>(__vfn));
275 }
276
277 template<typename _Tp>
278 void
279 __atomic_notify_address(const _Tp* __addr, bool __all,
280 bool __bare_wait = false) noexcept
281 {
282 __detail::__wait_args __args{ __addr, __bare_wait };
283 __detail::__notify_impl(__addr, __all, __args);
284 }
285
286_GLIBCXX_END_NAMESPACE_VERSION
287} // namespace std
288#endif // __glibcxx_atomic_wait
289#endif // _GLIBCXX_ATOMIC_WAIT_H
typename underlying_type< _Tp >::type underlying_type_t
Alias template for underlying_type.
Definition type_traits:2908
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:1607
constexpr bitset< _Nb > operator&(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition bitset:1597
Implementation details not part of the namespace std interface.