| // -*- C++ -*- header. | |
| // Copyright (C) 2020-2026 Free Software Foundation, Inc. | |
| // | |
| // This file is part of the GNU ISO C++ Library. This library is free | |
| // software; you can redistribute it and/or modify it under the | |
| // terms of the GNU General Public License as published by the | |
| // Free Software Foundation; either version 3, or (at your option) | |
| // any later version. | |
| // This library is distributed in the hope that it will be useful, | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| // GNU General Public License for more details. | |
| // Under Section 7 of GPL version 3, you are granted additional | |
| // permissions described in the GCC Runtime Library Exception, version | |
| // 3.1, as published by the Free Software Foundation. | |
| // You should have received a copy of the GNU General Public License and | |
| // a copy of the GCC Runtime Library Exception along with this program; | |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| // <http://www.gnu.org/licenses/>. | |
| /** @file bits/atomic_wait.h | |
| * This is an internal header file, included by other library headers. | |
| * Do not attempt to use it directly. @headername{atomic} | |
| */ | |
| #ifndef _GLIBCXX_ATOMIC_WAIT_H | |
| #define _GLIBCXX_ATOMIC_WAIT_H 1 | |
| #ifdef _GLIBCXX_SYSHDR | |
| #pragma GCC system_header | |
| #endif | |
| #include <bits/version.h> | |
| #if __glibcxx_atomic_wait | |
| #include <bits/gthr.h> | |
| #include <ext/numeric_traits.h> | |
| #include <bits/stl_pair.h> | |
| namespace std _GLIBCXX_VISIBILITY(default) | |
| { | |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION | |
| namespace __detail | |
| { | |
| // TODO: this needs to be false for types with padding, e.g. __int20. | |
| // TODO: should this be true only for integral, enum, and pointer types? | |
| template<typename _Tp> | |
| concept __waitable | |
| = is_scalar_v<_Tp> && __builtin_popcountg(sizeof(_Tp)) == 1 | |
| && (sizeof(_Tp) <= sizeof(__UINT64_TYPE__)); | |
| } | |
| #if defined _GLIBCXX_HAVE_LINUX_FUTEX | |
| namespace __detail | |
| { | |
| // Use futex syscall on int objects. | |
| using __platform_wait_t = int; | |
| inline constexpr size_t __platform_wait_alignment = 4; | |
| } | |
| // Defined to true for a subset of __waitable types which are statically | |
| // known to definitely be able to use futex, not a proxy wait. | |
| template<typename _Tp> | |
| inline constexpr bool __platform_wait_uses_type | |
| = __detail::__waitable<_Tp> | |
| && sizeof(_Tp) == sizeof(int) && alignof(_Tp) >= 4; | |
| #elif defined __APPLE__ \ | |
| && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200 | |
| namespace __detail | |
| { | |
| using __platform_wait_t = __INT32_TYPE__; | |
| inline constexpr size_t __platform_wait_alignment = 4; | |
| } | |
| // Defined to true for a subset of __waitable types which are statically | |
| // known to definitely be able to use __ulock_wait, not a proxy wait. | |
| // We know that OS Versions later than 10.15 support 64b wait types even | |
| // though we must make the __platform_wait_t 32b for compatibility with | |
| // earlier versions of __ulock_xxxx. | |
| template<typename _Tp> | |
| inline constexpr bool __platform_wait_uses_type | |
| = __detail::__waitable<_Tp> | |
| # if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101500 | |
| && sizeof(_Tp) == 4 && alignof(_Tp) >= 4; | |
| # else | |
| && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4) | |
| || (sizeof(_Tp) == 8 && alignof(_Tp) >= 8)); | |
| # endif | |
| #elif defined __FreeBSD__ && __SIZEOF_LONG__ == 8 | |
| namespace __detail | |
| { | |
| using __platform_wait_t = __UINT64_TYPE__; | |
| inline constexpr size_t __platform_wait_alignment = 8; | |
| } | |
| template<typename _Tp> | |
| inline constexpr bool __platform_wait_uses_type | |
| = __detail::__waitable<_Tp> | |
| && ((sizeof(_Tp) == 4 && alignof(_Tp) >= 4) | |
| || (sizeof(_Tp) == 8 && alignof(_Tp) >= 8)); | |
| #else | |
| // define _GLIBCXX_HAVE_PLATFORM_WAIT and implement __platform_wait() | |
| // and __platform_notify() if there is a more efficient primitive supported | |
| // by the platform (e.g. __ulock_wait()/__ulock_wake()) which is better than | |
| // a mutex/condvar based wait. | |
| namespace __detail | |
| { | |
| # if defined __OpenBSD__ || defined __DragonFly__ | |
| // These targets provide 32-bit futex-like syscalls. | |
| // We don't currently make use of them, but we want to in future. | |
| using __platform_wait_t = unsigned int; | |
| # elif ATOMIC_LONG_LOCK_FREE == 2 | |
| using __platform_wait_t = unsigned long; | |
| # else | |
| using __platform_wait_t = unsigned int; | |
| # endif | |
| inline constexpr size_t __platform_wait_alignment | |
| = sizeof(__platform_wait_t) < __alignof__(__platform_wait_t) | |
| ? __alignof__(__platform_wait_t) : sizeof(__platform_wait_t); | |
| } // namespace __detail | |
| // This must be false for the general case where we don't know of any | |
| // futex-like syscall. | |
| template<typename> | |
| inline constexpr bool __platform_wait_uses_type = false; | |
| #endif | |
| namespace __detail | |
| { | |
| inline void | |
| __thread_yield() noexcept | |
| { | |
| #if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD | |
| __gthread_yield(); | |
| #endif | |
| } | |
| inline void | |
| __thread_relax() noexcept | |
| { | |
| #if defined __i386__ || defined __x86_64__ | |
| __builtin_ia32_pause(); | |
| #else | |
| __thread_yield(); | |
| #endif | |
| } | |
| // return true if equal | |
| template<typename _Tp> | |
| inline bool | |
| __atomic_eq(const _Tp& __a, const _Tp& __b) | |
| { | |
| // TODO make this do the correct padding bit ignoring comparison | |
| return __builtin_memcmp(std::addressof(__a), std::addressof(__b), | |
| sizeof(_Tp)) == 0; | |
| } | |
| // Storage for up to 64 bits of value, should be considered opaque bits. | |
| using __wait_value_type = __UINT64_TYPE__; | |
| // lightweight std::optional<__wait_value_type> | |
| struct __wait_result_type | |
| { | |
| __wait_value_type _M_val; | |
| unsigned char _M_has_val : 1; // _M_val value was loaded before return. | |
| unsigned char _M_timeout : 1; // Waiting function ended with timeout. | |
| unsigned char _M_unused : 6; // padding | |
| }; | |
| enum class __wait_flags : __UINT_LEAST32_TYPE__ | |
| { | |
| __abi_version = 0x00000000, | |
| // currently unused = 1, | |
| __track_contention = 2, | |
| __do_spin = 4, | |
| __spin_only = 8, // Ignored unless __do_spin is also set. | |
| // __abi_version_mask = 0xff000000, | |
| }; | |
| [[__gnu__::__always_inline__]] | |
| constexpr __wait_flags | |
| operator|(__wait_flags __l, __wait_flags __r) noexcept | |
| { | |
| using _Ut = underlying_type_t<__wait_flags>; | |
| return static_cast<__wait_flags>(static_cast<_Ut>(__l) | |
| | static_cast<_Ut>(__r)); | |
| } | |
| [[__gnu__::__always_inline__]] | |
| constexpr __wait_flags& | |
| operator|=(__wait_flags& __l, __wait_flags __r) noexcept | |
| { return __l = __l | __r; } | |
| // Simple aggregate containing arguments used by implementation details. | |
| struct __wait_args_base | |
| { | |
| __wait_flags _M_flags; | |
| int _M_order = __ATOMIC_ACQUIRE; // Memory order for loads from _M_obj. | |
| __wait_value_type _M_old = 0; // Previous value of *_M_obj. | |
| void* _M_wait_state = nullptr; // For proxy wait and tracking contention. | |
| const void* _M_obj = nullptr; // The address of the object to wait on. | |
| unsigned char _M_obj_size = 0; // The size of that object. | |
| // Test whether _M_flags & __flags is non-zero. | |
| bool | |
| operator&(__wait_flags __flags) const noexcept | |
| { | |
| using _Ut = underlying_type_t<__wait_flags>; | |
| return static_cast<_Ut>(_M_flags) & static_cast<_Ut>(__flags); | |
| } | |
| }; | |
| // Utility for populating a __wait_args_base structure. | |
| struct __wait_args : __wait_args_base | |
| { | |
| template<typename _Tp> requires (!is_same_v<_Tp, __wait_args>) | |
| explicit | |
| __wait_args(const _Tp* __addr, bool __bare_wait = false) noexcept | |
| : __wait_args_base{ _S_flags_for(__addr, __bare_wait) } | |
| { | |
| _M_obj = __addr; // Might be replaced by _M_setup_wait | |
| if constexpr (__waitable<_Tp>) | |
| // __wait_impl might be able to wait directly on __addr | |
| // instead of using a proxy, depending on its size. | |
| _M_obj_size = sizeof(_Tp); | |
| } | |
| __wait_args(const __platform_wait_t* __addr, __platform_wait_t __old, | |
| int __order, bool __bare_wait = false) noexcept | |
| : __wait_args(__addr, __bare_wait) | |
| { | |
| _M_order = __order; | |
| _M_old = __old; | |
| } | |
| __wait_args(const __wait_args&) noexcept = default; | |
| __wait_args& operator=(const __wait_args&) noexcept = default; | |
| template<typename _Tp, typename _ValFn> | |
| _Tp | |
| _M_setup_wait(const _Tp* __addr, _ValFn __vfn) | |
| { | |
| static_assert(is_same_v<_Tp, decay_t<decltype(__vfn())>>); | |
| if constexpr (!__platform_wait_uses_type<_Tp>) | |
| if (_M_setup_proxy_wait(__addr)) | |
| { | |
| // We will use a proxy wait for this object. | |
| // The library has set _M_wait_state, _M_obj, _M_obj_size, | |
| // and _M_old. | |
| // Call __vfn to load the current value from *__addr | |
| // (which must happen after the call to _M_setup_proxy_wait). | |
| return __vfn(); | |
| } | |
| // We will use a futex-like operation to wait on this object, | |
| // so just load the value, store it into _M_old, and return it. | |
| return _M_store(__vfn()); | |
| } | |
| // Called after a wait returns, to prepare to wait again. | |
| template<typename _Tp, typename _ValFn> | |
| _Tp | |
| _M_on_wake(const _Tp* __addr, _ValFn __vfn, __wait_result_type __res) | |
| { | |
| if constexpr (!__platform_wait_uses_type<_Tp>) // maybe a proxy wait | |
| if (_M_obj != __addr) // definitely a proxy wait | |
| { | |
| if (__res._M_has_val) | |
| // Previous wait loaded a recent value from the proxy. | |
| _M_old = __res._M_val; | |
| else // Load a new value from the proxy and store in _M_old. | |
| (void) _M_setup_proxy_wait(nullptr); | |
| // Read the current value of *__addr | |
| return __vfn(); | |
| } | |
| if (__res._M_has_val) // The previous wait loaded a recent value. | |
| { | |
| _M_old = __res._M_val; | |
| // Not a proxy wait, so the value in __res._M_val was loaded | |
| // from *__addr and we don't need to call __vfn(). | |
| if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__)) | |
| return __builtin_bit_cast(_Tp, (__UINT64_TYPE__)_M_old); | |
| else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__)) | |
| return __builtin_bit_cast(_Tp, (__UINT32_TYPE__)_M_old); | |
| else if constexpr (sizeof(_Tp) == sizeof(__UINT16_TYPE__)) | |
| return __builtin_bit_cast(_Tp, (__UINT16_TYPE__)_M_old); | |
| else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__)) | |
| return __builtin_bit_cast(_Tp, (__UINT8_TYPE__)_M_old); | |
| else // Should be a proxy wait for this size! | |
| __glibcxx_assert(false); | |
| } | |
| else | |
| return _M_store(__vfn()); | |
| } | |
| private: | |
| // Store __val in _M_old. | |
| // pre: This must be a non-proxy wait. | |
| template<typename _Tp> | |
| [[__gnu__::__always_inline__]] | |
| _Tp | |
| _M_store(_Tp __val) | |
| { | |
| // We have to consider various sizes, because a future libstdc++.so | |
| // might enable non-proxy waits for additional sizes. | |
| if constexpr (sizeof(_Tp) == sizeof(__UINT64_TYPE__)) | |
| _M_old = __builtin_bit_cast(__UINT64_TYPE__, __val); | |
| else if constexpr (sizeof(_Tp) == sizeof(__UINT32_TYPE__)) | |
| _M_old = __builtin_bit_cast(__UINT32_TYPE__, __val); | |
| else if constexpr (sizeof(_Tp) == sizeof(__UINT16_TYPE__)) | |
| _M_old = __builtin_bit_cast(__UINT16_TYPE__, __val); | |
| else if constexpr (sizeof(_Tp) == sizeof(__UINT8_TYPE__)) | |
| _M_old = __builtin_bit_cast(__UINT8_TYPE__, __val); | |
| else // Should be a proxy wait for this size! | |
| __glibcxx_assert(false); | |
| return __val; | |
| } | |
| // Prepare `*this` for a call to `__wait_impl` or `__wait_until_impl`. | |
| // See comments in src/c++20/atomic.cc for more details. | |
| bool | |
| _M_setup_proxy_wait(const void* __addr); | |
| template<typename _Tp> | |
| static constexpr __wait_flags | |
| _S_flags_for(const _Tp*, bool __bare_wait) noexcept | |
| { | |
| using enum __wait_flags; | |
| __wait_flags __res = __abi_version | __do_spin; | |
| if (!__bare_wait) | |
| __res |= __track_contention; | |
| return __res; | |
| } | |
| }; | |
| __wait_result_type | |
| __wait_impl(const void* __addr, __wait_args_base&); | |
| void | |
| __notify_impl(const void* __addr, bool __all, const __wait_args_base&); | |
| } // namespace __detail | |
| // Wait on __addr while __pred(__vfn()) is false. | |
| // If __bare_wait is false, increment a counter while waiting. | |
| // For callers that keep their own count of waiters, use __bare_wait=true. | |
| // The effect of __vfn() must be an atomic load from __addr and nothing else. | |
| template<typename _Tp, typename _Pred, typename _ValFn> | |
| void | |
| __atomic_wait_address(const _Tp* __addr, _Pred&& __pred, _ValFn&& __vfn, | |
| bool __bare_wait = false) noexcept | |
| { | |
| __detail::__wait_args __args{ __addr, __bare_wait }; | |
| _Tp __val = __args._M_setup_wait(__addr, __vfn); | |
| while (!__pred(__val)) | |
| { | |
| auto __res = __detail::__wait_impl(__addr, __args); | |
| __val = __args._M_on_wake(__addr, __vfn, __res); | |
| } | |
| // C++26 will return __val | |
| } | |
| // Wait on __addr while *__addr == __old is true. | |
| inline void | |
| __atomic_wait_address_v(const __detail::__platform_wait_t* __addr, | |
| __detail::__platform_wait_t __old, | |
| int __order, bool __bare_wait = false) | |
| { | |
| // This function must not be used if __wait_impl might use a proxy wait: | |
| __glibcxx_assert(__platform_wait_uses_type<__detail::__platform_wait_t>); | |
| __detail::__wait_args __args{ __addr, __old, __order, __bare_wait }; | |
| // C++26 will not ignore the return value here | |
| __detail::__wait_impl(__addr, __args); | |
| } | |
| // Wait on __addr while __vfn() == __old is true. | |
| template<typename _Tp, typename _ValFn> | |
| void | |
| __atomic_wait_address_v(const _Tp* __addr, _Tp __old, | |
| _ValFn __vfn) noexcept | |
| { | |
| auto __pfn = [&](const _Tp& __val) | |
| { return !__detail::__atomic_eq(__old, __val); }; | |
| std::__atomic_wait_address(__addr, __pfn, forward<_ValFn>(__vfn)); | |
| } | |
| template<typename _Tp> | |
| void | |
| __atomic_notify_address(const _Tp* __addr, bool __all, | |
| bool __bare_wait = false) noexcept | |
| { | |
| __detail::__wait_args __args{ __addr, __bare_wait }; | |
| __detail::__notify_impl(__addr, __all, __args); | |
| } | |
| _GLIBCXX_END_NAMESPACE_VERSION | |
| } // namespace std | |
| #endif // __glibcxx_atomic_wait | |
| #endif // _GLIBCXX_ATOMIC_WAIT_H |