libstdc++
atomic_wait.h
Go to the documentation of this file.
1 // -*- C++ -*- header.
2 
3 // Copyright (C) 2020-2026 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 
45 namespace 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 #elif defined __APPLE__ \
73  && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
74  namespace __detail
75  {
76  using __platform_wait_t = __INT32_TYPE__;
77  inline constexpr size_t __platform_wait_alignment = 4;
78  }
79  // Defined to true for a subset of __waitable types which are statically
80  // known to definitely be able to use __ulock_wait, not a proxy wait.
81  // We know that OS Versions later than 10.15 support 64b wait types even
82  // though we must make the __platform_wait_t 32b for compatibility with
83  // earlier versions of __ulock_xxxx.
84  template<typename _Tp>
85  inline constexpr bool __platform_wait_uses_type
86  = __detail::__waitable<_Tp>
87 # if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101500
88  && sizeof(_Tp) == 4 && alignof(_Tp) >= 4;
89 # else
90  && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4)
91  || (sizeof(_Tp) == 8 && alignof(_Tp) >= 8));
92 # endif
93 #elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8
94  namespace __detail
95  {
96  using __platform_wait_t = __UINT64_TYPE__;
97  inline constexpr size_t __platform_wait_alignment = 8;
98  }
99  template<typename _Tp>
100  inline constexpr bool __platform_wait_uses_type
101  = __detail::__waitable<_Tp>
102  && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4)
103  || (sizeof(_Tp) == 8 && alignof(_Tp) >= 8));
104 #else
105 // define _GLIBCX_HAVE_PLATFORM_WAIT and implement __platform_wait()
106 // and __platform_notify() if there is a more efficient primitive supported
107 // by the platform (e.g. __ulock_wait()/__ulock_wake()) which is better than
108 // a mutex/condvar based wait.
109  namespace __detail
110  {
111 # if defined __OpenBSD__ || defined __DragonFly__
112  // These targets provide 32-bit futex-like syscalls.
113  // We don't currently make use of them, but we want to in future.
114  using __platform_wait_t = unsigned int;
115 # elif ATOMIC_LONG_LOCK_FREE == 2
116  using __platform_wait_t = unsigned long;
117 # else
118  using __platform_wait_t = unsigned int;
119 # endif
120  inline constexpr size_t __platform_wait_alignment
121  = sizeof(__platform_wait_t) < __alignof__(__platform_wait_t)
122  ? __alignof__(__platform_wait_t) : sizeof(__platform_wait_t);
123  } // namespace __detail
124 
125  // This must be false for the general case where we don't know of any
126  // futex-like syscall.
127  template<typename>
128  inline constexpr bool __platform_wait_uses_type = false;
129 #endif
130 
131  namespace __detail
132  {
133  inline void
134  __thread_yield() noexcept
135  {
136 #if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD
137  __gthread_yield();
138 #endif
139  }
140 
141  inline void
142  __thread_relax() noexcept
143  {
144 #if defined __i386__ || defined __x86_64__
145  __builtin_ia32_pause();
146 #else
147  __thread_yield();
148 #endif
149  }
150 
151  // return true if equal
152  template<typename _Tp>
153  inline bool
154  __atomic_eq(const _Tp& __a, const _Tp& __b)
155  {
156  // TODO make this do the correct padding bit ignoring comparison
157  return __builtin_memcmp(std::addressof(__a), std::addressof(__b),
158  sizeof(_Tp)) == 0;
159  }
160 
161  // Storage for up to 64 bits of value, should be considered opaque bits.
162  using __wait_value_type = __UINT64_TYPE__;
163 
164  // lightweight std::optional<__wait_value_type>
165  struct __wait_result_type
166  {
167  __wait_value_type _M_val;
168  unsigned char _M_has_val : 1; // _M_val value was loaded before return.
169  unsigned char _M_timeout : 1; // Waiting function ended with timeout.
170  unsigned char _M_unused : 6; // padding
171  };
172 
173  enum class __wait_flags : __UINT_LEAST32_TYPE__
174  {
175  __abi_version = 0x00000000,
176  // currently unused = 1,
177  __track_contention = 2,
178  __do_spin = 4,
179  __spin_only = 8, // Ignored unless __do_spin is also set.
180  // __abi_version_mask = 0xff000000,
181  };
182 
183  [[__gnu__::__always_inline__]]
184  constexpr __wait_flags
185  operator|(__wait_flags __l, __wait_flags __r) noexcept
186  {
187  using _Ut = underlying_type_t<__wait_flags>;
188  return static_cast<__wait_flags>(static_cast<_Ut>(__l)
189  | static_cast<_Ut>(__r));
190  }
191 
192  [[__gnu__::__always_inline__]]
193  constexpr __wait_flags&
194  operator|=(__wait_flags& __l, __wait_flags __r) noexcept
195  { return __l = __l | __r; }
196 
197  // Simple aggregate containing arguments used by implementation details.
198  struct __wait_args_base
199  {
200  __wait_flags _M_flags;
201  int _M_order = __ATOMIC_ACQUIRE; // Memory order for loads from _M_obj.
202  __wait_value_type _M_old = 0; // Previous value of *_M_obj.
203  void* _M_wait_state = nullptr; // For proxy wait and tracking contention.
204  const void* _M_obj = nullptr; // The address of the object to wait on.
205  unsigned char _M_obj_size = 0; // The size of that object.
206 
207  // Test whether _M_flags & __flags is non-zero.
208  bool
209  operator&(__wait_flags __flags) const noexcept
210  {
211  using _Ut = underlying_type_t<__wait_flags>;
212  return static_cast<_Ut>(_M_flags) & static_cast<_Ut>(__flags);
213  }
214  };
215 
216  // Utility for populating a __wait_args_base structure.
217  struct __wait_args : __wait_args_base
218  {
219  template<typename _Tp> requires (!is_same_v<_Tp, __wait_args>)
220  explicit
221  __wait_args(const _Tp* __addr, bool __bare_wait = false) noexcept
222  : __wait_args_base{ _S_flags_for(__addr, __bare_wait) }
223  {
224  _M_obj = __addr; // Might be replaced by _M_setup_wait
225  if constexpr (__waitable<_Tp>)
226  // __wait_impl might be able to wait directly on __addr
227  // instead of using a proxy, depending on its size.
228  _M_obj_size = sizeof(_Tp);
229  }
230 
231  __wait_args(const __platform_wait_t* __addr, __platform_wait_t __old,
232  int __order, bool __bare_wait = false) noexcept
233  : __wait_args(__addr, __bare_wait)
234  {
235  _M_order = __order;
236  _M_old = __old;
237  }
238 
239  __wait_args(const __wait_args&) noexcept = default;
240  __wait_args& operator=(const __wait_args&) noexcept = default;
241 
242  template<typename _Tp, typename _ValFn>
243  _Tp
244  _M_setup_wait(const _Tp* __addr, _ValFn __vfn)
245  {
246  static_assert(is_same_v<_Tp, decay_t<decltype(__vfn())>>);
247 
248  if constexpr (!__platform_wait_uses_type<_Tp>)
249  if (_M_setup_proxy_wait(__addr))
250  {
251  // We will use a proxy wait for this object.
252  // The library has set _M_wait_state, _M_obj, _M_obj_size,
253  // and _M_old.
254  // Call __vfn to load the current value from *__addr
255  // (which must happen after the call to _M_setup_proxy_wait).
256  return __vfn();
257  }
258 
259  // We will use a futex-like operation to wait on this object,
260  // so just load the value, store it into _M_old, and return it.
261  return _M_store(__vfn());
262  }
263 
264  // Called after a wait returns, to prepare to wait again.
265  template<typename _Tp, typename _ValFn>
266  _Tp
267  _M_on_wake(const _Tp* __addr, _ValFn __vfn, __wait_result_type __res)
268  {
269  if constexpr (!__platform_wait_uses_type<_Tp>) // maybe a proxy wait
270  if (_M_obj != __addr) // definitely a proxy wait
271  {
272  if (__res._M_has_val)
273  // Previous wait loaded a recent value from the proxy.
274  _M_old = __res._M_val;
275  else // Load a new value from the proxy and store in _M_old.
276  (void) _M_setup_proxy_wait(nullptr);
277  // Read the current value of *__addr
278  return __vfn();
279  }
280 
281  if (__res._M_has_val) // The previous wait loaded a recent value.
282  {
283  _M_old = __res._M_val;
284 
285  // Not a proxy wait, so the value in __res._M_val was loaded
286  // from *__addr and we don't need to call __vfn().
287  if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
288  return __builtin_bit_cast(_Tp, (__UINT64_TYPE__)_M_old);
289  else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__))
290  return __builtin_bit_cast(_Tp, (__UINT32_TYPE__)_M_old);
291  else if constexpr (sizeof(_Tp) == sizeof(__UINT16_TYPE__))
292  return __builtin_bit_cast(_Tp, (__UINT16_TYPE__)_M_old);
293  else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__))
294  return __builtin_bit_cast(_Tp, (__UINT8_TYPE__)_M_old);
295  else // Should be a proxy wait for this size!
296  __glibcxx_assert(false);
297  }
298  else
299  return _M_store(__vfn());
300  }
301 
302  private:
303  // Store __val in _M_old.
304  // pre: This must be a non-proxy wait.
305  template<typename _Tp>
306  [[__gnu__::__always_inline__]]
307  _Tp
308  _M_store(_Tp __val)
309  {
310  // We have to consider various sizes, because a future libstdc++.so
311  // might enable non-proxy waits for additional sizes.
312  if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__))
313  _M_old = __builtin_bit_cast(__UINT64_TYPE__, __val);
314  else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__))
315  _M_old = __builtin_bit_cast(__UINT32_TYPE__, __val);
316  else if constexpr (sizeof(_Tp) == sizeof(__UINT16_TYPE__))
317  _M_old = __builtin_bit_cast(__UINT16_TYPE__, __val);
318  else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__))
319  _M_old = __builtin_bit_cast(__UINT8_TYPE__, __val);
320  else // Should be a proxy wait for this size!
321  __glibcxx_assert(false);
322  return __val;
323  }
324 
325  // Prepare `*this` for a call to `__wait_impl` or `__wait_until_impl`.
326  // See comments in src/c++20/atomic.cc for more details.
327  bool
328  _M_setup_proxy_wait(const void* __addr);
329 
330  template<typename _Tp>
331  static constexpr __wait_flags
332  _S_flags_for(const _Tp*, bool __bare_wait) noexcept
333  {
334  using enum __wait_flags;
335  __wait_flags __res = __abi_version | __do_spin;
336  if (!__bare_wait)
337  __res |= __track_contention;
338  return __res;
339  }
340  };
341 
342  __wait_result_type
343  __wait_impl(const void* __addr, __wait_args_base&);
344 
345  void
346  __notify_impl(const void* __addr, bool __all, const __wait_args_base&);
347  } // namespace __detail
348 
349  // Wait on __addr while __pred(__vfn()) is false.
350  // If __bare_wait is false, increment a counter while waiting.
351  // For callers that keep their own count of waiters, use __bare_wait=true.
352  // The effect of __vfn() must be an atomic load from __addr and nothing else.
353  template<typename _Tp, typename _Pred, typename _ValFn>
354  void
355  __atomic_wait_address(const _Tp* __addr, _Pred&& __pred, _ValFn&& __vfn,
356  bool __bare_wait = false) noexcept
357  {
358  __detail::__wait_args __args{ __addr, __bare_wait };
359  _Tp __val = __args._M_setup_wait(__addr, __vfn);
360  while (!__pred(__val))
361  {
362  auto __res = __detail::__wait_impl(__addr, __args);
363  __val = __args._M_on_wake(__addr, __vfn, __res);
364  }
365  // C++26 will return __val
366  }
367 
368  // Wait on __addr while *__addr == __old is true.
369  inline void
370  __atomic_wait_address_v(const __detail::__platform_wait_t* __addr,
371  __detail::__platform_wait_t __old,
372  int __order, bool __bare_wait = false)
373  {
374  // This function must not be used if __wait_impl might use a proxy wait:
375  __glibcxx_assert(__platform_wait_uses_type<__detail::__platform_wait_t>);
376 
377  __detail::__wait_args __args{ __addr, __old, __order, __bare_wait };
378  // C++26 will not ignore the return value here
379  __detail::__wait_impl(__addr, __args);
380  }
381 
382  // Wait on __addr while __vfn() == __old is true.
383  template<typename _Tp, typename _ValFn>
384  void
385  __atomic_wait_address_v(const _Tp* __addr, _Tp __old,
386  _ValFn __vfn) noexcept
387  {
388  auto __pfn = [&](const _Tp& __val)
389  { return !__detail::__atomic_eq(__old, __val); };
390  std::__atomic_wait_address(__addr, __pfn, forward<_ValFn>(__vfn));
391  }
392 
393  template<typename _Tp>
394  void
395  __atomic_notify_address(const _Tp* __addr, bool __all,
396  bool __bare_wait = false) noexcept
397  {
398  __detail::__wait_args __args{ __addr, __bare_wait };
399  __detail::__notify_impl(__addr, __all, __args);
400  }
401 
402 _GLIBCXX_END_NAMESPACE_VERSION
403 } // namespace std
404 #endif // __glibcxx_atomic_wait
405 #endif // _GLIBCXX_ATOMIC_WAIT_H
typename decay< _Tp >::type decay_t
Alias template for decay.
Definition: type_traits:2942
constexpr _Tp * addressof(_Tp &__r) noexcept
Returns the actual address of the object or function referenced by r, even in the presence of an over...
Definition: move.h:176
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:1618
constexpr bitset< _Nb > operator|(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition: bitset:1628