#pragma GCC system_header
#endif
+#include <bits/version.h>
+
+#ifdef __glibcxx_semaphore // C++ >= 20 && hosted && atomic_wait
#include <bits/atomic_base.h>
#include <bits/chrono.h>
-#if __glibcxx_atomic_wait
#include <bits/atomic_timed_wait.h>
#include <ext/numeric_traits.h>
-#endif // __cpp_lib_atomic_wait
-
-#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
-# include <cerrno> // errno, EINTR, EAGAIN etc.
-# include <limits.h> // SEM_VALUE_MAX
-# include <semaphore.h> // sem_t, sem_init, sem_wait, sem_post etc.
-#elif defined(_GLIBCXX_USE_POSIX_SEMAPHORE)
-# warning "POSIX semaphore not available, ignoring _GLIBCXX_USE_POSIX_SEMAPHORE"
-# undef _GLIBCXX_USE_POSIX_SEMAPHORE
-#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
-#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
- struct __platform_semaphore
+ template<bool _Platform_wait>
+ struct __semaphore_base
{
- using __clock_t = chrono::system_clock;
-#ifdef SEM_VALUE_MAX
- static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
-#else
- static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX;
-#endif
-
- explicit __platform_semaphore(ptrdiff_t __count) noexcept
- {
- sem_init(&_M_semaphore, 0, __count);
- }
-
- __platform_semaphore(const __platform_semaphore&) = delete;
- __platform_semaphore& operator=(const __platform_semaphore&) = delete;
-
- ~__platform_semaphore()
- { sem_destroy(&_M_semaphore); }
-
- _GLIBCXX_ALWAYS_INLINE void
- _M_acquire() noexcept
- {
- while (sem_wait(&_M_semaphore))
- if (errno != EINTR)
- std::__terminate();
- }
-
- _GLIBCXX_ALWAYS_INLINE bool
- _M_try_acquire() noexcept
- {
- while (sem_trywait(&_M_semaphore))
- {
- if (errno == EAGAIN) // already locked
- return false;
- else if (errno != EINTR)
- std::__terminate();
- // else got EINTR so retry
- }
- return true;
- }
-
- _GLIBCXX_ALWAYS_INLINE void
- _M_release(ptrdiff_t __update) noexcept
- {
- for(; __update != 0; --__update)
- if (sem_post(&_M_semaphore))
- std::__terminate();
- }
-
- bool
- _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
- noexcept
- {
- auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
- auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
+ using __count_type = __conditional_t<_Platform_wait,
+ __detail::__platform_wait_t,
+ ptrdiff_t>;
- struct timespec __ts =
- {
- static_cast<std::time_t>(__s.time_since_epoch().count()),
- static_cast<long>(__ns.count())
- };
+ static constexpr ptrdiff_t _S_max
+ = __gnu_cxx::__int_traits<__count_type>::__max;
- while (sem_timedwait(&_M_semaphore, &__ts))
- {
- if (errno == ETIMEDOUT)
- return false;
- else if (errno != EINTR)
- std::__terminate();
- }
- return true;
- }
-
- template<typename _Clock, typename _Duration>
- bool
- _M_try_acquire_until(const chrono::time_point<_Clock,
- _Duration>& __atime) noexcept
- {
- if constexpr (std::is_same_v<__clock_t, _Clock>)
- {
- using _Dur = __clock_t::duration;
- return _M_try_acquire_until_impl(chrono::ceil<_Dur>(__atime));
- }
- else
- {
- // TODO: if _Clock is monotonic_clock we could use
- // sem_clockwait with CLOCK_MONOTONIC.
+ constexpr explicit
+ __semaphore_base(__count_type __count) noexcept
+ : _M_counter(__count)
+ { }
- const typename _Clock::time_point __c_entry = _Clock::now();
- const auto __s_entry = __clock_t::now();
- const auto __delta = __atime - __c_entry;
- const auto __s_atime = __s_entry + __delta;
- if (_M_try_acquire_until_impl(__s_atime))
- return true;
+ __semaphore_base(const __semaphore_base&) = delete;
+ __semaphore_base& operator=(const __semaphore_base&) = delete;
- // We got a timeout when measured against __clock_t but
- // we need to check against the caller-supplied clock
- // to tell whether we should return a timeout.
- return (_Clock::now() < __atime);
- }
- }
-
- template<typename _Rep, typename _Period>
- _GLIBCXX_ALWAYS_INLINE bool
- _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
- noexcept
- { return _M_try_acquire_until(__clock_t::now() + __rtime); }
-
- private:
- sem_t _M_semaphore;
- };
-#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
-
-#if __glibcxx_atomic_wait
- struct __atomic_semaphore
- {
- static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
- explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept
- : _M_counter(__count)
- {
- __glibcxx_assert(__count >= 0 && __count <= _S_max);
- }
-
- __atomic_semaphore(const __atomic_semaphore&) = delete;
- __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
-
- static _GLIBCXX_ALWAYS_INLINE __detail::__platform_wait_t
- _S_get_current(__detail::__platform_wait_t* __counter) noexcept
+ static _GLIBCXX_ALWAYS_INLINE __count_type
+ _S_get_current(__count_type* __counter) noexcept
{
return __atomic_impl::load(__counter, memory_order::acquire);
}
static _GLIBCXX_ALWAYS_INLINE bool
- _S_do_try_acquire(__detail::__platform_wait_t* __counter,
- __detail::__platform_wait_t __old) noexcept
+ _S_do_try_acquire(__count_type* __counter, __count_type __old) noexcept
{
if (__old == 0)
return false;
_M_acquire() noexcept
{
auto const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
- auto const __pred = [this](__detail::__platform_wait_t __cur)
- { return _S_do_try_acquire(&this->_M_counter, __cur); };
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
std::__atomic_wait_address(&_M_counter, __pred, __vfn, true);
}
_M_try_acquire() noexcept
{
auto const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
- auto const __pred = [this](__detail::__platform_wait_t __cur)
- { return _S_do_try_acquire(&this->_M_counter, __cur); };
- return __atomic_wait_address_for(&_M_counter, __pred, __vfn,
- __detail::__wait_clock_t::duration(),
- true);
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ using __detail::__wait_clock_t;
+ return std::__atomic_wait_address_for(&_M_counter, __pred, __vfn,
+ __wait_clock_t::duration(),
+ true);
}
template<typename _Clock, typename _Duration>
_GLIBCXX_ALWAYS_INLINE bool
- _M_try_acquire_until(const chrono::time_point<_Clock,
- _Duration>& __atime) noexcept
+ _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
{
auto const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
- auto const __pred = [this](__detail::__platform_wait_t __cur)
- { return _S_do_try_acquire(&this->_M_counter, __cur); };
- return std::__atomic_wait_address_until(&_M_counter,
- __pred, __vfn, __atime, true);
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ return std::__atomic_wait_address_until(&_M_counter, __pred, __vfn,
+ __atime, true);
}
template<typename _Rep, typename _Period>
_M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
{
auto const __vfn = [this]{ return _S_get_current(&this->_M_counter); };
- auto const __pred = [this](__detail::__platform_wait_t __cur)
- { return _S_do_try_acquire(&this->_M_counter, __cur); };
- return std::__atomic_wait_address_for(&_M_counter,
- __pred, __vfn, __rtime, true);
+ auto const __pred = [this](__count_type __cur) {
+ return _S_do_try_acquire(&this->_M_counter, __cur);
+ };
+ return std::__atomic_wait_address_for(&_M_counter, __pred, __vfn,
+ __rtime, true);
}
- _GLIBCXX_ALWAYS_INLINE void
+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
_M_release(ptrdiff_t __update) noexcept
{
- if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
- return;
- if (__update > 1)
- __atomic_notify_address(&_M_counter, true, true);
- else
+ auto __old = __atomic_impl::fetch_add(&_M_counter, __update,
+ memory_order::release);
+ if (__old == 0 && __update > 0)
__atomic_notify_address(&_M_counter, true, true);
-// FIXME - Figure out why this does not wake a waiting thread
-// __atomic_notify_address_bare(&_M_counter, false);
+ return __old;
}
private:
- alignas(__detail::__platform_wait_alignment)
- __detail::__platform_wait_t _M_counter;
+ alignas(_Platform_wait ? __detail::__platform_wait_alignment
+ : __alignof__(__count_type))
+ __count_type _M_counter;
};
-#endif // __cpp_lib_atomic_wait
-// Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
-// use of Posix semaphores (sem_t). Doing so however, alters the ABI.
-#if defined __glibcxx_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
- using __semaphore_impl = __atomic_semaphore;
-#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
- using __semaphore_impl = __platform_semaphore;
-#endif
+ template<ptrdiff_t _Max>
+ using __semaphore_impl
+ = __semaphore_base<(_Max <= __semaphore_base<true>::_S_max)>;
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
+#endif // __glibcxx_semaphore
#endif // _GLIBCXX_SEMAPHORE_BASE_H
+++ /dev/null
-// 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.
-
-// You should have received a copy of the GNU General Public License along
-// with this library; see the file COPYING3. If not see
-// <http://www.gnu.org/licenses/>.
-
-// { dg-do run { target c++20 } }
-// { dg-additional-options "-pthread" { target pthread } }
-// { dg-require-gthreads "" }
-// { dg-add-options libatomic }
-
-#include <semaphore>
-#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
-#include <chrono>
-#include <thread>
-#include <atomic>
-#include <testsuite_hooks.h>
-
-void test01()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(2);
- s._M_acquire();
-
- auto const dur = 250ms;
- {
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( s._M_try_acquire_for(dur) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff < dur );
- }
-
- {
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( !s._M_try_acquire_for(dur) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff >= dur );
- }
-}
-
-void test02()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(1);
- std::atomic<int> a(0), b(0);
- std::thread t([&] {
- a.wait(0);
- auto const dur = 250ms;
- VERIFY( !s._M_try_acquire_for(dur) );
- b++;
- b.notify_one();
-
- a.wait(1);
- VERIFY( s._M_try_acquire_for(dur) );
- b++;
- b.notify_one();
- });
- t.detach();
-
- s._M_acquire();
- a++;
- a.notify_one();
- b.wait(0);
- s._M_release(1);
- a++;
- a.notify_one();
-
- b.wait(1);
-}
-
-void test03()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(2);
- s._M_acquire();
-
- auto const dur = 250ms;
- {
- auto const at = std::chrono::system_clock::now() + dur;
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( s._M_try_acquire_until(at) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff < dur );
- }
-
- {
- auto const at = std::chrono::system_clock::now() + dur;
- auto const t0 = std::chrono::steady_clock::now();
- VERIFY( !s._M_try_acquire_until(at) );
- auto const diff = std::chrono::steady_clock::now() - t0;
- VERIFY( diff >= dur );
- }
-}
-
-void test04()
-{
- using namespace std::chrono_literals;
- std::__platform_semaphore s(1);
- std::atomic<int> a(0), b(0);
- std::thread t([&] {
- a.wait(0);
- auto const dur = 250ms;
- {
- auto const at = std::chrono::system_clock::now() + dur;
- VERIFY( !s._M_try_acquire_until(at) );
-
- b++;
- b.notify_one();
- }
-
- a.wait(1);
- {
- auto const at = std::chrono::system_clock::now() + dur;
- VERIFY( s._M_try_acquire_until(at) );
- }
- b++;
- b.notify_one();
- });
- t.detach();
-
- s._M_acquire();
- a++;
- a.notify_one();
- b.wait(0);
- s._M_release(1);
- a++;
- a.notify_one();
-
- b.wait(1);
-}
-#endif
-
-int main()
-{
-#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
- test01();
- test02();
- test03();
- test04();
-#endif
- return 0;
-}