// -*- C++ -*- header. // Copyright (C) 2020-2025 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 // . /** @file bits/semaphore_base.h * This is an internal header file, included by other library headers. * Do not attempt to use it directly. @headername{semaphore} */ #ifndef _GLIBCXX_SEMAPHORE_BASE_H #define _GLIBCXX_SEMAPHORE_BASE_H 1 #ifdef _GLIBCXX_SYSHDR #pragma GCC system_header #endif #include #ifdef __glibcxx_semaphore // C++ >= 20 && hosted && atomic_wait #include #include #include #include namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __semaphore_impl { using __count_type = ptrdiff_t; static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<__count_type>::__max; constexpr explicit __semaphore_impl(__count_type __count) noexcept : _M_counter(__count) { } __semaphore_impl(const __semaphore_impl&) = delete; __semaphore_impl& operator=(const __semaphore_impl&) = delete; // Load the current counter value. _GLIBCXX_ALWAYS_INLINE __count_type _M_get_current() const noexcept { return __atomic_impl::load(&_M_counter, memory_order::acquire); } // Try to acquire the semaphore (i.e. decrement the counter). // Returns false if the current counter is zero, or if another thread // changes the value first. In the latter case, __cur is set to the new // value. _GLIBCXX_ALWAYS_INLINE bool _M_do_try_acquire(__count_type& __cur) noexcept { if (__cur == 0) return false; // Cannot decrement when it's already zero. return __atomic_impl::compare_exchange_strong(&_M_counter, __cur, __cur - 1, memory_order::acquire, memory_order::relaxed); } // Keep trying to acquire the semaphore in a loop until it succeeds. void _M_acquire() noexcept { auto __vfn = [this]{ return _M_get_current(); }; _Available __is_available{__vfn()}; while (!_M_do_try_acquire(__is_available._M_val)) if (!__is_available()) std::__atomic_wait_address(&_M_counter, __is_available, __vfn, true); } // Try to acquire the semaphore, retrying a small number of times // in case of contention. bool _M_try_acquire() noexcept { // The fastest implementation of this function is just _M_do_try_acquire // but that can fail under contention even when _M_count > 0. // Using _M_try_acquire_for(0ns) will retry a few times in a loop. return _M_try_acquire_for(__detail::__wait_clock_t::duration{}); } template bool _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept { auto __vfn = [this]{ return _M_get_current(); }; _Available __is_available{__vfn()}; while (!_M_do_try_acquire(__is_available._M_val)) if (!__is_available()) if (!std::__atomic_wait_address_until(&_M_counter, __is_available, __vfn, __atime, true)) return false; // timed out return true; } template bool _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept { auto __vfn = [this]{ return _M_get_current(); }; _Available __is_available{__vfn()}; while (!_M_do_try_acquire(__is_available._M_val)) if (!__is_available()) if (!std::__atomic_wait_address_for(&_M_counter, __is_available, __vfn, __rtime, true)) return false; // timed out return true; } _GLIBCXX_ALWAYS_INLINE ptrdiff_t _M_release(ptrdiff_t __update) noexcept { auto __old = __atomic_impl::fetch_add(&_M_counter, __update, memory_order::release); if (__old == 0 && __update > 0) __atomic_notify_address(&_M_counter, true, true); return __old; } private: struct _Available { __count_type _M_val; // Cache of the last value loaded from _M_counter. // Returns true if the cached value is non-zero and so it should be // possible to acquire the semaphore. bool operator()() const noexcept { return _M_val > 0; } // Argument should be the latest value of the counter. // Returns true (and caches the value) if it's non-zero, meaning it // should be possible to acquire the semaphore. Returns false otherwise. bool operator()(__count_type __cur) noexcept { if (__cur == 0) return false; _M_val = __cur; return true; } }; alignas(__atomic_ref<__count_type>::required_alignment) __count_type _M_counter; }; // Optimized specialization using __platform_wait (if available) template struct __platform_semaphore_impl { using __count_type = __detail::__platform_wait_t; static constexpr ptrdiff_t _S_max = _Binary ? 1 : __gnu_cxx::__int_traits<__count_type>::__max; constexpr explicit __platform_semaphore_impl(__count_type __count) noexcept : _M_counter(__count) { } __platform_semaphore_impl(__platform_semaphore_impl&) = delete; __platform_semaphore_impl& operator=(const __platform_semaphore_impl&) = delete; // Load the current counter value. _GLIBCXX_ALWAYS_INLINE __count_type _M_get_current() const noexcept { if constexpr (_Binary) return 1; // Not necessarily true, but optimistically assume it is. else return __atomic_impl::load(&_M_counter, memory_order::acquire); } // Try to acquire the semaphore (i.e. decrement the counter). // Returns false if the current counter is zero, or if another thread // changes the value first. In the latter case, __cur is set to the new // value. _GLIBCXX_ALWAYS_INLINE bool _M_do_try_acquire(__count_type& __cur) noexcept { if (__cur == 0) return false; // Cannot decrement when it's already zero. return __atomic_impl::compare_exchange_strong(&_M_counter, __cur, __cur - 1, memory_order::acquire, memory_order::relaxed); } // Keep trying to acquire the semaphore in a loop until it succeeds. void _M_acquire() noexcept { auto __val = _M_get_current(); while (!_M_do_try_acquire(__val)) if (__val == 0) { std::__atomic_wait_address_v(&_M_counter, __val, __ATOMIC_ACQUIRE, true); __val = _M_get_current(); } } // Try to acquire the semaphore. bool _M_try_acquire() noexcept { if constexpr (_Binary) { __count_type __val = 1; // Do not expect much contention on binary semaphore, only try once. return _M_do_try_acquire(__val); } else // Fastest implementation of this function is just _M_do_try_acquire // but that can fail under contention even when _M_count > 0. // Using _M_try_acquire_for(0ns) will retry a few times in a loop. return _M_try_acquire_for(__detail::__wait_clock_t::duration{}); } template bool _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept { auto __val = _M_get_current(); while (!_M_do_try_acquire(__val)) if (__val == 0) { if (!std::__atomic_wait_address_until_v(&_M_counter, 0, __ATOMIC_ACQUIRE, __atime, true)) return false; // timed out __val = _M_get_current(); } return true; } template bool _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept { auto __val = _M_get_current(); while (!_M_do_try_acquire(__val)) if (__val == 0) { if (!std::__atomic_wait_address_for_v(&_M_counter, 0, __ATOMIC_ACQUIRE, __rtime, true)) return false; // timed out __val = _M_get_current(); } return true; } _GLIBCXX_ALWAYS_INLINE ptrdiff_t _M_release(ptrdiff_t __update) noexcept { auto __old = __atomic_impl::fetch_add(&_M_counter, __update, memory_order::release); if (__old == 0 && __update > 0) __atomic_notify_address(&_M_counter, true, true); return __old; } protected: alignas(__detail::__platform_wait_alignment) __count_type _M_counter; }; template using _Semaphore_impl = __conditional_t<__platform_wait_uses_type<_Tp> && _Max <= __gnu_cxx::__int_traits<_Tp>::__max, __platform_semaphore_impl<(_Max <= 1)>, __semaphore_impl>; _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // __glibcxx_semaphore #endif // _GLIBCXX_SEMAPHORE_BASE_H