* [PATCH] Add C++2a synchronization support @ 2020-05-10 0:01 Thomas Rodgers 2020-05-11 14:05 ` Jonathan Wakely 0 siblings, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-05-10 0:01 UTC (permalink / raw) To: gcc-patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 3028 bytes --] * Note, this patch supersedes my previous atomic wait and semaphore patches. Add support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: le patch --] [-- Type: text/x-patch, Size: 82165 bytes --] From 436ab6fd5286a6467792263fbfbd603ba0f0c04d Mon Sep 17 00:00:00 2001 From: Thomas Rodgers <rodgert@appliantology.com> Date: Mon, 6 Apr 2020 17:58:47 -0700 Subject: [PATCH] Add C++2a synchronization support Add support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 162 +++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 270 +++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 280 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 270 +++++++++++++++++ libstdc++-v3/include/std/atomic | 61 ++++ libstdc++-v3/include/std/latch | 91 ++++++ libstdc++-v3/include/std/semaphore | 81 +++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 +++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.h | 88 ++++++ .../atomic/wait_notify/integrals.cc | 56 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 ++++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 ++++++ .../30_threads/semaphore/try_acquire_futex.cc | 51 ++++ .../30_threads/semaphore/try_acquire_posix.cc | 169 +++++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 26 files changed, 2235 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 80aeb3f8959..b3ac1a3365f 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index eb437ad8d8d..e73ff8b3e64 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -397,6 +397,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -414,6 +415,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -445,6 +447,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -519,6 +523,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 87fe0bd6000..b2cec0f1722 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,11 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#include <iostream> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +139,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept + { + __atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -803,6 +831,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { + __atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -891,6 +943,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept + { + __atomic_wait(__ptr, *std::__addressof(__old), + [=]() + { return load(__ptr, __m) == *std::__addressof(__old); }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { __atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { __atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1144,6 +1223,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1281,6 +1377,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + private: _Tp* _M_ptr; }; @@ -1376,6 +1488,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1531,6 +1659,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1640,6 +1784,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..10f0fe50ed9 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,270 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + enum class __atomic_wait_status { __no_timeout, __timeout }; + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, + __futex_bitset_match_any = 0xffffffff + }; + + using __platform_wait_clock_t = chrono::steady_clock; + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, + nullptr, __futex_bitset_match_any); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value) + { + return __platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) + return __atomic_wait_status::__no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, + const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::__no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(int32_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { + int32_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) + return __atomic_wait_status::__timeout; + + int32_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::__no_timeout; + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return (__timed_waiters&) __waiters::_S_for(__t); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>::__value) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::__timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..32070a54f40 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,280 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + +#define _GLIBCXX_SPIN_COUNT_1 16 +#define _GLIBCXX_SPIN_COUNT_2 12 + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + template<class _Tp> + struct __platform_wait_uses_type + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, + __platform_wait_t>::value }; +#else + enum { __value = std::false_type::value }; +#endif + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __futex_private_flag = 128, +#else + __futex_private_flag = 0, +#endif + __futex_wait = 0, + __futex_wake = 1, + __futex_wait_bitset = 9, + __futex_wake_bitset = 10, + __futex_wait_private = __futex_wait | __futex_private_flag, + __futex_wake_private = __futex_wake | __futex_private_flag + }; + + void + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + void + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept + { + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); + } +#endif + + struct alignas(64) __waiters + { + int32_t alignas(64) _M_ver = 0; + int32_t alignas(64) _M_wait = 0; + + // TODO make this used only where we don't have futexes + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +#ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +#else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +#endif + + int32_t + _M_enter_wait() noexcept + { + int32_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(int32_t __version) const noexcept + { + int32_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + int32_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + } + + int32_t + _M_waiting() const noexcept + { + int32_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); + } + + static __waiters& + _S_for(void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + int32_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for((void*) __addr)) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<class _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) + { + if (__pred()) + return true; + + if (__i < _GLIBCXX_SPIN_COUNT_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<class _Tp, class _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>::__value) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<class _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>::__value) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..b3c83bbc70b --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,270 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/semaphore.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{atomic} + */ + +#ifndef _GLIBCXX_SEMAPHORE_BASE_H +#define _GLIBCXX_SEMAPHORE_BASE_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> +#include <limits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_t> + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + + __platform_semaphore(ptrdiff_t __count) noexcept + { + static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX"); + auto __e = sem_init(&_M_semaphore, 0, __count); + if (__e) + std::terminate(); + } + + ~__platform_semaphore() + { + auto __e = sem_destroy(&_M_semaphore); + if (__e) + std::terminate(); + } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto __err = sem_wait(&_M_semaphore); + if (__err) + std::terminate(); + } + + template<typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + __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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err) + std::terminate(); + return true; + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + if constexpr (std::is_same<__clock_t, _Clock>::value) + { + return __try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__try_acquire_until_impl(__s_atime)) + return true; + + // 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 + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { return try_acquire_until(__clock_t::now() + __rtime); } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + do + { + auto __err = sem_post(&_M_semaphore); + if (__err) + std::terminate(); + } while (--__update); + } + + private: + sem_t _M_semaphore; + }; +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE + + template<typename _Tp> + struct __atomic_semaphore + { + static constexpr size_t _S_alignment = __alignof__(_Tp); + + __atomic_semaphore(_Tp __count) + : _M_a(__count) + { } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(_S_alignment) _Tp _M_a; + }; + +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_t> + using __semaphore_base = __platform_semaphore<__least_max_t>; +#else +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX + template<ptrdiff_t __least_max_t> + using __semaphore_base = std::conditional<(__least_max_t > 0 + && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()), + __atomic_semaphore<__detail::__platform_wait_t>, + __atomic_semaphore<ptrdiff_t>>::type; + // __platform_semaphore +# else +# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_t> + using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX), + __platform_semaphore<__least_max_t>, + __atomic_semaphore<ptrdiff_t>>::type; +# else + template<ptrdiff_t __least_max_t> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; +# endif +# endif +#endif + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index a455286a784..3f18774031d 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_i.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_i.notify_one(); } + + void notify_all() const noexcept + { _M_i.notify_all(); } +#endif + }; #undef _GLIBCXX20_INIT @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..0099877416e --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,91 @@ +//<latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <limits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + static constexpr size_t _S_alignment = __alignof__(ptrdiff_t); + public: + static constexpr + _GLIBCXX_ALWAYS_INLINE ptrdiff_t + max() noexcept + { return numeric_limits<ptrdiff_t>::max(); } + + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(_S_alignment) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..b51940b46ac --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,81 @@ +//<semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()> + class counting_semaphore + { + __semaphore_base<__least_max_value> _M_sem; + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t max() noexcept + { return __least_max_value; } + + void release(ptrdiff_t __update = 1) + { _M_sem.release(__update); } + + void acquire() + { _M_sem.acquire(); } + + bool try_acquire() noexcept + { return _M_sem.try_acquire(); } + + template<class _Rep, class _Period> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return _M_sem.try_acquire_for(__rel_time); } + + template<class _Clock, class _Duration> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) + { return _M_sem.try_acquire_until(__abs_time); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index c3a5bd26e63..390990282b0 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -188,6 +188,8 @@ #endif #define __cpp_lib_type_identity 201806L #define __cpp_lib_unwrap_ref 201811L +#define __cpp_lib_semaphore 201907L +#define __cpp_lib_latch 201907L #if _GLIBCXX_HOSTED #undef __cpp_lib_array_constexpr diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..0da374ece87 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,88 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + VERIFY(check_atomic_wait_notify(a, b) == b); + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..2afd19a7d14 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..756727f33b3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..10bb500d261 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..b96b8a59c64 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..d38cef86cfc --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..965554a3c28 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc new file mode 100644 index 00000000000..5e05606e97f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +void test01() +{ + // the implementation optimizes for values of least_max_t that can fit + // in a futex, make sure we cover the case where least_max_t doesn't + auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max(); + std::counting_semaphore<least_max_t> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + test01(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..bf99fd3cf8f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + // The implementation supports posix as an implementation strategy + // make sure we cover that case +#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test03() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +void test04() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test05() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + test01(); + test02(); + test03(); + test04(); + test05(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..cc67c5c0bf0 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-05-10 0:01 [PATCH] Add C++2a synchronization support Thomas Rodgers @ 2020-05-11 14:05 ` Jonathan Wakely 2020-05-11 15:43 ` Thomas Rodgers 2020-05-11 20:59 ` Thomas Rodgers 0 siblings, 2 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-05-11 14:05 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++ On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote: >* Note, this patch supersedes my previous atomic wait and semaphore >patches. > >Add support for - > atomic wait/notify_one/notify_all > counting_semaphore > binary_semaphore > latch > > * include/Makefile.am (bits_headers): Add new header. > * include/Makefile.in: Regenerate. > * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. Should be two colons before wait. >diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in >index eb437ad8d8d..e73ff8b3e64 100644 >--- a/libstdc++-v3/include/Makefile.in >+++ b/libstdc++-v3/include/Makefile.in Generated files don't need to be in the patch. >diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >index 87fe0bd6000..b2cec0f1722 100644 >--- a/libstdc++-v3/include/bits/atomic_base.h >+++ b/libstdc++-v3/include/bits/atomic_base.h >@@ -37,6 +37,11 @@ > #include <bits/atomic_lockfree_defines.h> > #include <bits/move.h> > >+#if __cplusplus > 201703L >+#include <bits/atomic_wait.h> >+#include <iostream> <iostream> shouldn't be here (it adds runtime cost, as well as compile-time). >@@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__m)); > } > >+#if __cplusplus > 201703L >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept Please format everything to <= 80 columns (ideally < 80). >+ wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >+ { >+ __atomic_wait(&_M_p, __old, This should be qualified to prevent ADL. >+ [__m, this, __old]() >+ { return this->load(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_notify(&_M_p, false); } Qualify to prevent ADL here too, and all similar calls. >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept >+ { >+ __atomic_wait(__ptr, *std::__addressof(__old), Can't this just be __old instead of *std::__addressof(__old) ? >+ [=]() >+ { return load(__ptr, __m) == *std::__addressof(__old); }); Same here? >diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >new file mode 100644 >index 00000000000..10f0fe50ed9 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >@@ -0,0 +1,270 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/atomic_wait.h> >+ >+#include <chrono> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <sys/time.h> >+#endif >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+ _GLIBCXX_BEGIN_NAMESPACE_VERSION >+ enum class __atomic_wait_status { __no_timeout, __timeout }; Blank line before and after this enum definition please. >+ namespace __detail >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ enum >+ { >+ __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, >+ __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, >+ __futex_bitset_match_any = 0xffffffff >+ }; >+ >+ using __platform_wait_clock_t = chrono::steady_clock; Blank line after this using-decl please. >+ template<typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, >+ const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ Eventually we'll want to move the rest of this function (which doesn't depend on the template argument) into the compiled library, but it's better to be header-only for now. >+ struct timespec __rt = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, >+ nullptr, __futex_bitset_match_any); >+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >+ std::terminate(); >+ return (__platform_wait_clock_t::now() < __atime) >+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >+ } >+ >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+ if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value) This is C++20 so you can use is_same_v here, which uses the intrinsic directly and avoids instantiating the is_same class template. >+ { >+ return __platform_wait_until_impl(__addr, __val, __atime); >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __platform_wait_clock_t::time_point __s_entry = >+ __platform_wait_clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) >+ return __atomic_wait_status::__no_timeout; >+ >+ // 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. >+ if (_Clock::now() < __atime) >+ return __atomic_wait_status::__no_timeout; >+ return __atomic_wait_status::__timeout; >+ } >+ } >+#endif >+ >+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >+ template<typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >+ const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime) The std:: qualification here isn't needed (and doesn't help with keeping the line below 80 cols). >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ __gthread_time_t __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), >+ CLOCK_MONOTONIC, >+ &__ts); >+ return (chrono::steady_clock::now() < __atime) >+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >+ } >+#endif >+ >+ template<typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >+ const chrono::time_point<chrono::system_clock, _Duration>& __atime) >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ __gthread_time_t __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), >+ &__ts); >+ return (chrono::system_clock::now() < __atime) >+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >+ } >+ >+ // return true if timeout >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >+ using __clock_t = chrono::steady_clock; >+#else >+ using __clock_t = chrono::system_clock; >+#endif >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t::time_point __s_entry = __clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (__cond_wait_until_impl(__cv, __lock, __s_atime)) >+ return __atomic_wait_status::__no_timeout; >+ // 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. >+ if (_Clock::now() < __atime) >+ return __atomic_wait_status::__no_timeout; >+ return __atomic_wait_status::__timeout; >+ } >+ >+ struct __timed_waiters : __waiters >+ { >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ _M_do_wait_until(int32_t __version, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+ int32_t __cur = 0; >+ __waiters::__lock_t __l(_M_mtx); >+ while (__cur <= __version) >+ { >+ if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) >+ return __atomic_wait_status::__timeout; >+ >+ int32_t __last = __cur; >+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >+ if (__cur < __last) >+ break; // break the loop if version overflows >+ } >+ return __atomic_wait_status::__no_timeout; >+ } >+ >+ static __timed_waiters& >+ _S_timed_for(void* __t) >+ { >+ static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); >+ return (__timed_waiters&) __waiters::_S_for(__t); >+ } >+ }; >+ } // namespace __detail >+ >+ template<typename _Tp, typename _Pred, >+ typename _Clock, typename _Duration> >+ bool >+ __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ using namespace __detail; >+ >+ if (__atomic_spin(__pred)) Qualify to prevent ADL. >+ return true; >+ >+ auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >+ auto __version = __w._M_enter_wait(); >+ do >+ { >+ __atomic_wait_status __res; >+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >+ { >+ __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old, >+ __atime); >+ } >+ else >+ { >+ __res = __w._M_do_wait_until(__version, __atime); >+ } >+ if (__res == __atomic_wait_status::__timeout) >+ return false; >+ } >+ while (!__pred() && __atime < _Clock::now()); >+ __w._M_leave_wait(); >+ >+ // if timed out, return false >+ return (_Clock::now() < __atime); >+ } >+ >+ template<typename _Tp, typename _Pred, >+ typename _Rep, typename _Period> >+ bool >+ __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { >+ using namespace __detail; >+ >+ if (__atomic_spin(__pred)) >+ return true; >+ >+ if (!__rtime.count()) >+ return false; // no rtime supplied, and spin did not acquire >+ >+ using __dur = chrono::steady_clock::duration; >+ auto __reltime = chrono::duration_cast<__dur>(__rtime); >+ if (__reltime < __rtime) >+ ++__reltime; >+ >+ >+ return __atomic_wait_until(__addr, __old, std::move(__pred), >+ chrono::steady_clock::now() + __reltime); >+ } >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+#endif >diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >new file mode 100644 >index 00000000000..32070a54f40 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_wait.h >@@ -0,0 +1,280 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/gthr.h> >+#include <bits/std_mutex.h> >+#include <bits/unique_lock.h> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <climits> >+#include <unistd.h> >+#include <syscall.h> >+#endif >+ >+#define _GLIBCXX_SPIN_COUNT_1 16 >+#define _GLIBCXX_SPIN_COUNT_2 12 >+ >+// TODO get this from Autoconf >+#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ namespace __detail >+ { >+ using __platform_wait_t = int; >+ >+ template<class _Tp> This should be typename not class. >+ struct __platform_wait_uses_type >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, This should be remove_cv_t. >+ __platform_wait_t>::value }; >+#else >+ enum { __value = std::false_type::value }; >+#endif There's no need to use the C++03 enum hack here, it should just derive from true_type or false_type. template<typename _Tp> struct __platform_wait_uses_type #ifdef _GLIBCXX_HAVE_LINUX_FUTEX : is_same<std::remove_cv_t<_Tp>, __platform_wait_t> #else : false_type #endif { }; Or better yet, just use a variable template: template<typename _Tp> inline constexpr bool __platform_wait_uses_type #ifdef _GLIBCXX_HAVE_LINUX_FUTEX = is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>; #else = false; #endif >+ }; >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ enum >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >+ __futex_private_flag = 128, >+#else >+ __futex_private_flag = 0, >+#endif >+ __futex_wait = 0, >+ __futex_wake = 1, >+ __futex_wait_bitset = 9, >+ __futex_wake_bitset = 10, >+ __futex_wait_private = __futex_wait | __futex_private_flag, >+ __futex_wake_private = __futex_wake | __futex_private_flag >+ }; >+ >+ void >+ __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept >+ { >+ auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); >+ if (__e && !(errno == EINTR || errno == EAGAIN)) >+ std::terminate(); >+ } >+ >+ void >+ __platform_notify(__platform_wait_t* __addr, bool __all) noexcept >+ { >+ syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); >+ } >+#endif >+ >+ struct alignas(64) __waiters Isn't alignas(64) already implied by the first data member? >+ { >+ int32_t alignas(64) _M_ver = 0; >+ int32_t alignas(64) _M_wait = 0; >+ >+ // TODO make this used only where we don't have futexes Don't we always need these even with futexes, for the types that don't use a futex? >+ using __lock_t = std::unique_lock<std::mutex>; >+ mutable __lock_t::mutex_type _M_mtx; >+ >+#ifdef __GTHREAD_COND_INIT >+ mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >+ __waiters() noexcept = default; If we moved std::condition_variable into its own header (or <bits/std_mutex.h>, could we reuse that here instead of using __gthread_cond_t directly? >+#else >+ mutable __gthread_cond_t _M_cv; >+ __waiters() noexcept >+ { >+ __GTHREAD_COND_INIT_FUNCTION(&_M_cond); >+ } >+#endif >+ >+ int32_t >+ _M_enter_wait() noexcept >+ { >+ int32_t __res; >+ __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); >+ __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); >+ return __res; >+ } >+ >+ void >+ _M_leave_wait() noexcept >+ { >+ __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); >+ } >+ >+ void >+ _M_do_wait(int32_t __version) const noexcept >+ { >+ int32_t __cur = 0; >+ while (__cur <= __version) >+ { >+ __waiters::__lock_t __l(_M_mtx); >+ auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); >+ if (__e) >+ std::terminate(); >+ int32_t __last = __cur; >+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >+ if (__cur < __last) >+ break; // break the loop if version overflows >+ } >+ } >+ >+ int32_t >+ _M_waiting() const noexcept >+ { >+ int32_t __res; >+ __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >+ return __res; >+ } >+ >+ void >+ _M_notify(bool __all) noexcept >+ { >+ __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >+ auto __e = __gthread_cond_broadcast(&_M_cv); >+ if (__e) >+ __throw_system_error(__e); >+ } >+ >+ static __waiters& >+ _S_for(void* __t) >+ { >+ const unsigned char __mask = 0xf; >+ static __waiters __w[__mask + 1]; >+ >+ auto __key = _Hash_impl::hash(__t) & __mask; >+ return __w[__key]; >+ } >+ }; >+ >+ struct __waiter >+ { >+ __waiters& _M_w; >+ int32_t _M_version; >+ >+ template<typename _Tp> >+ __waiter(const _Tp* __addr) noexcept >+ : _M_w(__waiters::_S_for((void*) __addr)) >+ , _M_version(_M_w._M_enter_wait()) >+ { } >+ >+ ~__waiter() >+ { _M_w._M_leave_wait(); } >+ >+ void _M_do_wait() noexcept >+ { _M_w._M_do_wait(_M_version); } >+ }; >+ >+ void >+ __thread_relax() noexcept >+ { >+#if defined __i386__ || defined __x86_64__ >+ __builtin_ia32_pause(); >+#elif defined _GLIBCXX_USE_SCHED_YIELD >+ __gthread_yield(); >+#endif >+ } >+ >+ void >+ __thread_yield() noexcept >+ { >+#if defined _GLIBCXX_USE_SCHED_YIELD >+ __gthread_yield(); >+#endif >+ } >+ >+ } // namespace __detail >+ >+ template<class _Pred> s/class/template/ >+ bool >+ __atomic_spin(_Pred __pred) noexcept >+ { >+ for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >+ { >+ if (__pred()) >+ return true; >+ >+ if (__i < _GLIBCXX_SPIN_COUNT_2) >+ __detail::__thread_relax(); >+ else >+ __detail::__thread_yield(); >+ } >+ return false; >+ } >+ >+ template<class _Tp, class _Pred> s/class/template/ >+ void >+ __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >+ { >+ using namespace __detail; >+ if (__atomic_spin(__pred)) >+ return; >+ >+ __waiter __w(__addr); >+ while (!__pred()) >+ { >+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >+ { >+ __platform_wait((__platform_wait_t*)(void*) __addr, __old); >+ } >+ else >+ { >+ // TODO support timed backoff when this can be moved into the lib >+ __w._M_do_wait(); >+ } >+ } >+ } >+ >+ template<class _Tp> s/class/template/ >+ void >+ __atomic_notify(const _Tp* __addr, bool __all) noexcept >+ { >+ using namespace __detail; >+ auto& __w = __waiters::_S_for((void*)__addr); >+ if (!__w._M_waiting()) When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting() ever be true? Won't this always return before notifying? Is there meant to be a __waiter constructed here? >+ return; >+ >+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >+ { >+ __platform_notify((__platform_wait_t*)(void*) __addr, __all); >+ } >+ else >+ { >+ __w._M_notify(__all); >+ } >+ } >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+#endif >diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >new file mode 100644 >index 00000000000..b3c83bbc70b >--- /dev/null >+++ b/libstdc++-v3/include/bits/semaphore_base.h >@@ -0,0 +1,270 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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/semaphore.h Should be bits/semaphore_base.h >+ * This is an internal header file, included by other library headers. >+ * Do not attempt to use it directly. @headername{atomic} Should be @headername{semaphore} >+ */ >+ >+#ifndef _GLIBCXX_SEMAPHORE_BASE_H >+#define _GLIBCXX_SEMAPHORE_BASE_H 1 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/atomic_base.h> >+#include <bits/atomic_timed_wait.h> >+ >+#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) >+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >+#include <semaphore.h> >+#endif >+ >+#include <chrono> >+#include <type_traits> >+#include <limits> <ext/numeric_traits.h> is much smaller than <limits> and should be used for limits of integer types. (I recently added <bits/int_limits.h> too but that was a mistake that I need to fix). >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ template<ptrdiff_t __least_max_t> __least_max_t isn't a type so shouldn't have the _t suffix. >+ struct __platform_semaphore >+ { >+ using __clock_t = chrono::system_clock; >+ >+ __platform_semaphore(ptrdiff_t __count) noexcept Should this constructor be explicit? >+ { >+ static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX"); Our static_assert messages should state the positive condition, not the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX", which is what the real condition is anyway, so you might as well omit the string literal. >+ auto __e = sem_init(&_M_semaphore, 0, __count); >+ if (__e) >+ std::terminate(); >+ } >+ >+ ~__platform_semaphore() >+ { >+ auto __e = sem_destroy(&_M_semaphore); >+ if (__e) >+ std::terminate(); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ acquire() noexcept >+ { >+ auto __err = sem_wait(&_M_semaphore); >+ if (__err) >+ std::terminate(); >+ } >+ >+ template<typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE bool Do we really need this to be always_inline? >+ __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); >+ >+ struct timespec __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ auto __err = sem_timedwait(&_M_semaphore, &__ts); >+ if (__err && (errno == ETIMEDOUT)) >+ return false; >+ else if (__err) >+ std::terminate(); >+ return true; >+ } >+ >+ template<typename _Clock, typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE bool always_inline? >+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ if constexpr (std::is_same<__clock_t, _Clock>::value) is_same_v >+ { >+ return __try_acquire_until_impl(__atime); >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t __s_entry = __clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (__try_acquire_until_impl(__s_atime)) >+ return true; >+ >+ // 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 >+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { return try_acquire_until(__clock_t::now() + __rtime); } >+ >+ template<typename _Clock, typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE void >+ release(ptrdiff_t __update) noexcept >+ { >+ do >+ { >+ auto __err = sem_post(&_M_semaphore); >+ if (__err) >+ std::terminate(); >+ } while (--__update); >+ } >+ >+ private: >+ sem_t _M_semaphore; >+ }; >+#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ >+ template<typename _Tp> >+ struct __atomic_semaphore >+ { >+ static constexpr size_t _S_alignment = __alignof__(_Tp); >+ >+ __atomic_semaphore(_Tp __count) Should this be explicit? >+ : _M_a(__count) >+ { } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ acquire() noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ __atomic_wait(&_M_a, __old, __pred); >+ } >+ >+ bool >+ try_acquire() noexcept >+ { >+ auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ if (__old == 0) >+ return false; >+ >+ return __atomic_spin([this, &__old] >+ { >+ return __atomic_impl::compare_exchange_weak(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }); >+ } >+ >+ template<typename _Clock, typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ return __atomic_wait_until(&_M_a, __old, __pred, __atime); >+ } >+ >+ template<typename _Rep, typename _Period> >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ return __atomic_wait_for(&_M_a, __old, __pred, __rtime); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ release(ptrdiff_t __update) noexcept >+ { >+ if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) >+ return; >+ if (__update > 1) >+ __atomic_impl::notify_all(&_M_a); >+ else >+ __atomic_impl::notify_one(&_M_a); >+ } >+ >+ private: >+ alignas(_S_alignment) _Tp _M_a; Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no need for the _S_alignment constant if it's only used in one place. >+ }; >+ >+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE >+ template<ptrdiff_t __least_max_t> Rename __least_max_t here too. >+ using __semaphore_base = __platform_semaphore<__least_max_t>; >+#else >+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ template<ptrdiff_t __least_max_t> >+ using __semaphore_base = std::conditional<(__least_max_t > 0 This should use conditional_t<> not conditional<>::type. The least-max_value can't be negative. If it's zero, can't we use a futex or semaphore? So the '__least_max_t > 0' condition is wrong? >+ && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()), Should that be <= rather than < ? >+ __atomic_semaphore<__detail::__platform_wait_t>, >+ __atomic_semaphore<ptrdiff_t>>::type; >+ // __platform_semaphore >+# else >+# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid an extra level of #if nesting. >+ template<ptrdiff_t __least_max_t> >+ using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX), >+ __platform_semaphore<__least_max_t>, >+ __atomic_semaphore<ptrdiff_t>>::type; >+# else >+ template<ptrdiff_t __least_max_t> >+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>; >+# endif >+# endif >+#endif >+ >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+ >+#endif >diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic >index a455286a784..3f18774031d 100644 >--- a/libstdc++-v3/include/std/atomic >+++ b/libstdc++-v3/include/std/atomic >@@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > compare_exchange_strong(bool& __i1, bool __i2, > memory_order __m = memory_order_seq_cst) volatile noexcept > { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >+ >+#if __cplusplus > 201703L >+ void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { _M_base.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_base.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_base.notify_all(); } >+#endif > }; > > #if __cplusplus <= 201703L >@@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > memory_order __m = memory_order_seq_cst) volatile noexcept > { return compare_exchange_strong(__e, __i, __m, > __cmpexch_failure_order(__m)); } >+#if __cplusplus > 201703L >+ void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept >+ { _M_i.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_i.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_i.notify_all(); } >+#endif >+ > }; > #undef _GLIBCXX20_INIT > >@@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__m)); > } > >+#if __cplusplus > 201703L >+ void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >+ { _M_b.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_b.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_b.notify_all(); } >+#endif > __pointer_type > fetch_add(ptrdiff_t __d, > memory_order __m = memory_order_seq_cst) noexcept >@@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > memory_order_seq_cst); > } > >+ >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ inline void atomic_wait(const atomic<_Tp>* __a, >+ typename std::atomic<_Tp>::value_type __old) noexcept >+ { __a->wait(__old); } >+ >+ template<typename _Tp> >+ inline void atomic_wait_explicit(const atomic<_Tp>* __a, >+ typename std::atomic<_Tp>::value_type __old, >+ std::memory_order __m) noexcept >+ { __a->wait(__old, __m); } >+ >+ template<typename _Tp> >+ inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >+ { __a->notify_one(); } >+ >+ template<typename _Tp> >+ inline void atomic_notify_all(atomic<_Tp>* __a) noexcept >+ { __a->notify_all(); } >+ >+#endif // C++2a >+ > // Function templates for atomic_integral and atomic_pointer operations only. > // Some operations (and, or, xor) are only available for atomic integrals, > // which is implemented by taking a parameter of type __atomic_base<_ITp>*. >diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch >new file mode 100644 >index 00000000000..0099877416e >--- /dev/null >+++ b/libstdc++-v3/include/std/latch >@@ -0,0 +1,91 @@ >+//<latch> -*- C++ -*- A space before <latch>. >+ >+// Copyright (C) 2020 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 include/latch >+ * This is a Standard C++ Library header. Align "This" with "@file" here. >+ */ >+ >+#ifndef _GLIBCXX_LATCH >+#define _GLIBCXX_LATCH >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_latch 201907L >+ >+#include <bits/atomic_base.h> >+#include <limits> Use <ext/numeric_traits.h> here too. >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ class latch >+ { >+ static constexpr size_t _S_alignment = __alignof__(ptrdiff_t); >+ public: >+ static constexpr >+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t >+ max() noexcept >+ { return numeric_limits<ptrdiff_t>::max(); } >+ >+ constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } >+ >+ ~latch() = default; >+ latch(const latch&) = delete; >+ latch& operator=(const latch&) = delete; >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ count_down(ptrdiff_t __update = 1) >+ { >+ auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); >+ if (__old == __update) >+ __atomic_impl::notify_all(&_M_a); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_wait() const noexcept >+ { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ wait() const >+ { >+ auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ arrive_and_wait(ptrdiff_t __update = 1) >+ { >+ count_down(); >+ wait(); >+ } >+ >+ private: >+ alignas(_S_alignment) ptrdiff_t _M_a; Just use __alignof__ directly here and get rid of _S_alignment? >+ }; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_LATCH >diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore >new file mode 100644 >index 00000000000..b51940b46ac >--- /dev/null >+++ b/libstdc++-v3/include/std/semaphore >@@ -0,0 +1,81 @@ >+//<semaphore> -*- C++ -*- A space before <semaphore>. >+ >+// Copyright (C) 2020 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 include/semaphore >+ * This is a Standard C++ Library header. Align "This" with "@file" here. >+ */ >+ >+#ifndef _GLIBCXX_SEMAPHORE >+#define _GLIBCXX_SEMAPHORE >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_semaphore 201907L >+#include <bits/semaphore_base.h> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()> >+ class counting_semaphore >+ { I don't see a static_assert making it ill-formed to use a negative value for __least_max_value. Is that enforced somewhere else? The standard says it's ill-formed, so we should also have a _neg.cc test checking that we reject it. >+ __semaphore_base<__least_max_value> _M_sem; Blank line after this please. >+ public: >+ explicit counting_semaphore(ptrdiff_t __desired) noexcept >+ : _M_sem(__desired) >+ { } >+ >+ ~counting_semaphore() = default; >+ >+ counting_semaphore(const counting_semaphore&) = delete; >+ counting_semaphore& operator=(const counting_semaphore&) = delete; >+ >+ static constexpr ptrdiff_t max() noexcept >+ { return __least_max_value; } >+ >+ void release(ptrdiff_t __update = 1) >+ { _M_sem.release(__update); } >+ >+ void acquire() >+ { _M_sem.acquire(); } >+ >+ bool try_acquire() noexcept >+ { return _M_sem.try_acquire(); } >+ >+ template<class _Rep, class _Period> s/class/template/ >+ bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) >+ { return _M_sem.try_acquire_for(__rel_time); } >+ >+ template<class _Clock, class _Duration> s/class/template/ >+ bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) >+ { return _M_sem.try_acquire_until(__abs_time); } >+ }; >+ >+ using binary_semaphore = std::counting_semaphore<1>; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_SEMAPHORE >diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >index c3a5bd26e63..390990282b0 100644 >--- a/libstdc++-v3/include/std/version >+++ b/libstdc++-v3/include/std/version >@@ -188,6 +188,8 @@ > #endif > #define __cpp_lib_type_identity 201806L > #define __cpp_lib_unwrap_ref 201811L >+#define __cpp_lib_semaphore 201907L >+#define __cpp_lib_latch 201907L These features aren't supported in a freestanding implementation, so should be in the #if _GLIBCXX_HOSTED block (and the macros should be in alphabetical order). > > #if _GLIBCXX_HOSTED > #undef __cpp_lib_array_constexpr >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >new file mode 100644 >index 00000000000..1ced9d44b20 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >@@ -0,0 +1,103 @@ >+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } Use { dg-add-options libatomic } instead of adding -latomic -L... ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-05-11 14:05 ` Jonathan Wakely @ 2020-05-11 15:43 ` Thomas Rodgers 2020-05-11 17:05 ` Jonathan Wakely 2020-05-11 20:59 ` Thomas Rodgers 1 sibling, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-05-11 15:43 UTC (permalink / raw) To: Jonathan Wakely; +Cc: gcc-patches, libstdc++ Jonathan Wakely writes: > On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote: <snip> >>+#include <iostream> > > <iostream> shouldn't be here (it adds runtime cost, as well as > compile-time). > Oversight, not removed after debugging it. <snip> > > Can't this just be __old instead of *std::__addressof(__old) ? > Copypasta from elsewhere in the same class, I believe. I'll change it. <snip> > > Isn't alignas(64) already implied by the first data member? > Yes >>+ { >>+ int32_t alignas(64) _M_ver = 0; >>+ int32_t alignas(64) _M_wait = 0; >>+ >>+ // TODO make this used only where we don't have futexes > > Don't we always need these even with futexes, for the types that don't > use a futex? > If we have futexes, we can use the address of _M_ver to wake _M_do_wait() instead of using a condvar for types that don't use a futex directly. >>+ using __lock_t = std::unique_lock<std::mutex>; >+ mutable __lock_t::mutex_type _M_mtx; >>+ >>+#ifdef __GTHREAD_COND_INIT >>+ mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >>+ __waiters() noexcept = default; > > If we moved std::condition_variable into its own header (or > <bits/std_mutex.h>, could we reuse that here instead of using > __gthread_cond_t directly? > Yes, I started down that route initially, I could revisit it in a future patch as part of also making it's use only necessary when the platform doesn't support futex. >>+ __atomic_notify(const _Tp* __addr, bool __all) noexcept >>+ { >>+ using namespace __detail; >>+ auto& __w = __waiters::_S_for((void*)__addr); >>+ if (!__w._M_waiting()) > > When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting() > ever be true? Won't this always return before notifying? > > Is there meant to be a __waiter constructed here? > __waiter (an RAII type) is constructed in the __atomic_wait(), that increments the _M_wait count on the way into the wait, and decrements it on the way out, __atomic_notify checks to see if that count is non-zero before invoking the platform/semaphore notify because it is cheaper to do the atomic load than it is to make the syscall() when there are no waiters. >>+ return; >>+ >>+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >>+ { >>+ __platform_notify((__platform_wait_t*)(void*) __addr, __all); >>+ } <snip> >>+ struct __platform_semaphore >>+ { >>+ using __clock_t = chrono::system_clock; >>+ >>+ __platform_semaphore(ptrdiff_t __count) noexcept > > Should this constructor be explicit? > Yes. >>+ template<typename _Duration> >>+ _GLIBCXX_ALWAYS_INLINE bool > > Do we really need this to be always_inline? > Probably not, copypasta from elsewhere in the same file. >>+ __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); <snip> >>+ template<typename _Tp> >>+ struct __atomic_semaphore >>+ { >>+ static constexpr size_t _S_alignment = __alignof__(_Tp); >>+ >>+ __atomic_semaphore(_Tp __count) > > Should this be explicit? > Yes. >>+ private: >>+ alignas(_S_alignment) _Tp _M_a; > > Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no > need for the _S_alignment constant if it's only used in one place. > Yes. >>+ }; >>+ >>+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE >>+ template<ptrdiff_t __least_max_t> > > Rename __least_max_t here too. > >>+ using __semaphore_base = __platform_semaphore<__least_max_t>; >>+#else >>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ template<ptrdiff_t __least_max_t> >>+ using __semaphore_base = std::conditional<(__least_max_t > 0 > > This should use conditional_t<> not conditional<>::type. > > The least-max_value can't be negative. If it's zero, can't we use a > futex or semaphore? So the '__least_max_t > 0' condition is wrong? > Yes. >>+ && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()), > > Should that be <= rather than < ? > Likely. >>+ __atomic_semaphore<__detail::__platform_wait_t>, >>+ __atomic_semaphore<ptrdiff_t>>::type; >>+ // __platform_semaphore >>+# else <snip...> ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-05-11 15:43 ` Thomas Rodgers @ 2020-05-11 17:05 ` Jonathan Wakely 0 siblings, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-05-11 17:05 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++ On 11/05/20 08:43 -0700, Thomas Rodgers wrote: > >Jonathan Wakely writes: > >> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote: > ><snip> > >>>+#include <iostream> >> >> <iostream> shouldn't be here (it adds runtime cost, as well as >> compile-time). >> >Oversight, not removed after debugging it. > ><snip> > >> >> Can't this just be __old instead of *std::__addressof(__old) ? >> >Copypasta from elsewhere in the same class, I believe. I'll change it. > ><snip> > >> >> Isn't alignas(64) already implied by the first data member? >> > >Yes > >>>+ { >>>+ int32_t alignas(64) _M_ver = 0; >>>+ int32_t alignas(64) _M_wait = 0; >>>+ >>>+ // TODO make this used only where we don't have futexes >> >> Don't we always need these even with futexes, for the types that don't >> use a futex? >> > >If we have futexes, we can use the address of _M_ver to wake >_M_do_wait() instead of using a condvar for types that don't use a >futex directly. > >>>+ using __lock_t = std::unique_lock<std::mutex>; >>+ mutable __lock_t::mutex_type _M_mtx; >>>+ >>>+#ifdef __GTHREAD_COND_INIT >>>+ mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >>>+ __waiters() noexcept = default; >> >> If we moved std::condition_variable into its own header (or >> <bits/std_mutex.h>, could we reuse that here instead of using >> __gthread_cond_t directly? >> >Yes, I started down that route initially, I could revisit it in a future >patch as part of also making it's use only necessary when the platform >doesn't support futex. > >>>+ __atomic_notify(const _Tp* __addr, bool __all) noexcept >>>+ { >>>+ using namespace __detail; >>>+ auto& __w = __waiters::_S_for((void*)__addr); >>>+ if (!__w._M_waiting()) >> >> When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting() >> ever be true? Won't this always return before notifying? >> >> Is there meant to be a __waiter constructed here? >> > >__waiter (an RAII type) is constructed in the __atomic_wait(), that >increments the _M_wait count on the way into the wait, and decrements it >on the way out, __atomic_notify checks to see if that count is non-zero >before invoking the platform/semaphore notify because it is cheaper >to do the atomic load than it is to make the syscall() when there are no >waiters. Doh, yes of course. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-05-11 14:05 ` Jonathan Wakely 2020-05-11 15:43 ` Thomas Rodgers @ 2020-05-11 20:59 ` Thomas Rodgers 2020-05-23 22:52 ` Thomas Rodgers 1 sibling, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-05-11 20:59 UTC (permalink / raw) To: Jonathan Wakely; +Cc: gcc-patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 61 bytes --] I *think* I have addressed everything in the attached patch. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: updated patch --] [-- Type: text/x-patch, Size: 79499 bytes --] commit 24a989d2bf2158bdbe2511310d0583d0c6226f71 Author: Thomas Rodgers <rodgert@appliantology.com> Date: Mon Apr 6 17:58:47 2020 -0700 Add C++2a synchronization support Add support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 80aeb3f8959..b3ac1a3365f 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 87fe0bd6000..73a8a77271e 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -542,6 +545,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -803,6 +831,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -891,6 +943,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1144,6 +1222,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1281,6 +1376,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + private: _Tp* _M_ptr; }; @@ -1376,6 +1487,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1531,6 +1658,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1640,6 +1783,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..691eed128a6 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,278 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { __no_timeout, __timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, + __futex_bitset_match_any = 0xffffffff + }; + + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, + nullptr, __futex_bitset_match_any); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (std::is_same_v<__platform_wait_clock_t, _Clock>) + { + return __platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) + return __atomic_wait_status::__no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout + : __atomic_wait_status::__timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::__no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(int32_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { + int32_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) + return __atomic_wait_status::__timeout; + + int32_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::__no_timeout; + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return (__timed_waiters&) __waiters::_S_for(__t); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::__timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..1deacf02cce --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,282 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + +#define _GLIBCXX_SPIN_COUNT_1 16 +#define _GLIBCXX_SPIN_COUNT_2 12 + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__numeric_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __futex_private_flag = 128, +#else + __futex_private_flag = 0, +#endif + __futex_wait = 0, + __futex_wake = 1, + __futex_wait_bitset = 9, + __futex_wake_bitset = 10, + __futex_wait_private = __futex_wait | __futex_private_flag, + __futex_wake_private = __futex_wake | __futex_private_flag + }; + + void + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + void + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept + { + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + int32_t alignas(64) _M_ver = 0; + int32_t alignas(64) _M_wait = 0; + + // TODO make this used only where we don't have futexes + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +#ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +#else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +#endif + + int32_t + _M_enter_wait() noexcept + { + int32_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(int32_t __version) const noexcept + { + int32_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + int32_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + } + + int32_t + _M_waiting() const noexcept + { + int32_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); + } + + static __waiters& + _S_for(void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + int32_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for((void*) __addr)) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) + { + if (__pred()) + return true; + + if (__i < _GLIBCXX_SPIN_COUNT_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..db5949e5b44 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,272 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + + __platform_semaphore(ptrdiff_t __count) noexcept + { + static_assert( __least_max_value <= SEM_VALUE_MAX, ""); + auto __e = sem_init(&_M_semaphore, 0, __count); + if (__e) + std::terminate(); + } + + ~__platform_semaphore() + { + auto __e = sem_destroy(&_M_semaphore); + if (__e) + std::terminate(); + } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto __err = sem_wait(&_M_semaphore); + if (__err) + std::terminate(); + } + + template<typename _Duration> + bool + __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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err) + std::terminate(); + return true; + } + + template<typename _Clock, typename _Duration> + bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + if constexpr (std::is_same<__clock_t, _Clock>::value) + { + return __try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__try_acquire_until_impl(__s_atime)) + return true; + + // 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 + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { return try_acquire_until(__clock_t::now() + __rtime); } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + do + { + auto __err = sem_post(&_M_semaphore); + if (__err) + std::terminate(); + } while (--__update); + } + + private: + sem_t _M_semaphore; + }; +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE + + template<typename _Tp> + struct __atomic_semaphore + { + __atomic_semaphore(_Tp __count) + : _M_a(__count) + { } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = __platform_semaphore<__least_max_value>; +#else +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= __detail::__platform_wait_max_value), + __atomic_semaphore<__detail::__platform_wait_t>, + __atomic_semaphore<ptrdiff_t>>; + +// __platform_semaphore +# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= SEM_VALUE_MAX), + __platform_semaphore<__least_max_value>, + __atomic_semaphore<ptrdiff_t>>; +# else + template<ptrdiff_t __least_max_value> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; +# endif +#endif + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index a455286a784..3f18774031d 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_i.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_i.notify_one(); } + + void notify_all() const noexcept + { _M_i.notify_all(); } +#endif + }; #undef _GLIBCXX20_INIT @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..aa5299d9fdd --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr + _GLIBCXX_ALWAYS_INLINE ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..90cf3244647 --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,86 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >=0, ""); + + __semaphore_base<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t max() noexcept + { return __least_max_value; } + + void release(ptrdiff_t __update = 1) + { _M_sem.release(__update); } + + void acquire() + { _M_sem.acquire(); } + + bool try_acquire() noexcept + { return _M_sem.try_acquire(); } + + template<class _Rep, class _Period> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return _M_sem.try_acquire_for(__rel_time); } + + template<class _Clock, class _Duration> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) + { return _M_sem.try_acquire_until(__abs_time); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index c3a5bd26e63..390990282b0 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -188,6 +188,8 @@ #endif #define __cpp_lib_type_identity 201806L #define __cpp_lib_unwrap_ref 201811L +#define __cpp_lib_semaphore 201907L +#define __cpp_lib_latch 201907L #if _GLIBCXX_HOSTED #undef __cpp_lib_array_constexpr diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..0da374ece87 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,88 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + VERIFY(check_atomic_wait_notify(a, b) == b); + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..2afd19a7d14 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..756727f33b3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..10bb500d261 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..b96b8a59c64 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..1ac9d261ca5 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..d38cef86cfc --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..965554a3c28 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc new file mode 100644 index 00000000000..5e05606e97f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +void test01() +{ + // the implementation optimizes for values of least_max_t that can fit + // in a futex, make sure we cover the case where least_max_t doesn't + auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max(); + std::counting_semaphore<least_max_t> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + test01(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..bf99fd3cf8f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + // The implementation supports posix as an implementation strategy + // make sure we cover that case +#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test03() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +void test04() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test05() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + test01(); + test02(); + test03(); + test04(); + test05(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..cc67c5c0bf0 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} [-- Attachment #3: Type: text/plain, Size: 43750 bytes --] Jonathan Wakely writes: > On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote: >>* Note, this patch supersedes my previous atomic wait and semaphore >>patches. >> >>Add support for - >> atomic wait/notify_one/notify_all >> counting_semaphore >> binary_semaphore >> latch >> >> * include/Makefile.am (bits_headers): Add new header. >> * include/Makefile.in: Regenerate. >> * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. > > Should be two colons before wait. > >>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in >>index eb437ad8d8d..e73ff8b3e64 100644 >>--- a/libstdc++-v3/include/Makefile.in >>+++ b/libstdc++-v3/include/Makefile.in > > Generated files don't need to be in the patch. > >>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >>index 87fe0bd6000..b2cec0f1722 100644 >>--- a/libstdc++-v3/include/bits/atomic_base.h >>+++ b/libstdc++-v3/include/bits/atomic_base.h >>@@ -37,6 +37,11 @@ >> #include <bits/atomic_lockfree_defines.h> >> #include <bits/move.h> >> >>+#if __cplusplus > 201703L >>+#include <bits/atomic_wait.h> >>+#include <iostream> > > <iostream> shouldn't be here (it adds runtime cost, as well as > compile-time). > >>@@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__m)); >> } >> >>+#if __cplusplus > 201703L >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept > > Please format everything to <= 80 columns (ideally < 80). > >>+ wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >>+ { >>+ __atomic_wait(&_M_p, __old, > > This should be qualified to prevent ADL. > >>+ [__m, this, __old]() >>+ { return this->load(__m) != __old; }); >>+ } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { __atomic_notify(&_M_p, false); } > > Qualify to prevent ADL here too, and all similar calls. > >>+#if __cplusplus > 201703L >>+ template<typename _Tp> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept >>+ { >>+ __atomic_wait(__ptr, *std::__addressof(__old), > > Can't this just be __old instead of *std::__addressof(__old) ? > >>+ [=]() >>+ { return load(__ptr, __m) == *std::__addressof(__old); }); > > Same here? > >>diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >>new file mode 100644 >>index 00000000000..10f0fe50ed9 >>--- /dev/null >>+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >>@@ -0,0 +1,270 @@ >>+// -*- C++ -*- header. >>+ >>+// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >>+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >>+ >>+#pragma GCC system_header >>+ >>+#include <bits/c++config.h> >>+#include <bits/functional_hash.h> >>+#include <bits/atomic_wait.h> >>+ >>+#include <chrono> >>+ >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+#include <sys/time.h> >>+#endif >>+ >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ enum class __atomic_wait_status { __no_timeout, __timeout }; > > Blank line before and after this enum definition please. > >>+ namespace __detail >>+ { >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ enum >>+ { >>+ __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, >>+ __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, >>+ __futex_bitset_match_any = 0xffffffff >>+ }; >>+ >>+ using __platform_wait_clock_t = chrono::steady_clock; > > Blank line after this using-decl please. > >>+ template<typename _Duration> >>+ __atomic_wait_status >>+ __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, >>+ const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept >>+ { >>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>+ > > Eventually we'll want to move the rest of this function (which doesn't > depend on the template argument) into the compiled library, but it's > better to be header-only for now. > >>+ struct timespec __rt = >>+ { >>+ static_cast<std::time_t>(__s.time_since_epoch().count()), >>+ static_cast<long>(__ns.count()) >>+ }; >>+ >>+ auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, >>+ nullptr, __futex_bitset_match_any); >>+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >>+ std::terminate(); >>+ return (__platform_wait_clock_t::now() < __atime) >>+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>+ } >>+ >>+ template<typename _Clock, typename _Duration> >>+ __atomic_wait_status >>+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >>+ const chrono::time_point<_Clock, _Duration>& __atime) >>+ { >>+ if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value) > > This is C++20 so you can use is_same_v here, which uses the intrinsic > directly and avoids instantiating the is_same class template. > >>+ { >>+ return __platform_wait_until_impl(__addr, __val, __atime); >>+ } >>+ else >>+ { >>+ const typename _Clock::time_point __c_entry = _Clock::now(); >>+ const __platform_wait_clock_t::time_point __s_entry = >>+ __platform_wait_clock_t::now(); >>+ const auto __delta = __atime - __c_entry; >>+ const auto __s_atime = __s_entry + __delta; >>+ if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) >>+ return __atomic_wait_status::__no_timeout; >>+ >>+ // 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. >>+ if (_Clock::now() < __atime) >>+ return __atomic_wait_status::__no_timeout; >>+ return __atomic_wait_status::__timeout; >>+ } >>+ } >>+#endif >>+ >>+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >>+ template<typename _Duration> >>+ __atomic_wait_status >>+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>+ const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime) > > The std:: qualification here isn't needed (and doesn't help with > keeping the line below 80 cols). > >>+ { >>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>+ >>+ __gthread_time_t __ts = >>+ { >>+ static_cast<std::time_t>(__s.time_since_epoch().count()), >>+ static_cast<long>(__ns.count()) >>+ }; >>+ >>+ pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), >>+ CLOCK_MONOTONIC, >>+ &__ts); >>+ return (chrono::steady_clock::now() < __atime) >>+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>+ } >>+#endif >>+ >>+ template<typename _Duration> >>+ __atomic_wait_status >>+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>+ const chrono::time_point<chrono::system_clock, _Duration>& __atime) >>+ { >>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>+ >>+ __gthread_time_t __ts = >>+ { >>+ static_cast<std::time_t>(__s.time_since_epoch().count()), >>+ static_cast<long>(__ns.count()) >>+ }; >>+ >>+ __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), >>+ &__ts); >>+ return (chrono::system_clock::now() < __atime) >>+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>+ } >>+ >>+ // return true if timeout >>+ template<typename _Clock, typename _Duration> >>+ __atomic_wait_status >>+ __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>+ const chrono::time_point<_Clock, _Duration>& __atime) >>+ { >>+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >>+ using __clock_t = chrono::steady_clock; >>+#else >>+ using __clock_t = chrono::system_clock; >>+#endif >>+ const typename _Clock::time_point __c_entry = _Clock::now(); >>+ const __clock_t::time_point __s_entry = __clock_t::now(); >>+ const auto __delta = __atime - __c_entry; >>+ const auto __s_atime = __s_entry + __delta; >>+ if (__cond_wait_until_impl(__cv, __lock, __s_atime)) >>+ return __atomic_wait_status::__no_timeout; >>+ // 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. >>+ if (_Clock::now() < __atime) >>+ return __atomic_wait_status::__no_timeout; >>+ return __atomic_wait_status::__timeout; >>+ } >>+ >>+ struct __timed_waiters : __waiters >>+ { >>+ template<typename _Clock, typename _Duration> >>+ __atomic_wait_status >>+ _M_do_wait_until(int32_t __version, >>+ const chrono::time_point<_Clock, _Duration>& __atime) >>+ { >>+ int32_t __cur = 0; >>+ __waiters::__lock_t __l(_M_mtx); >>+ while (__cur <= __version) >>+ { >>+ if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) >>+ return __atomic_wait_status::__timeout; >>+ >>+ int32_t __last = __cur; >>+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >>+ if (__cur < __last) >>+ break; // break the loop if version overflows >>+ } >>+ return __atomic_wait_status::__no_timeout; >>+ } >>+ >>+ static __timed_waiters& >>+ _S_timed_for(void* __t) >>+ { >>+ static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); >>+ return (__timed_waiters&) __waiters::_S_for(__t); >>+ } >>+ }; >>+ } // namespace __detail >>+ >>+ template<typename _Tp, typename _Pred, >>+ typename _Clock, typename _Duration> >>+ bool >>+ __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >>+ const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>+ { >>+ using namespace __detail; >>+ >>+ if (__atomic_spin(__pred)) > > Qualify to prevent ADL. > >>+ return true; >>+ >>+ auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >>+ auto __version = __w._M_enter_wait(); >>+ do >>+ { >>+ __atomic_wait_status __res; >>+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >>+ { >>+ __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old, >>+ __atime); >>+ } >>+ else >>+ { >>+ __res = __w._M_do_wait_until(__version, __atime); >>+ } >>+ if (__res == __atomic_wait_status::__timeout) >>+ return false; >>+ } >>+ while (!__pred() && __atime < _Clock::now()); >>+ __w._M_leave_wait(); >>+ >>+ // if timed out, return false >>+ return (_Clock::now() < __atime); >>+ } >>+ >>+ template<typename _Tp, typename _Pred, >>+ typename _Rep, typename _Period> >>+ bool >>+ __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, >>+ const chrono::duration<_Rep, _Period>& __rtime) noexcept >>+ { >>+ using namespace __detail; >>+ >>+ if (__atomic_spin(__pred)) >>+ return true; >>+ >>+ if (!__rtime.count()) >>+ return false; // no rtime supplied, and spin did not acquire >>+ >>+ using __dur = chrono::steady_clock::duration; >>+ auto __reltime = chrono::duration_cast<__dur>(__rtime); >>+ if (__reltime < __rtime) >>+ ++__reltime; >>+ >>+ >>+ return __atomic_wait_until(__addr, __old, std::move(__pred), >>+ chrono::steady_clock::now() + __reltime); >>+ } >>+_GLIBCXX_END_NAMESPACE_VERSION >>+} // namespace std >>+#endif >>diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >>new file mode 100644 >>index 00000000000..32070a54f40 >>--- /dev/null >>+++ b/libstdc++-v3/include/bits/atomic_wait.h >>@@ -0,0 +1,280 @@ >>+// -*- C++ -*- header. >>+ >>+// Copyright (C) 2020 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 >>+ >>+#pragma GCC system_header >>+ >>+#include <bits/c++config.h> >>+#include <bits/functional_hash.h> >>+#include <bits/gthr.h> >>+#include <bits/std_mutex.h> >>+#include <bits/unique_lock.h> >>+ >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+#include <climits> >>+#include <unistd.h> >>+#include <syscall.h> >>+#endif >>+ >>+#define _GLIBCXX_SPIN_COUNT_1 16 >>+#define _GLIBCXX_SPIN_COUNT_2 12 >>+ >>+// TODO get this from Autoconf >>+#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >>+ >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ namespace __detail >>+ { >>+ using __platform_wait_t = int; >>+ >>+ template<class _Tp> > > This should be typename not class. > >>+ struct __platform_wait_uses_type >>+ { >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, > > This should be remove_cv_t. > >>+ __platform_wait_t>::value }; >>+#else >>+ enum { __value = std::false_type::value }; >>+#endif > > There's no need to use the C++03 enum hack here, it should just derive > from true_type or false_type. > > template<typename _Tp> > struct __platform_wait_uses_type > #ifdef _GLIBCXX_HAVE_LINUX_FUTEX > : is_same<std::remove_cv_t<_Tp>, __platform_wait_t> > #else > : false_type > #endif > { }; > > Or better yet, just use a variable template: > > template<typename _Tp> > inline constexpr bool __platform_wait_uses_type > #ifdef _GLIBCXX_HAVE_LINUX_FUTEX > = is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>; > #else > = false; > #endif > > > >>+ }; >>+ >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ enum >>+ { >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >>+ __futex_private_flag = 128, >>+#else >>+ __futex_private_flag = 0, >>+#endif >>+ __futex_wait = 0, >>+ __futex_wake = 1, >>+ __futex_wait_bitset = 9, >>+ __futex_wake_bitset = 10, >>+ __futex_wait_private = __futex_wait | __futex_private_flag, >>+ __futex_wake_private = __futex_wake | __futex_private_flag >>+ }; >>+ >>+ void >>+ __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept >>+ { >>+ auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); >>+ if (__e && !(errno == EINTR || errno == EAGAIN)) >>+ std::terminate(); >>+ } >>+ >>+ void >>+ __platform_notify(__platform_wait_t* __addr, bool __all) noexcept >>+ { >>+ syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); >>+ } >>+#endif >>+ >>+ struct alignas(64) __waiters > > Isn't alignas(64) already implied by the first data member? > >>+ { >>+ int32_t alignas(64) _M_ver = 0; >>+ int32_t alignas(64) _M_wait = 0; >>+ >>+ // TODO make this used only where we don't have futexes > > Don't we always need these even with futexes, for the types that don't > use a futex? > >>+ using __lock_t = std::unique_lock<std::mutex>; >>+ mutable __lock_t::mutex_type _M_mtx; >>+ >>+#ifdef __GTHREAD_COND_INIT >>+ mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >>+ __waiters() noexcept = default; > > If we moved std::condition_variable into its own header (or > <bits/std_mutex.h>, could we reuse that here instead of using > __gthread_cond_t directly? > >>+#else >>+ mutable __gthread_cond_t _M_cv; >>+ __waiters() noexcept >>+ { >>+ __GTHREAD_COND_INIT_FUNCTION(&_M_cond); >>+ } >>+#endif >>+ >>+ int32_t >>+ _M_enter_wait() noexcept >>+ { >>+ int32_t __res; >>+ __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); >>+ __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); >>+ return __res; >>+ } >>+ >>+ void >>+ _M_leave_wait() noexcept >>+ { >>+ __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); >>+ } >>+ >>+ void >>+ _M_do_wait(int32_t __version) const noexcept >>+ { >>+ int32_t __cur = 0; >>+ while (__cur <= __version) >>+ { >>+ __waiters::__lock_t __l(_M_mtx); >>+ auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); >>+ if (__e) >>+ std::terminate(); >>+ int32_t __last = __cur; >>+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >>+ if (__cur < __last) >>+ break; // break the loop if version overflows >>+ } >>+ } >>+ >>+ int32_t >>+ _M_waiting() const noexcept >>+ { >>+ int32_t __res; >>+ __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >>+ return __res; >>+ } >>+ >>+ void >>+ _M_notify(bool __all) noexcept >>+ { >>+ __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >>+ auto __e = __gthread_cond_broadcast(&_M_cv); >>+ if (__e) >>+ __throw_system_error(__e); >>+ } >>+ >>+ static __waiters& >>+ _S_for(void* __t) >>+ { >>+ const unsigned char __mask = 0xf; >>+ static __waiters __w[__mask + 1]; >>+ >>+ auto __key = _Hash_impl::hash(__t) & __mask; >>+ return __w[__key]; >>+ } >>+ }; >>+ >>+ struct __waiter >>+ { >>+ __waiters& _M_w; >>+ int32_t _M_version; >>+ >>+ template<typename _Tp> >>+ __waiter(const _Tp* __addr) noexcept >>+ : _M_w(__waiters::_S_for((void*) __addr)) >>+ , _M_version(_M_w._M_enter_wait()) >>+ { } >>+ >>+ ~__waiter() >>+ { _M_w._M_leave_wait(); } >>+ >>+ void _M_do_wait() noexcept >>+ { _M_w._M_do_wait(_M_version); } >>+ }; >>+ >>+ void >>+ __thread_relax() noexcept >>+ { >>+#if defined __i386__ || defined __x86_64__ >>+ __builtin_ia32_pause(); >>+#elif defined _GLIBCXX_USE_SCHED_YIELD >>+ __gthread_yield(); >>+#endif >>+ } >>+ >>+ void >>+ __thread_yield() noexcept >>+ { >>+#if defined _GLIBCXX_USE_SCHED_YIELD >>+ __gthread_yield(); >>+#endif >>+ } >>+ >>+ } // namespace __detail >>+ >>+ template<class _Pred> > > s/class/template/ > >>+ bool >>+ __atomic_spin(_Pred __pred) noexcept >>+ { >>+ for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >>+ { >>+ if (__pred()) >>+ return true; >>+ >>+ if (__i < _GLIBCXX_SPIN_COUNT_2) >>+ __detail::__thread_relax(); >>+ else >>+ __detail::__thread_yield(); >>+ } >>+ return false; >>+ } >>+ >>+ template<class _Tp, class _Pred> > > s/class/template/ > >>+ void >>+ __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >>+ { >>+ using namespace __detail; >>+ if (__atomic_spin(__pred)) >>+ return; >>+ >>+ __waiter __w(__addr); >>+ while (!__pred()) >>+ { >>+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >>+ { >>+ __platform_wait((__platform_wait_t*)(void*) __addr, __old); >>+ } >>+ else >>+ { >>+ // TODO support timed backoff when this can be moved into the lib >>+ __w._M_do_wait(); >>+ } >>+ } >>+ } >>+ >>+ template<class _Tp> > > s/class/template/ > >>+ void >>+ __atomic_notify(const _Tp* __addr, bool __all) noexcept >>+ { >>+ using namespace __detail; >>+ auto& __w = __waiters::_S_for((void*)__addr); >>+ if (!__w._M_waiting()) > > When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting() > ever be true? Won't this always return before notifying? > > Is there meant to be a __waiter constructed here? > >>+ return; >>+ >>+ if constexpr (__platform_wait_uses_type<_Tp>::__value) >>+ { >>+ __platform_notify((__platform_wait_t*)(void*) __addr, __all); >>+ } >>+ else >>+ { >>+ __w._M_notify(__all); >>+ } >>+ } >>+_GLIBCXX_END_NAMESPACE_VERSION >>+} // namespace std >>+#endif >>diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >>new file mode 100644 >>index 00000000000..b3c83bbc70b >>--- /dev/null >>+++ b/libstdc++-v3/include/bits/semaphore_base.h >>@@ -0,0 +1,270 @@ >>+// -*- C++ -*- header. >>+ >>+// Copyright (C) 2020 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/semaphore.h > > Should be bits/semaphore_base.h > >>+ * This is an internal header file, included by other library headers. >>+ * Do not attempt to use it directly. @headername{atomic} > > Should be @headername{semaphore} > >>+ */ >>+ >>+#ifndef _GLIBCXX_SEMAPHORE_BASE_H >>+#define _GLIBCXX_SEMAPHORE_BASE_H 1 >>+ >>+#pragma GCC system_header >>+ >>+#include <bits/c++config.h> >>+#include <bits/atomic_base.h> >>+#include <bits/atomic_timed_wait.h> >>+ >>+#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) >>+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >>+#include <semaphore.h> >>+#endif >>+ >>+#include <chrono> >>+#include <type_traits> >>+#include <limits> > > <ext/numeric_traits.h> is much smaller than <limits> and should be > used for limits of integer types. (I recently added > <bits/int_limits.h> too but that was a mistake that I need to fix). > > >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ >>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >>+ template<ptrdiff_t __least_max_t> > > __least_max_t isn't a type so shouldn't have the _t suffix. > >>+ struct __platform_semaphore >>+ { >>+ using __clock_t = chrono::system_clock; >>+ >>+ __platform_semaphore(ptrdiff_t __count) noexcept > > Should this constructor be explicit? > >>+ { >>+ static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX"); > > Our static_assert messages should state the positive condition, not > the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX", > which is what the real condition is anyway, so you might as well omit > the string literal. > >>+ auto __e = sem_init(&_M_semaphore, 0, __count); >>+ if (__e) >>+ std::terminate(); >>+ } >>+ >>+ ~__platform_semaphore() >>+ { >>+ auto __e = sem_destroy(&_M_semaphore); >>+ if (__e) >>+ std::terminate(); >>+ } >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ acquire() noexcept >>+ { >>+ auto __err = sem_wait(&_M_semaphore); >>+ if (__err) >>+ std::terminate(); >>+ } >>+ >>+ template<typename _Duration> >>+ _GLIBCXX_ALWAYS_INLINE bool > > Do we really need this to be always_inline? > >>+ __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); >>+ >>+ struct timespec __ts = >>+ { >>+ static_cast<std::time_t>(__s.time_since_epoch().count()), >>+ static_cast<long>(__ns.count()) >>+ }; >>+ >>+ auto __err = sem_timedwait(&_M_semaphore, &__ts); >>+ if (__err && (errno == ETIMEDOUT)) >>+ return false; >>+ else if (__err) >>+ std::terminate(); >>+ return true; >>+ } >>+ >>+ template<typename _Clock, typename _Duration> >>+ _GLIBCXX_ALWAYS_INLINE bool > > always_inline? > >>+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>+ { >>+ if constexpr (std::is_same<__clock_t, _Clock>::value) > > is_same_v > >>+ { >>+ return __try_acquire_until_impl(__atime); >>+ } >>+ else >>+ { >>+ const typename _Clock::time_point __c_entry = _Clock::now(); >>+ const __clock_t __s_entry = __clock_t::now(); >>+ const auto __delta = __atime - __c_entry; >>+ const auto __s_atime = __s_entry + __delta; >>+ if (__try_acquire_until_impl(__s_atime)) >>+ return true; >>+ >>+ // 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 >>+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>+ { return try_acquire_until(__clock_t::now() + __rtime); } >>+ >>+ template<typename _Clock, typename _Duration> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ release(ptrdiff_t __update) noexcept >>+ { >>+ do >>+ { >>+ auto __err = sem_post(&_M_semaphore); >>+ if (__err) >>+ std::terminate(); >>+ } while (--__update); >>+ } >>+ >>+ private: >>+ sem_t _M_semaphore; >>+ }; >>+#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE >>+ >>+ template<typename _Tp> >>+ struct __atomic_semaphore >>+ { >>+ static constexpr size_t _S_alignment = __alignof__(_Tp); >>+ >>+ __atomic_semaphore(_Tp __count) > > Should this be explicit? > >>+ : _M_a(__count) >>+ { } >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ acquire() noexcept >>+ { >>+ auto const __pred = [this] >>+ { >>+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>+ if (__old == 0) >>+ return false; >>+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >>+ __old, __old - 1, >>+ memory_order::acquire, >>+ memory_order::release); >>+ }; >>+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>+ __atomic_wait(&_M_a, __old, __pred); >>+ } >>+ >>+ bool >>+ try_acquire() noexcept >>+ { >>+ auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); >>+ if (__old == 0) >>+ return false; >>+ >>+ return __atomic_spin([this, &__old] >>+ { >>+ return __atomic_impl::compare_exchange_weak(&this->_M_a, >>+ __old, __old - 1, >>+ memory_order::acquire, >>+ memory_order::release); >>+ }); >>+ } >>+ >>+ template<typename _Clock, typename _Duration> >>+ _GLIBCXX_ALWAYS_INLINE bool >>+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>+ { >>+ auto const __pred = [this] >>+ { >>+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>+ if (__old == 0) >>+ return false; >>+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >>+ __old, __old - 1, >>+ memory_order::acquire, >>+ memory_order::release); >>+ }; >>+ >>+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>+ return __atomic_wait_until(&_M_a, __old, __pred, __atime); >>+ } >>+ >>+ template<typename _Rep, typename _Period> >>+ _GLIBCXX_ALWAYS_INLINE bool >>+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>+ { >>+ auto const __pred = [this] >>+ { >>+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>+ if (__old == 0) >>+ return false; >>+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >>+ __old, __old - 1, >>+ memory_order::acquire, >>+ memory_order::release); >>+ }; >>+ >>+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>+ return __atomic_wait_for(&_M_a, __old, __pred, __rtime); >>+ } >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ release(ptrdiff_t __update) noexcept >>+ { >>+ if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) >>+ return; >>+ if (__update > 1) >>+ __atomic_impl::notify_all(&_M_a); >>+ else >>+ __atomic_impl::notify_one(&_M_a); >>+ } >>+ >>+ private: >>+ alignas(_S_alignment) _Tp _M_a; > > Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no > need for the _S_alignment constant if it's only used in one place. > >>+ }; >>+ >>+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE >>+ template<ptrdiff_t __least_max_t> > > Rename __least_max_t here too. > >>+ using __semaphore_base = __platform_semaphore<__least_max_t>; >>+#else >>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ template<ptrdiff_t __least_max_t> >>+ using __semaphore_base = std::conditional<(__least_max_t > 0 > > This should use conditional_t<> not conditional<>::type. > > The least-max_value can't be negative. If it's zero, can't we use a > futex or semaphore? So the '__least_max_t > 0' condition is wrong? > >>+ && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()), > > Should that be <= rather than < ? > >>+ __atomic_semaphore<__detail::__platform_wait_t>, >>+ __atomic_semaphore<ptrdiff_t>>::type; >>+ // __platform_semaphore >>+# else >>+# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE > > Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid > an extra level of #if nesting. > >>+ template<ptrdiff_t __least_max_t> >>+ using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX), >>+ __platform_semaphore<__least_max_t>, >>+ __atomic_semaphore<ptrdiff_t>>::type; >>+# else >>+ template<ptrdiff_t __least_max_t> >>+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>; >>+# endif >>+# endif >>+#endif >>+ >>+_GLIBCXX_END_NAMESPACE_VERSION >>+} // namespace std >>+ >>+#endif >>diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic >>index a455286a784..3f18774031d 100644 >>--- a/libstdc++-v3/include/std/atomic >>+++ b/libstdc++-v3/include/std/atomic >>@@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> compare_exchange_strong(bool& __i1, bool __i2, >> memory_order __m = memory_order_seq_cst) volatile noexcept >> { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >>+ >>+#if __cplusplus > 201703L >>+ void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept >>+ { _M_base.wait(__old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ void notify_one() const noexcept >>+ { _M_base.notify_one(); } >>+ >>+ void notify_all() const noexcept >>+ { _M_base.notify_all(); } >>+#endif >> }; >> >> #if __cplusplus <= 201703L >>@@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> memory_order __m = memory_order_seq_cst) volatile noexcept >> { return compare_exchange_strong(__e, __i, __m, >> __cmpexch_failure_order(__m)); } >>+#if __cplusplus > 201703L >>+ void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept >>+ { _M_i.wait(__old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ void notify_one() const noexcept >>+ { _M_i.notify_one(); } >>+ >>+ void notify_all() const noexcept >>+ { _M_i.notify_all(); } >>+#endif >>+ >> }; >> #undef _GLIBCXX20_INIT >> >>@@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__m)); >> } >> >>+#if __cplusplus > 201703L >>+ void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >>+ { _M_b.wait(__old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ void notify_one() const noexcept >>+ { _M_b.notify_one(); } >>+ >>+ void notify_all() const noexcept >>+ { _M_b.notify_all(); } >>+#endif >> __pointer_type >> fetch_add(ptrdiff_t __d, >> memory_order __m = memory_order_seq_cst) noexcept >>@@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> memory_order_seq_cst); >> } >> >>+ >>+#if __cplusplus > 201703L >>+ template<typename _Tp> >>+ inline void atomic_wait(const atomic<_Tp>* __a, >>+ typename std::atomic<_Tp>::value_type __old) noexcept >>+ { __a->wait(__old); } >>+ >>+ template<typename _Tp> >>+ inline void atomic_wait_explicit(const atomic<_Tp>* __a, >>+ typename std::atomic<_Tp>::value_type __old, >>+ std::memory_order __m) noexcept >>+ { __a->wait(__old, __m); } >>+ >>+ template<typename _Tp> >>+ inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >>+ { __a->notify_one(); } >>+ >>+ template<typename _Tp> >>+ inline void atomic_notify_all(atomic<_Tp>* __a) noexcept >>+ { __a->notify_all(); } >>+ >>+#endif // C++2a >>+ >> // Function templates for atomic_integral and atomic_pointer operations only. >> // Some operations (and, or, xor) are only available for atomic integrals, >> // which is implemented by taking a parameter of type __atomic_base<_ITp>*. >>diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch >>new file mode 100644 >>index 00000000000..0099877416e >>--- /dev/null >>+++ b/libstdc++-v3/include/std/latch >>@@ -0,0 +1,91 @@ >>+//<latch> -*- C++ -*- > > A space before <latch>. > >>+ >>+// Copyright (C) 2020 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 include/latch >>+ * This is a Standard C++ Library header. > > Align "This" with "@file" here. > >>+ */ >>+ >>+#ifndef _GLIBCXX_LATCH >>+#define _GLIBCXX_LATCH >>+ >>+#pragma GCC system_header >>+ >>+#if __cplusplus > 201703L >>+#define __cpp_lib_latch 201907L >>+ >>+#include <bits/atomic_base.h> >>+#include <limits> > > Use <ext/numeric_traits.h> here too. > >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ >>+ class latch >>+ { >>+ static constexpr size_t _S_alignment = __alignof__(ptrdiff_t); >>+ public: >>+ static constexpr >>+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t >>+ max() noexcept >>+ { return numeric_limits<ptrdiff_t>::max(); } >>+ >>+ constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } >>+ >>+ ~latch() = default; >>+ latch(const latch&) = delete; >>+ latch& operator=(const latch&) = delete; >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ count_down(ptrdiff_t __update = 1) >>+ { >>+ auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); >>+ if (__old == __update) >>+ __atomic_impl::notify_all(&_M_a); >>+ } >>+ >>+ _GLIBCXX_ALWAYS_INLINE bool >>+ try_wait() const noexcept >>+ { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait() const >>+ { >>+ auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >>+ __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); >>+ } >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ arrive_and_wait(ptrdiff_t __update = 1) >>+ { >>+ count_down(); >>+ wait(); >>+ } >>+ >>+ private: >>+ alignas(_S_alignment) ptrdiff_t _M_a; > > Just use __alignof__ directly here and get rid of _S_alignment? > >>+ }; >>+_GLIBCXX_END_NAMESPACE_VERSION >>+} // namespace >>+#endif // __cplusplus > 201703L >>+#endif // _GLIBCXX_LATCH >>diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore >>new file mode 100644 >>index 00000000000..b51940b46ac >>--- /dev/null >>+++ b/libstdc++-v3/include/std/semaphore >>@@ -0,0 +1,81 @@ >>+//<semaphore> -*- C++ -*- > > A space before <semaphore>. > >>+ >>+// Copyright (C) 2020 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 include/semaphore >>+ * This is a Standard C++ Library header. > > Align "This" with "@file" here. > >>+ */ >>+ >>+#ifndef _GLIBCXX_SEMAPHORE >>+#define _GLIBCXX_SEMAPHORE >>+ >>+#pragma GCC system_header >>+ >>+#if __cplusplus > 201703L >>+#define __cpp_lib_semaphore 201907L >>+#include <bits/semaphore_base.h> >>+ >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ >>+ template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()> >>+ class counting_semaphore >>+ { > > I don't see a static_assert making it ill-formed to use a negative > value for __least_max_value. Is that enforced somewhere else? > > The standard says it's ill-formed, so we should also have a _neg.cc > test checking that we reject it. > >>+ __semaphore_base<__least_max_value> _M_sem; > > Blank line after this please. > >>+ public: >>+ explicit counting_semaphore(ptrdiff_t __desired) noexcept >>+ : _M_sem(__desired) >>+ { } >>+ >>+ ~counting_semaphore() = default; >>+ >>+ counting_semaphore(const counting_semaphore&) = delete; >>+ counting_semaphore& operator=(const counting_semaphore&) = delete; >>+ >>+ static constexpr ptrdiff_t max() noexcept >>+ { return __least_max_value; } >>+ >>+ void release(ptrdiff_t __update = 1) >>+ { _M_sem.release(__update); } >>+ >>+ void acquire() >>+ { _M_sem.acquire(); } >>+ >>+ bool try_acquire() noexcept >>+ { return _M_sem.try_acquire(); } >>+ >>+ template<class _Rep, class _Period> > > s/class/template/ > >>+ bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) >>+ { return _M_sem.try_acquire_for(__rel_time); } >>+ >>+ template<class _Clock, class _Duration> > > s/class/template/ > >>+ bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) >>+ { return _M_sem.try_acquire_until(__abs_time); } >>+ }; >>+ >>+ using binary_semaphore = std::counting_semaphore<1>; >>+_GLIBCXX_END_NAMESPACE_VERSION >>+} // namespace >>+#endif // __cplusplus > 201703L >>+#endif // _GLIBCXX_SEMAPHORE >>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >>index c3a5bd26e63..390990282b0 100644 >>--- a/libstdc++-v3/include/std/version >>+++ b/libstdc++-v3/include/std/version >>@@ -188,6 +188,8 @@ >> #endif >> #define __cpp_lib_type_identity 201806L >> #define __cpp_lib_unwrap_ref 201811L >>+#define __cpp_lib_semaphore 201907L >>+#define __cpp_lib_latch 201907L > > These features aren't supported in a freestanding implementation, so > should be in the #if _GLIBCXX_HOSTED block (and the macros should be > in alphabetical order). > >> >> #if _GLIBCXX_HOSTED >> #undef __cpp_lib_array_constexpr >>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >>new file mode 100644 >>index 00000000000..1ced9d44b20 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >>@@ -0,0 +1,103 @@ >>+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } > > Use { dg-add-options libatomic } instead of adding -latomic -L... ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-05-11 20:59 ` Thomas Rodgers @ 2020-05-23 22:52 ` Thomas Rodgers 2020-05-24 17:41 ` Thomas Rodgers 2020-06-06 0:29 ` Thomas Rodgers 0 siblings, 2 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-05-23 22:52 UTC (permalink / raw) To: libstdc++, gcc-patches; +Cc: Jonathan Wakely, Thomas Rodgers [-- Attachment #1: Type: text/plain, Size: 16 bytes --] Updated patch. [-- Attachment #2: 0001-Add-C-2a-synchronization-support.patch --] [-- Type: application/octet-stream, Size: 86718 bytes --] From a3decdc503fbaa0805358946ac5646bfa17840e4 Mon Sep 17 00:00:00 2001 From: Thomas Rodgers <rodgert@appliantology.com> Date: Mon, 6 Apr 2020 17:58:47 -0700 Subject: [PATCH] Add C++2a synchronization support Add support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 161 +++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 282 +++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 291 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 272 ++++++++++++++++ libstdc++-v3/include/std/atomic | 61 ++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 86 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 +++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.h | 88 ++++++ .../atomic/wait_notify/integrals.cc | 56 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 28 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_futex.cc | 51 +++ .../30_threads/semaphore/try_acquire_posix.cc | 169 ++++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 27 files changed, 2291 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 80aeb3f8959..b3ac1a3365f 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index eb437ad8d8d..e73ff8b3e64 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -397,6 +397,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -414,6 +415,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -445,6 +447,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -519,6 +523,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 3b66b040976..68d9e7e3756 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -562,6 +565,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -823,6 +851,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -911,6 +963,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1164,6 +1242,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1301,6 +1396,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + private: _Tp* _M_ptr; }; @@ -1396,6 +1507,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1551,6 +1678,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1660,6 +1803,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..adef80aca61 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,282 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { __no_timeout, __timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, + __futex_bitset_match_any = 0xffffffff + }; + + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, + nullptr, __futex_bitset_match_any); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (std::is_same_v<__platform_wait_clock_t, _Clock>) + { + return __platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) + return __atomic_wait_status::__no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout + : __atomic_wait_status::__timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::__no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) + return __atomic_wait_status::__timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::__no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return (__timed_waiters&) __waiters::_S_for(__t); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::__timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..92c1e2526ed --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,291 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + +#define _GLIBCXX_SPIN_COUNT_1 16 +#define _GLIBCXX_SPIN_COUNT_2 12 + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__numeric_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __futex_private_flag = 128, +#else + __futex_private_flag = 0, +#endif + __futex_wait = 0, + __futex_wake = 1, + __futex_wait_bitset = 9, + __futex_wake_bitset = 10, + __futex_wait_private = __futex_wait | __futex_private_flag, + __futex_wake_private = __futex_wake | __futex_private_flag + }; + + void + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + void + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept + { + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for((void*) __addr)) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) + { + if (__pred()) + return true; + + if (__i < _GLIBCXX_SPIN_COUNT_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..f0c4235d91c --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,272 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + + explicit __platform_semaphore(ptrdiff_t __count) noexcept + { + static_assert( __least_max_value <= SEM_VALUE_MAX, ""); + auto __e = sem_init(&_M_semaphore, 0, __count); + if (__e) + std::terminate(); + } + + ~__platform_semaphore() + { + auto __e = sem_destroy(&_M_semaphore); + if (__e) + std::terminate(); + } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto __err = sem_wait(&_M_semaphore); + if (__err) + std::terminate(); + } + + template<typename _Duration> + bool + __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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err) + std::terminate(); + return true; + } + + template<typename _Clock, typename _Duration> + bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + if constexpr (std::is_same<__clock_t, _Clock>::value) + { + return __try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__try_acquire_until_impl(__s_atime)) + return true; + + // 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 + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { return try_acquire_until(__clock_t::now() + __rtime); } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + do + { + auto __err = sem_post(&_M_semaphore); + if (__err) + std::terminate(); + } while (--__update); + } + + private: + sem_t _M_semaphore; + }; +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE + + template<typename _Tp> + struct __atomic_semaphore + { + explicit __atomic_semaphore(_Tp __count) + : _M_a(__count) + { } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = __platform_semaphore<__least_max_value>; +#else +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= __detail::__platform_wait_max_value), + __atomic_semaphore<__detail::__platform_wait_t>, + __atomic_semaphore<ptrdiff_t>>; + +// __platform_semaphore +# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= SEM_VALUE_MAX), + __platform_semaphore<__least_max_value>, + __atomic_semaphore<ptrdiff_t>>; +# else + template<ptrdiff_t __least_max_value> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; +# endif +#endif + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index a455286a784..3f18774031d 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_i.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_i.notify_one(); } + + void notify_all() const noexcept + { _M_i.notify_all(); } +#endif + }; #undef _GLIBCXX20_INIT @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..aa5299d9fdd --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr + _GLIBCXX_ALWAYS_INLINE ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..90cf3244647 --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,86 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >=0, ""); + + __semaphore_base<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t max() noexcept + { return __least_max_value; } + + void release(ptrdiff_t __update = 1) + { _M_sem.release(__update); } + + void acquire() + { _M_sem.acquire(); } + + bool try_acquire() noexcept + { return _M_sem.try_acquire(); } + + template<class _Rep, class _Period> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return _M_sem.try_acquire_for(__rel_time); } + + template<class _Clock, class _Duration> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) + { return _M_sem.try_acquire_until(__abs_time); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index c6bde2cfbda..f09da3344f7 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -189,6 +189,8 @@ #endif #define __cpp_lib_type_identity 201806L #define __cpp_lib_unwrap_ref 201811L +#define __cpp_lib_semaphore 201907L +#define __cpp_lib_latch 201907L #if _GLIBCXX_HOSTED #undef __cpp_lib_array_constexpr diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..0da374ece87 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,88 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + VERIFY(check_atomic_wait_notify(a, b) == b); + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..2afd19a7d14 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..756727f33b3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..10bb500d261 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..b96b8a59c64 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..1ac9d261ca5 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..d38cef86cfc --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..965554a3c28 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc new file mode 100644 index 00000000000..5e05606e97f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +void test01() +{ + // the implementation optimizes for values of least_max_t that can fit + // in a futex, make sure we cover the case where least_max_t doesn't + auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max(); + std::counting_semaphore<least_max_t> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + test01(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..bf99fd3cf8f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + // The implementation supports posix as an implementation strategy + // make sure we cover that case +#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test03() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +void test04() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test05() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + test01(); + test02(); + test03(); + test04(); + test05(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..cc67c5c0bf0 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 [-- Attachment #3: Type: text/plain, Size: 47984 bytes --] > On May 11, 2020, at 1:59 PM, Thomas Rodgers via Libstdc++ <libstdc++@gcc.gnu.org> wrote: > > I *think* I have addressed everything in the attached patch. > <0001-Add-C-2a-synchronization-support.patch> > Jonathan Wakely writes: > >> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote: >>> * Note, this patch supersedes my previous atomic wait and semaphore >>> patches. >>> >>> Add support for - >>> atomic wait/notify_one/notify_all >>> counting_semaphore >>> binary_semaphore >>> latch >>> >>> * include/Makefile.am (bits_headers): Add new header. >>> * include/Makefile.in: Regenerate. >>> * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. >> >> Should be two colons before wait. >> >>> diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in >>> index eb437ad8d8d..e73ff8b3e64 100644 >>> --- a/libstdc++-v3/include/Makefile.in >>> +++ b/libstdc++-v3/include/Makefile.in >> >> Generated files don't need to be in the patch. >> >>> diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >>> index 87fe0bd6000..b2cec0f1722 100644 >>> --- a/libstdc++-v3/include/bits/atomic_base.h >>> +++ b/libstdc++-v3/include/bits/atomic_base.h >>> @@ -37,6 +37,11 @@ >>> #include <bits/atomic_lockfree_defines.h> >>> #include <bits/move.h> >>> >>> +#if __cplusplus > 201703L >>> +#include <bits/atomic_wait.h> >>> +#include <iostream> >> >> <iostream> shouldn't be here (it adds runtime cost, as well as >> compile-time). >> >>> @@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> __cmpexch_failure_order(__m)); >>> } >>> >>> +#if __cplusplus > 201703L >>> + _GLIBCXX_ALWAYS_INLINE void >>> + wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept >> >> Please format everything to <= 80 columns (ideally < 80). >> >>> + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >>> + { >>> + __atomic_wait(&_M_p, __old, >> >> This should be qualified to prevent ADL. >> >>> + [__m, this, __old]() >>> + { return this->load(__m) != __old; }); >>> + } >>> + >>> + // TODO add const volatile overload >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + notify_one() const noexcept >>> + { __atomic_notify(&_M_p, false); } >> >> Qualify to prevent ADL here too, and all similar calls. >> >>> +#if __cplusplus > 201703L >>> + template<typename _Tp> >>> + _GLIBCXX_ALWAYS_INLINE void >>> + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept >>> + { >>> + __atomic_wait(__ptr, *std::__addressof(__old), >> >> Can't this just be __old instead of *std::__addressof(__old) ? >> >>> + [=]() >>> + { return load(__ptr, __m) == *std::__addressof(__old); }); >> >> Same here? >> >>> diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >>> new file mode 100644 >>> index 00000000000..10f0fe50ed9 >>> --- /dev/null >>> +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >>> @@ -0,0 +1,270 @@ >>> +// -*- C++ -*- header. >>> + >>> +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >>> +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >>> + >>> +#pragma GCC system_header >>> + >>> +#include <bits/c++config.h> >>> +#include <bits/functional_hash.h> >>> +#include <bits/atomic_wait.h> >>> + >>> +#include <chrono> >>> + >>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> +#include <sys/time.h> >>> +#endif >>> + >>> +namespace std _GLIBCXX_VISIBILITY(default) >>> +{ >>> + _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> + enum class __atomic_wait_status { __no_timeout, __timeout }; >> >> Blank line before and after this enum definition please. >> >>> + namespace __detail >>> + { >>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> + enum >>> + { >>> + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, >>> + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, >>> + __futex_bitset_match_any = 0xffffffff >>> + }; >>> + >>> + using __platform_wait_clock_t = chrono::steady_clock; >> >> Blank line after this using-decl please. >> >>> + template<typename _Duration> >>> + __atomic_wait_status >>> + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, >>> + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept >>> + { >>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>> + >> >> Eventually we'll want to move the rest of this function (which doesn't >> depend on the template argument) into the compiled library, but it's >> better to be header-only for now. >> >>> + struct timespec __rt = >>> + { >>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>> + static_cast<long>(__ns.count()) >>> + }; >>> + >>> + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, >>> + nullptr, __futex_bitset_match_any); >>> + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >>> + std::terminate(); >>> + return (__platform_wait_clock_t::now() < __atime) >>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>> + } >>> + >>> + template<typename _Clock, typename _Duration> >>> + __atomic_wait_status >>> + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >>> + const chrono::time_point<_Clock, _Duration>& __atime) >>> + { >>> + if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value) >> >> This is C++20 so you can use is_same_v here, which uses the intrinsic >> directly and avoids instantiating the is_same class template. >> >>> + { >>> + return __platform_wait_until_impl(__addr, __val, __atime); >>> + } >>> + else >>> + { >>> + const typename _Clock::time_point __c_entry = _Clock::now(); >>> + const __platform_wait_clock_t::time_point __s_entry = >>> + __platform_wait_clock_t::now(); >>> + const auto __delta = __atime - __c_entry; >>> + const auto __s_atime = __s_entry + __delta; >>> + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) >>> + return __atomic_wait_status::__no_timeout; >>> + >>> + // 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. >>> + if (_Clock::now() < __atime) >>> + return __atomic_wait_status::__no_timeout; >>> + return __atomic_wait_status::__timeout; >>> + } >>> + } >>> +#endif >>> + >>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >>> + template<typename _Duration> >>> + __atomic_wait_status >>> + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>> + const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime) >> >> The std:: qualification here isn't needed (and doesn't help with >> keeping the line below 80 cols). >> >>> + { >>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>> + >>> + __gthread_time_t __ts = >>> + { >>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>> + static_cast<long>(__ns.count()) >>> + }; >>> + >>> + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), >>> + CLOCK_MONOTONIC, >>> + &__ts); >>> + return (chrono::steady_clock::now() < __atime) >>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>> + } >>> +#endif >>> + >>> + template<typename _Duration> >>> + __atomic_wait_status >>> + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>> + const chrono::time_point<chrono::system_clock, _Duration>& __atime) >>> + { >>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>> + >>> + __gthread_time_t __ts = >>> + { >>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>> + static_cast<long>(__ns.count()) >>> + }; >>> + >>> + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), >>> + &__ts); >>> + return (chrono::system_clock::now() < __atime) >>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>> + } >>> + >>> + // return true if timeout >>> + template<typename _Clock, typename _Duration> >>> + __atomic_wait_status >>> + __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>> + const chrono::time_point<_Clock, _Duration>& __atime) >>> + { >>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >>> + using __clock_t = chrono::steady_clock; >>> +#else >>> + using __clock_t = chrono::system_clock; >>> +#endif >>> + const typename _Clock::time_point __c_entry = _Clock::now(); >>> + const __clock_t::time_point __s_entry = __clock_t::now(); >>> + const auto __delta = __atime - __c_entry; >>> + const auto __s_atime = __s_entry + __delta; >>> + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) >>> + return __atomic_wait_status::__no_timeout; >>> + // 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. >>> + if (_Clock::now() < __atime) >>> + return __atomic_wait_status::__no_timeout; >>> + return __atomic_wait_status::__timeout; >>> + } >>> + >>> + struct __timed_waiters : __waiters >>> + { >>> + template<typename _Clock, typename _Duration> >>> + __atomic_wait_status >>> + _M_do_wait_until(int32_t __version, >>> + const chrono::time_point<_Clock, _Duration>& __atime) >>> + { >>> + int32_t __cur = 0; >>> + __waiters::__lock_t __l(_M_mtx); >>> + while (__cur <= __version) >>> + { >>> + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) >>> + return __atomic_wait_status::__timeout; >>> + >>> + int32_t __last = __cur; >>> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >>> + if (__cur < __last) >>> + break; // break the loop if version overflows >>> + } >>> + return __atomic_wait_status::__no_timeout; >>> + } >>> + >>> + static __timed_waiters& >>> + _S_timed_for(void* __t) >>> + { >>> + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); >>> + return (__timed_waiters&) __waiters::_S_for(__t); >>> + } >>> + }; >>> + } // namespace __detail >>> + >>> + template<typename _Tp, typename _Pred, >>> + typename _Clock, typename _Duration> >>> + bool >>> + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >>> + const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>> + { >>> + using namespace __detail; >>> + >>> + if (__atomic_spin(__pred)) >> >> Qualify to prevent ADL. >> >>> + return true; >>> + >>> + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >>> + auto __version = __w._M_enter_wait(); >>> + do >>> + { >>> + __atomic_wait_status __res; >>> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >>> + { >>> + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old, >>> + __atime); >>> + } >>> + else >>> + { >>> + __res = __w._M_do_wait_until(__version, __atime); >>> + } >>> + if (__res == __atomic_wait_status::__timeout) >>> + return false; >>> + } >>> + while (!__pred() && __atime < _Clock::now()); >>> + __w._M_leave_wait(); >>> + >>> + // if timed out, return false >>> + return (_Clock::now() < __atime); >>> + } >>> + >>> + template<typename _Tp, typename _Pred, >>> + typename _Rep, typename _Period> >>> + bool >>> + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, >>> + const chrono::duration<_Rep, _Period>& __rtime) noexcept >>> + { >>> + using namespace __detail; >>> + >>> + if (__atomic_spin(__pred)) >>> + return true; >>> + >>> + if (!__rtime.count()) >>> + return false; // no rtime supplied, and spin did not acquire >>> + >>> + using __dur = chrono::steady_clock::duration; >>> + auto __reltime = chrono::duration_cast<__dur>(__rtime); >>> + if (__reltime < __rtime) >>> + ++__reltime; >>> + >>> + >>> + return __atomic_wait_until(__addr, __old, std::move(__pred), >>> + chrono::steady_clock::now() + __reltime); >>> + } >>> +_GLIBCXX_END_NAMESPACE_VERSION >>> +} // namespace std >>> +#endif >>> diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >>> new file mode 100644 >>> index 00000000000..32070a54f40 >>> --- /dev/null >>> +++ b/libstdc++-v3/include/bits/atomic_wait.h >>> @@ -0,0 +1,280 @@ >>> +// -*- C++ -*- header. >>> + >>> +// Copyright (C) 2020 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 >>> + >>> +#pragma GCC system_header >>> + >>> +#include <bits/c++config.h> >>> +#include <bits/functional_hash.h> >>> +#include <bits/gthr.h> >>> +#include <bits/std_mutex.h> >>> +#include <bits/unique_lock.h> >>> + >>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> +#include <climits> >>> +#include <unistd.h> >>> +#include <syscall.h> >>> +#endif >>> + >>> +#define _GLIBCXX_SPIN_COUNT_1 16 >>> +#define _GLIBCXX_SPIN_COUNT_2 12 >>> + >>> +// TODO get this from Autoconf >>> +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >>> + >>> +namespace std _GLIBCXX_VISIBILITY(default) >>> +{ >>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>> + namespace __detail >>> + { >>> + using __platform_wait_t = int; >>> + >>> + template<class _Tp> >> >> This should be typename not class. >> >>> + struct __platform_wait_uses_type >>> + { >>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> + enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, >> >> This should be remove_cv_t. >> >>> + __platform_wait_t>::value }; >>> +#else >>> + enum { __value = std::false_type::value }; >>> +#endif >> >> There's no need to use the C++03 enum hack here, it should just derive >> from true_type or false_type. >> >> template<typename _Tp> >> struct __platform_wait_uses_type >> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX >> : is_same<std::remove_cv_t<_Tp>, __platform_wait_t> >> #else >> : false_type >> #endif >> { }; >> >> Or better yet, just use a variable template: >> >> template<typename _Tp> >> inline constexpr bool __platform_wait_uses_type >> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX >> = is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>; >> #else >> = false; >> #endif >> >> >> >>> + }; >>> + >>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> + enum >>> + { >>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >>> + __futex_private_flag = 128, >>> +#else >>> + __futex_private_flag = 0, >>> +#endif >>> + __futex_wait = 0, >>> + __futex_wake = 1, >>> + __futex_wait_bitset = 9, >>> + __futex_wake_bitset = 10, >>> + __futex_wait_private = __futex_wait | __futex_private_flag, >>> + __futex_wake_private = __futex_wake | __futex_private_flag >>> + }; >>> + >>> + void >>> + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept >>> + { >>> + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); >>> + if (__e && !(errno == EINTR || errno == EAGAIN)) >>> + std::terminate(); >>> + } >>> + >>> + void >>> + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept >>> + { >>> + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); >>> + } >>> +#endif >>> + >>> + struct alignas(64) __waiters >> >> Isn't alignas(64) already implied by the first data member? >> >>> + { >>> + int32_t alignas(64) _M_ver = 0; >>> + int32_t alignas(64) _M_wait = 0; >>> + >>> + // TODO make this used only where we don't have futexes >> >> Don't we always need these even with futexes, for the types that don't >> use a futex? >> >>> + using __lock_t = std::unique_lock<std::mutex>; >>> + mutable __lock_t::mutex_type _M_mtx; >>> + >>> +#ifdef __GTHREAD_COND_INIT >>> + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >>> + __waiters() noexcept = default; >> >> If we moved std::condition_variable into its own header (or >> <bits/std_mutex.h>, could we reuse that here instead of using >> __gthread_cond_t directly? >> >>> +#else >>> + mutable __gthread_cond_t _M_cv; >>> + __waiters() noexcept >>> + { >>> + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); >>> + } >>> +#endif >>> + >>> + int32_t >>> + _M_enter_wait() noexcept >>> + { >>> + int32_t __res; >>> + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); >>> + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); >>> + return __res; >>> + } >>> + >>> + void >>> + _M_leave_wait() noexcept >>> + { >>> + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); >>> + } >>> + >>> + void >>> + _M_do_wait(int32_t __version) const noexcept >>> + { >>> + int32_t __cur = 0; >>> + while (__cur <= __version) >>> + { >>> + __waiters::__lock_t __l(_M_mtx); >>> + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); >>> + if (__e) >>> + std::terminate(); >>> + int32_t __last = __cur; >>> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >>> + if (__cur < __last) >>> + break; // break the loop if version overflows >>> + } >>> + } >>> + >>> + int32_t >>> + _M_waiting() const noexcept >>> + { >>> + int32_t __res; >>> + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >>> + return __res; >>> + } >>> + >>> + void >>> + _M_notify(bool __all) noexcept >>> + { >>> + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >>> + auto __e = __gthread_cond_broadcast(&_M_cv); >>> + if (__e) >>> + __throw_system_error(__e); >>> + } >>> + >>> + static __waiters& >>> + _S_for(void* __t) >>> + { >>> + const unsigned char __mask = 0xf; >>> + static __waiters __w[__mask + 1]; >>> + >>> + auto __key = _Hash_impl::hash(__t) & __mask; >>> + return __w[__key]; >>> + } >>> + }; >>> + >>> + struct __waiter >>> + { >>> + __waiters& _M_w; >>> + int32_t _M_version; >>> + >>> + template<typename _Tp> >>> + __waiter(const _Tp* __addr) noexcept >>> + : _M_w(__waiters::_S_for((void*) __addr)) >>> + , _M_version(_M_w._M_enter_wait()) >>> + { } >>> + >>> + ~__waiter() >>> + { _M_w._M_leave_wait(); } >>> + >>> + void _M_do_wait() noexcept >>> + { _M_w._M_do_wait(_M_version); } >>> + }; >>> + >>> + void >>> + __thread_relax() noexcept >>> + { >>> +#if defined __i386__ || defined __x86_64__ >>> + __builtin_ia32_pause(); >>> +#elif defined _GLIBCXX_USE_SCHED_YIELD >>> + __gthread_yield(); >>> +#endif >>> + } >>> + >>> + void >>> + __thread_yield() noexcept >>> + { >>> +#if defined _GLIBCXX_USE_SCHED_YIELD >>> + __gthread_yield(); >>> +#endif >>> + } >>> + >>> + } // namespace __detail >>> + >>> + template<class _Pred> >> >> s/class/template/ >> >>> + bool >>> + __atomic_spin(_Pred __pred) noexcept >>> + { >>> + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >>> + { >>> + if (__pred()) >>> + return true; >>> + >>> + if (__i < _GLIBCXX_SPIN_COUNT_2) >>> + __detail::__thread_relax(); >>> + else >>> + __detail::__thread_yield(); >>> + } >>> + return false; >>> + } >>> + >>> + template<class _Tp, class _Pred> >> >> s/class/template/ >> >>> + void >>> + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >>> + { >>> + using namespace __detail; >>> + if (__atomic_spin(__pred)) >>> + return; >>> + >>> + __waiter __w(__addr); >>> + while (!__pred()) >>> + { >>> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >>> + { >>> + __platform_wait((__platform_wait_t*)(void*) __addr, __old); >>> + } >>> + else >>> + { >>> + // TODO support timed backoff when this can be moved into the lib >>> + __w._M_do_wait(); >>> + } >>> + } >>> + } >>> + >>> + template<class _Tp> >> >> s/class/template/ >> >>> + void >>> + __atomic_notify(const _Tp* __addr, bool __all) noexcept >>> + { >>> + using namespace __detail; >>> + auto& __w = __waiters::_S_for((void*)__addr); >>> + if (!__w._M_waiting()) >> >> When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting() >> ever be true? Won't this always return before notifying? >> >> Is there meant to be a __waiter constructed here? >> >>> + return; >>> + >>> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >>> + { >>> + __platform_notify((__platform_wait_t*)(void*) __addr, __all); >>> + } >>> + else >>> + { >>> + __w._M_notify(__all); >>> + } >>> + } >>> +_GLIBCXX_END_NAMESPACE_VERSION >>> +} // namespace std >>> +#endif >>> diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >>> new file mode 100644 >>> index 00000000000..b3c83bbc70b >>> --- /dev/null >>> +++ b/libstdc++-v3/include/bits/semaphore_base.h >>> @@ -0,0 +1,270 @@ >>> +// -*- C++ -*- header. >>> + >>> +// Copyright (C) 2020 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/semaphore.h >> >> Should be bits/semaphore_base.h >> >>> + * This is an internal header file, included by other library headers. >>> + * Do not attempt to use it directly. @headername{atomic} >> >> Should be @headername{semaphore} >> >>> + */ >>> + >>> +#ifndef _GLIBCXX_SEMAPHORE_BASE_H >>> +#define _GLIBCXX_SEMAPHORE_BASE_H 1 >>> + >>> +#pragma GCC system_header >>> + >>> +#include <bits/c++config.h> >>> +#include <bits/atomic_base.h> >>> +#include <bits/atomic_timed_wait.h> >>> + >>> +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) >>> +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >>> +#include <semaphore.h> >>> +#endif >>> + >>> +#include <chrono> >>> +#include <type_traits> >>> +#include <limits> >> >> <ext/numeric_traits.h> is much smaller than <limits> and should be >> used for limits of integer types. (I recently added >> <bits/int_limits.h> too but that was a mistake that I need to fix). >> >> >>> +namespace std _GLIBCXX_VISIBILITY(default) >>> +{ >>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>> + >>> +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >>> + template<ptrdiff_t __least_max_t> >> >> __least_max_t isn't a type so shouldn't have the _t suffix. >> >>> + struct __platform_semaphore >>> + { >>> + using __clock_t = chrono::system_clock; >>> + >>> + __platform_semaphore(ptrdiff_t __count) noexcept >> >> Should this constructor be explicit? >> >>> + { >>> + static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX"); >> >> Our static_assert messages should state the positive condition, not >> the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX", >> which is what the real condition is anyway, so you might as well omit >> the string literal. >> >>> + auto __e = sem_init(&_M_semaphore, 0, __count); >>> + if (__e) >>> + std::terminate(); >>> + } >>> + >>> + ~__platform_semaphore() >>> + { >>> + auto __e = sem_destroy(&_M_semaphore); >>> + if (__e) >>> + std::terminate(); >>> + } >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + acquire() noexcept >>> + { >>> + auto __err = sem_wait(&_M_semaphore); >>> + if (__err) >>> + std::terminate(); >>> + } >>> + >>> + template<typename _Duration> >>> + _GLIBCXX_ALWAYS_INLINE bool >> >> Do we really need this to be always_inline? >> >>> + __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); >>> + >>> + struct timespec __ts = >>> + { >>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>> + static_cast<long>(__ns.count()) >>> + }; >>> + >>> + auto __err = sem_timedwait(&_M_semaphore, &__ts); >>> + if (__err && (errno == ETIMEDOUT)) >>> + return false; >>> + else if (__err) >>> + std::terminate(); >>> + return true; >>> + } >>> + >>> + template<typename _Clock, typename _Duration> >>> + _GLIBCXX_ALWAYS_INLINE bool >> >> always_inline? >> >>> + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>> + { >>> + if constexpr (std::is_same<__clock_t, _Clock>::value) >> >> is_same_v >> >>> + { >>> + return __try_acquire_until_impl(__atime); >>> + } >>> + else >>> + { >>> + const typename _Clock::time_point __c_entry = _Clock::now(); >>> + const __clock_t __s_entry = __clock_t::now(); >>> + const auto __delta = __atime - __c_entry; >>> + const auto __s_atime = __s_entry + __delta; >>> + if (__try_acquire_until_impl(__s_atime)) >>> + return true; >>> + >>> + // 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 >>> + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>> + { return try_acquire_until(__clock_t::now() + __rtime); } >>> + >>> + template<typename _Clock, typename _Duration> >>> + _GLIBCXX_ALWAYS_INLINE void >>> + release(ptrdiff_t __update) noexcept >>> + { >>> + do >>> + { >>> + auto __err = sem_post(&_M_semaphore); >>> + if (__err) >>> + std::terminate(); >>> + } while (--__update); >>> + } >>> + >>> + private: >>> + sem_t _M_semaphore; >>> + }; >>> +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE >>> + >>> + template<typename _Tp> >>> + struct __atomic_semaphore >>> + { >>> + static constexpr size_t _S_alignment = __alignof__(_Tp); >>> + >>> + __atomic_semaphore(_Tp __count) >> >> Should this be explicit? >> >>> + : _M_a(__count) >>> + { } >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + acquire() noexcept >>> + { >>> + auto const __pred = [this] >>> + { >>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>> + if (__old == 0) >>> + return false; >>> + return __atomic_impl::compare_exchange_strong(&this->_M_a, >>> + __old, __old - 1, >>> + memory_order::acquire, >>> + memory_order::release); >>> + }; >>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>> + __atomic_wait(&_M_a, __old, __pred); >>> + } >>> + >>> + bool >>> + try_acquire() noexcept >>> + { >>> + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); >>> + if (__old == 0) >>> + return false; >>> + >>> + return __atomic_spin([this, &__old] >>> + { >>> + return __atomic_impl::compare_exchange_weak(&this->_M_a, >>> + __old, __old - 1, >>> + memory_order::acquire, >>> + memory_order::release); >>> + }); >>> + } >>> + >>> + template<typename _Clock, typename _Duration> >>> + _GLIBCXX_ALWAYS_INLINE bool >>> + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>> + { >>> + auto const __pred = [this] >>> + { >>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>> + if (__old == 0) >>> + return false; >>> + return __atomic_impl::compare_exchange_strong(&this->_M_a, >>> + __old, __old - 1, >>> + memory_order::acquire, >>> + memory_order::release); >>> + }; >>> + >>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>> + return __atomic_wait_until(&_M_a, __old, __pred, __atime); >>> + } >>> + >>> + template<typename _Rep, typename _Period> >>> + _GLIBCXX_ALWAYS_INLINE bool >>> + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>> + { >>> + auto const __pred = [this] >>> + { >>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>> + if (__old == 0) >>> + return false; >>> + return __atomic_impl::compare_exchange_strong(&this->_M_a, >>> + __old, __old - 1, >>> + memory_order::acquire, >>> + memory_order::release); >>> + }; >>> + >>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>> + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); >>> + } >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + release(ptrdiff_t __update) noexcept >>> + { >>> + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) >>> + return; >>> + if (__update > 1) >>> + __atomic_impl::notify_all(&_M_a); >>> + else >>> + __atomic_impl::notify_one(&_M_a); >>> + } >>> + >>> + private: >>> + alignas(_S_alignment) _Tp _M_a; >> >> Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no >> need for the _S_alignment constant if it's only used in one place. >> >>> + }; >>> + >>> +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE >>> + template<ptrdiff_t __least_max_t> >> >> Rename __least_max_t here too. >> >>> + using __semaphore_base = __platform_semaphore<__least_max_t>; >>> +#else >>> +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> + template<ptrdiff_t __least_max_t> >>> + using __semaphore_base = std::conditional<(__least_max_t > 0 >> >> This should use conditional_t<> not conditional<>::type. >> >> The least-max_value can't be negative. If it's zero, can't we use a >> futex or semaphore? So the '__least_max_t > 0' condition is wrong? >> >>> + && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()), >> >> Should that be <= rather than < ? >> >>> + __atomic_semaphore<__detail::__platform_wait_t>, >>> + __atomic_semaphore<ptrdiff_t>>::type; >>> + // __platform_semaphore >>> +# else >>> +# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >> >> Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid >> an extra level of #if nesting. >> >>> + template<ptrdiff_t __least_max_t> >>> + using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX), >>> + __platform_semaphore<__least_max_t>, >>> + __atomic_semaphore<ptrdiff_t>>::type; >>> +# else >>> + template<ptrdiff_t __least_max_t> >>> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; >>> +# endif >>> +# endif >>> +#endif >>> + >>> +_GLIBCXX_END_NAMESPACE_VERSION >>> +} // namespace std >>> + >>> +#endif >>> diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic >>> index a455286a784..3f18774031d 100644 >>> --- a/libstdc++-v3/include/std/atomic >>> +++ b/libstdc++-v3/include/std/atomic >>> @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> compare_exchange_strong(bool& __i1, bool __i2, >>> memory_order __m = memory_order_seq_cst) volatile noexcept >>> { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >>> + >>> +#if __cplusplus > 201703L >>> + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept >>> + { _M_base.wait(__old, __m); } >>> + >>> + // TODO add const volatile overload >>> + >>> + void notify_one() const noexcept >>> + { _M_base.notify_one(); } >>> + >>> + void notify_all() const noexcept >>> + { _M_base.notify_all(); } >>> +#endif >>> }; >>> >>> #if __cplusplus <= 201703L >>> @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> memory_order __m = memory_order_seq_cst) volatile noexcept >>> { return compare_exchange_strong(__e, __i, __m, >>> __cmpexch_failure_order(__m)); } >>> +#if __cplusplus > 201703L >>> + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept >>> + { _M_i.wait(__old, __m); } >>> + >>> + // TODO add const volatile overload >>> + >>> + void notify_one() const noexcept >>> + { _M_i.notify_one(); } >>> + >>> + void notify_all() const noexcept >>> + { _M_i.notify_all(); } >>> +#endif >>> + >>> }; >>> #undef _GLIBCXX20_INIT >>> >>> @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> __cmpexch_failure_order(__m)); >>> } >>> >>> +#if __cplusplus > 201703L >>> + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >>> + { _M_b.wait(__old, __m); } >>> + >>> + // TODO add const volatile overload >>> + >>> + void notify_one() const noexcept >>> + { _M_b.notify_one(); } >>> + >>> + void notify_all() const noexcept >>> + { _M_b.notify_all(); } >>> +#endif >>> __pointer_type >>> fetch_add(ptrdiff_t __d, >>> memory_order __m = memory_order_seq_cst) noexcept >>> @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> memory_order_seq_cst); >>> } >>> >>> + >>> +#if __cplusplus > 201703L >>> + template<typename _Tp> >>> + inline void atomic_wait(const atomic<_Tp>* __a, >>> + typename std::atomic<_Tp>::value_type __old) noexcept >>> + { __a->wait(__old); } >>> + >>> + template<typename _Tp> >>> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, >>> + typename std::atomic<_Tp>::value_type __old, >>> + std::memory_order __m) noexcept >>> + { __a->wait(__old, __m); } >>> + >>> + template<typename _Tp> >>> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >>> + { __a->notify_one(); } >>> + >>> + template<typename _Tp> >>> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept >>> + { __a->notify_all(); } >>> + >>> +#endif // C++2a >>> + >>> // Function templates for atomic_integral and atomic_pointer operations only. >>> // Some operations (and, or, xor) are only available for atomic integrals, >>> // which is implemented by taking a parameter of type __atomic_base<_ITp>*. >>> diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch >>> new file mode 100644 >>> index 00000000000..0099877416e >>> --- /dev/null >>> +++ b/libstdc++-v3/include/std/latch >>> @@ -0,0 +1,91 @@ >>> +//<latch> -*- C++ -*- >> >> A space before <latch>. >> >>> + >>> +// Copyright (C) 2020 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 include/latch >>> + * This is a Standard C++ Library header. >> >> Align "This" with "@file" here. >> >>> + */ >>> + >>> +#ifndef _GLIBCXX_LATCH >>> +#define _GLIBCXX_LATCH >>> + >>> +#pragma GCC system_header >>> + >>> +#if __cplusplus > 201703L >>> +#define __cpp_lib_latch 201907L >>> + >>> +#include <bits/atomic_base.h> >>> +#include <limits> >> >> Use <ext/numeric_traits.h> here too. >> >>> +namespace std _GLIBCXX_VISIBILITY(default) >>> +{ >>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>> + >>> + class latch >>> + { >>> + static constexpr size_t _S_alignment = __alignof__(ptrdiff_t); >>> + public: >>> + static constexpr >>> + _GLIBCXX_ALWAYS_INLINE ptrdiff_t >>> + max() noexcept >>> + { return numeric_limits<ptrdiff_t>::max(); } >>> + >>> + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } >>> + >>> + ~latch() = default; >>> + latch(const latch&) = delete; >>> + latch& operator=(const latch&) = delete; >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + count_down(ptrdiff_t __update = 1) >>> + { >>> + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); >>> + if (__old == __update) >>> + __atomic_impl::notify_all(&_M_a); >>> + } >>> + >>> + _GLIBCXX_ALWAYS_INLINE bool >>> + try_wait() const noexcept >>> + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + wait() const >>> + { >>> + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >>> + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); >>> + } >>> + >>> + _GLIBCXX_ALWAYS_INLINE void >>> + arrive_and_wait(ptrdiff_t __update = 1) >>> + { >>> + count_down(); >>> + wait(); >>> + } >>> + >>> + private: >>> + alignas(_S_alignment) ptrdiff_t _M_a; >> >> Just use __alignof__ directly here and get rid of _S_alignment? >> >>> + }; >>> +_GLIBCXX_END_NAMESPACE_VERSION >>> +} // namespace >>> +#endif // __cplusplus > 201703L >>> +#endif // _GLIBCXX_LATCH >>> diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore >>> new file mode 100644 >>> index 00000000000..b51940b46ac >>> --- /dev/null >>> +++ b/libstdc++-v3/include/std/semaphore >>> @@ -0,0 +1,81 @@ >>> +//<semaphore> -*- C++ -*- >> >> A space before <semaphore>. >> >>> + >>> +// Copyright (C) 2020 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 include/semaphore >>> + * This is a Standard C++ Library header. >> >> Align "This" with "@file" here. >> >>> + */ >>> + >>> +#ifndef _GLIBCXX_SEMAPHORE >>> +#define _GLIBCXX_SEMAPHORE >>> + >>> +#pragma GCC system_header >>> + >>> +#if __cplusplus > 201703L >>> +#define __cpp_lib_semaphore 201907L >>> +#include <bits/semaphore_base.h> >>> + >>> +namespace std _GLIBCXX_VISIBILITY(default) >>> +{ >>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>> + >>> + template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()> >>> + class counting_semaphore >>> + { >> >> I don't see a static_assert making it ill-formed to use a negative >> value for __least_max_value. Is that enforced somewhere else? >> >> The standard says it's ill-formed, so we should also have a _neg.cc >> test checking that we reject it. >> >>> + __semaphore_base<__least_max_value> _M_sem; >> >> Blank line after this please. >> >>> + public: >>> + explicit counting_semaphore(ptrdiff_t __desired) noexcept >>> + : _M_sem(__desired) >>> + { } >>> + >>> + ~counting_semaphore() = default; >>> + >>> + counting_semaphore(const counting_semaphore&) = delete; >>> + counting_semaphore& operator=(const counting_semaphore&) = delete; >>> + >>> + static constexpr ptrdiff_t max() noexcept >>> + { return __least_max_value; } >>> + >>> + void release(ptrdiff_t __update = 1) >>> + { _M_sem.release(__update); } >>> + >>> + void acquire() >>> + { _M_sem.acquire(); } >>> + >>> + bool try_acquire() noexcept >>> + { return _M_sem.try_acquire(); } >>> + >>> + template<class _Rep, class _Period> >> >> s/class/template/ >> >>> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) >>> + { return _M_sem.try_acquire_for(__rel_time); } >>> + >>> + template<class _Clock, class _Duration> >> >> s/class/template/ >> >>> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) >>> + { return _M_sem.try_acquire_until(__abs_time); } >>> + }; >>> + >>> + using binary_semaphore = std::counting_semaphore<1>; >>> +_GLIBCXX_END_NAMESPACE_VERSION >>> +} // namespace >>> +#endif // __cplusplus > 201703L >>> +#endif // _GLIBCXX_SEMAPHORE >>> diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >>> index c3a5bd26e63..390990282b0 100644 >>> --- a/libstdc++-v3/include/std/version >>> +++ b/libstdc++-v3/include/std/version >>> @@ -188,6 +188,8 @@ >>> #endif >>> #define __cpp_lib_type_identity 201806L >>> #define __cpp_lib_unwrap_ref 201811L >>> +#define __cpp_lib_semaphore 201907L >>> +#define __cpp_lib_latch 201907L >> >> These features aren't supported in a freestanding implementation, so >> should be in the #if _GLIBCXX_HOSTED block (and the macros should be >> in alphabetical order). >> >>> >>> #if _GLIBCXX_HOSTED >>> #undef __cpp_lib_array_constexpr >>> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >>> new file mode 100644 >>> index 00000000000..1ced9d44b20 >>> --- /dev/null >>> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >>> @@ -0,0 +1,103 @@ >>> +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >> >> Use { dg-add-options libatomic } instead of adding -latomic -L... > ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-05-23 22:52 ` Thomas Rodgers @ 2020-05-24 17:41 ` Thomas Rodgers 2020-06-06 0:29 ` Thomas Rodgers 1 sibling, 0 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-05-24 17:41 UTC (permalink / raw) To: libstdc++, gcc-patches; +Cc: Thomas Rodgers, Jonathan Wakely [-- Attachment #1: Type: text/plain, Size: 100 bytes --] The previous patch had a Makefile.in included, should be a property filtered patch attached now. [-- Attachment #2: 0001-Add-C-2a-synchronization-support_f.patch --] [-- Type: application/octet-stream, Size: 85450 bytes --] From a3decdc503fbaa0805358946ac5646bfa17840e4 Mon Sep 17 00:00:00 2001 From: Thomas Rodgers <rodgert@appliantology.com> Date: Mon, 6 Apr 2020 17:58:47 -0700 Subject: [PATCH] Add C++2a synchronization support Add support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 161 +++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 282 +++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 291 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 272 ++++++++++++++++ libstdc++-v3/include/std/atomic | 61 ++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 86 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 +++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.h | 88 ++++++ .../atomic/wait_notify/integrals.cc | 56 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 28 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_futex.cc | 51 +++ .../30_threads/semaphore/try_acquire_posix.cc | 169 ++++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 27 files changed, 2291 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 80aeb3f8959..b3ac1a3365f 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 3b66b040976..68d9e7e3756 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -562,6 +565,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -823,6 +851,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -911,6 +963,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1164,6 +1242,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1301,6 +1396,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + private: _Tp* _M_ptr; }; @@ -1396,6 +1507,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1551,6 +1678,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1660,6 +1803,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..adef80aca61 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,282 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { __no_timeout, __timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, + __futex_bitset_match_any = 0xffffffff + }; + + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, + nullptr, __futex_bitset_match_any); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (std::is_same_v<__platform_wait_clock_t, _Clock>) + { + return __platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) + return __atomic_wait_status::__no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout + : __atomic_wait_status::__timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::__no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) + return __atomic_wait_status::__timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::__no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return (__timed_waiters&) __waiters::_S_for(__t); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::__timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..92c1e2526ed --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,291 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + +#define _GLIBCXX_SPIN_COUNT_1 16 +#define _GLIBCXX_SPIN_COUNT_2 12 + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__numeric_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __futex_private_flag = 128, +#else + __futex_private_flag = 0, +#endif + __futex_wait = 0, + __futex_wake = 1, + __futex_wait_bitset = 9, + __futex_wake_bitset = 10, + __futex_wait_private = __futex_wait | __futex_private_flag, + __futex_wake_private = __futex_wake | __futex_private_flag + }; + + void + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + void + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept + { + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for((void*) __addr)) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) + { + if (__pred()) + return true; + + if (__i < _GLIBCXX_SPIN_COUNT_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..f0c4235d91c --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,272 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + + explicit __platform_semaphore(ptrdiff_t __count) noexcept + { + static_assert( __least_max_value <= SEM_VALUE_MAX, ""); + auto __e = sem_init(&_M_semaphore, 0, __count); + if (__e) + std::terminate(); + } + + ~__platform_semaphore() + { + auto __e = sem_destroy(&_M_semaphore); + if (__e) + std::terminate(); + } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto __err = sem_wait(&_M_semaphore); + if (__err) + std::terminate(); + } + + template<typename _Duration> + bool + __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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err) + std::terminate(); + return true; + } + + template<typename _Clock, typename _Duration> + bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + if constexpr (std::is_same<__clock_t, _Clock>::value) + { + return __try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__try_acquire_until_impl(__s_atime)) + return true; + + // 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 + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { return try_acquire_until(__clock_t::now() + __rtime); } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + do + { + auto __err = sem_post(&_M_semaphore); + if (__err) + std::terminate(); + } while (--__update); + } + + private: + sem_t _M_semaphore; + }; +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE + + template<typename _Tp> + struct __atomic_semaphore + { + explicit __atomic_semaphore(_Tp __count) + : _M_a(__count) + { } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = __platform_semaphore<__least_max_value>; +#else +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= __detail::__platform_wait_max_value), + __atomic_semaphore<__detail::__platform_wait_t>, + __atomic_semaphore<ptrdiff_t>>; + +// __platform_semaphore +# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= SEM_VALUE_MAX), + __platform_semaphore<__least_max_value>, + __atomic_semaphore<ptrdiff_t>>; +# else + template<ptrdiff_t __least_max_value> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; +# endif +#endif + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index a455286a784..3f18774031d 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_i.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_i.notify_one(); } + + void notify_all() const noexcept + { _M_i.notify_all(); } +#endif + }; #undef _GLIBCXX20_INIT @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..aa5299d9fdd --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr + _GLIBCXX_ALWAYS_INLINE ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..90cf3244647 --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,86 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >=0, ""); + + __semaphore_base<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t max() noexcept + { return __least_max_value; } + + void release(ptrdiff_t __update = 1) + { _M_sem.release(__update); } + + void acquire() + { _M_sem.acquire(); } + + bool try_acquire() noexcept + { return _M_sem.try_acquire(); } + + template<class _Rep, class _Period> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return _M_sem.try_acquire_for(__rel_time); } + + template<class _Clock, class _Duration> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) + { return _M_sem.try_acquire_until(__abs_time); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index c6bde2cfbda..f09da3344f7 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -189,6 +189,8 @@ #endif #define __cpp_lib_type_identity 201806L #define __cpp_lib_unwrap_ref 201811L +#define __cpp_lib_semaphore 201907L +#define __cpp_lib_latch 201907L #if _GLIBCXX_HOSTED #undef __cpp_lib_array_constexpr diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..0da374ece87 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,88 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + VERIFY(check_atomic_wait_notify(a, b) == b); + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..2afd19a7d14 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..756727f33b3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..10bb500d261 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..b96b8a59c64 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..1ac9d261ca5 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..d38cef86cfc --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..965554a3c28 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc new file mode 100644 index 00000000000..5e05606e97f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +void test01() +{ + // the implementation optimizes for values of least_max_t that can fit + // in a futex, make sure we cover the case where least_max_t doesn't + auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max(); + std::counting_semaphore<least_max_t> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + test01(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..bf99fd3cf8f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + // The implementation supports posix as an implementation strategy + // make sure we cover that case +#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test03() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +void test04() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test05() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + test01(); + test02(); + test03(); + test04(); + test05(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..cc67c5c0bf0 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 [-- Attachment #3: Type: text/plain, Size: 49514 bytes --] > On May 23, 2020, at 3:52 PM, Thomas Rodgers <rodgert@appliantology.com> wrote: > > Updated patch. > > <0001-Add-C-2a-synchronization-support.patch> > >> On May 11, 2020, at 1:59 PM, Thomas Rodgers via Libstdc++ <libstdc++@gcc.gnu.org> wrote: >> >> I *think* I have addressed everything in the attached patch. >> <0001-Add-C-2a-synchronization-support.patch> >> Jonathan Wakely writes: >> >>> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote: >>>> * Note, this patch supersedes my previous atomic wait and semaphore >>>> patches. >>>> >>>> Add support for - >>>> atomic wait/notify_one/notify_all >>>> counting_semaphore >>>> binary_semaphore >>>> latch >>>> >>>> * include/Makefile.am (bits_headers): Add new header. >>>> * include/Makefile.in: Regenerate. >>>> * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. >>> >>> Should be two colons before wait. >>> >>>> diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in >>>> index eb437ad8d8d..e73ff8b3e64 100644 >>>> --- a/libstdc++-v3/include/Makefile.in >>>> +++ b/libstdc++-v3/include/Makefile.in >>> >>> Generated files don't need to be in the patch. >>> >>>> diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >>>> index 87fe0bd6000..b2cec0f1722 100644 >>>> --- a/libstdc++-v3/include/bits/atomic_base.h >>>> +++ b/libstdc++-v3/include/bits/atomic_base.h >>>> @@ -37,6 +37,11 @@ >>>> #include <bits/atomic_lockfree_defines.h> >>>> #include <bits/move.h> >>>> >>>> +#if __cplusplus > 201703L >>>> +#include <bits/atomic_wait.h> >>>> +#include <iostream> >>> >>> <iostream> shouldn't be here (it adds runtime cost, as well as >>> compile-time). >>> >>>> @@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> __cmpexch_failure_order(__m)); >>>> } >>>> >>>> +#if __cplusplus > 201703L >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept >>> >>> Please format everything to <= 80 columns (ideally < 80). >>> >>>> + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >>>> + { >>>> + __atomic_wait(&_M_p, __old, >>> >>> This should be qualified to prevent ADL. >>> >>>> + [__m, this, __old]() >>>> + { return this->load(__m) != __old; }); >>>> + } >>>> + >>>> + // TODO add const volatile overload >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + notify_one() const noexcept >>>> + { __atomic_notify(&_M_p, false); } >>> >>> Qualify to prevent ADL here too, and all similar calls. >>> >>>> +#if __cplusplus > 201703L >>>> + template<typename _Tp> >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept >>>> + { >>>> + __atomic_wait(__ptr, *std::__addressof(__old), >>> >>> Can't this just be __old instead of *std::__addressof(__old) ? >>> >>>> + [=]() >>>> + { return load(__ptr, __m) == *std::__addressof(__old); }); >>> >>> Same here? >>> >>>> diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >>>> new file mode 100644 >>>> index 00000000000..10f0fe50ed9 >>>> --- /dev/null >>>> +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >>>> @@ -0,0 +1,270 @@ >>>> +// -*- C++ -*- header. >>>> + >>>> +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >>>> +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >>>> + >>>> +#pragma GCC system_header >>>> + >>>> +#include <bits/c++config.h> >>>> +#include <bits/functional_hash.h> >>>> +#include <bits/atomic_wait.h> >>>> + >>>> +#include <chrono> >>>> + >>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>>> +#include <sys/time.h> >>>> +#endif >>>> + >>>> +namespace std _GLIBCXX_VISIBILITY(default) >>>> +{ >>>> + _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> + enum class __atomic_wait_status { __no_timeout, __timeout }; >>> >>> Blank line before and after this enum definition please. >>> >>>> + namespace __detail >>>> + { >>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>>> + enum >>>> + { >>>> + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, >>>> + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, >>>> + __futex_bitset_match_any = 0xffffffff >>>> + }; >>>> + >>>> + using __platform_wait_clock_t = chrono::steady_clock; >>> >>> Blank line after this using-decl please. >>> >>>> + template<typename _Duration> >>>> + __atomic_wait_status >>>> + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, >>>> + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept >>>> + { >>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>>> + >>> >>> Eventually we'll want to move the rest of this function (which doesn't >>> depend on the template argument) into the compiled library, but it's >>> better to be header-only for now. >>> >>>> + struct timespec __rt = >>>> + { >>>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>>> + static_cast<long>(__ns.count()) >>>> + }; >>>> + >>>> + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, >>>> + nullptr, __futex_bitset_match_any); >>>> + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >>>> + std::terminate(); >>>> + return (__platform_wait_clock_t::now() < __atime) >>>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>>> + } >>>> + >>>> + template<typename _Clock, typename _Duration> >>>> + __atomic_wait_status >>>> + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >>>> + const chrono::time_point<_Clock, _Duration>& __atime) >>>> + { >>>> + if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value) >>> >>> This is C++20 so you can use is_same_v here, which uses the intrinsic >>> directly and avoids instantiating the is_same class template. >>> >>>> + { >>>> + return __platform_wait_until_impl(__addr, __val, __atime); >>>> + } >>>> + else >>>> + { >>>> + const typename _Clock::time_point __c_entry = _Clock::now(); >>>> + const __platform_wait_clock_t::time_point __s_entry = >>>> + __platform_wait_clock_t::now(); >>>> + const auto __delta = __atime - __c_entry; >>>> + const auto __s_atime = __s_entry + __delta; >>>> + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) >>>> + return __atomic_wait_status::__no_timeout; >>>> + >>>> + // 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. >>>> + if (_Clock::now() < __atime) >>>> + return __atomic_wait_status::__no_timeout; >>>> + return __atomic_wait_status::__timeout; >>>> + } >>>> + } >>>> +#endif >>>> + >>>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >>>> + template<typename _Duration> >>>> + __atomic_wait_status >>>> + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>>> + const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime) >>> >>> The std:: qualification here isn't needed (and doesn't help with >>> keeping the line below 80 cols). >>> >>>> + { >>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>>> + >>>> + __gthread_time_t __ts = >>>> + { >>>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>>> + static_cast<long>(__ns.count()) >>>> + }; >>>> + >>>> + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), >>>> + CLOCK_MONOTONIC, >>>> + &__ts); >>>> + return (chrono::steady_clock::now() < __atime) >>>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>>> + } >>>> +#endif >>>> + >>>> + template<typename _Duration> >>>> + __atomic_wait_status >>>> + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>>> + const chrono::time_point<chrono::system_clock, _Duration>& __atime) >>>> + { >>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>>> + >>>> + __gthread_time_t __ts = >>>> + { >>>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>>> + static_cast<long>(__ns.count()) >>>> + }; >>>> + >>>> + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), >>>> + &__ts); >>>> + return (chrono::system_clock::now() < __atime) >>>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >>>> + } >>>> + >>>> + // return true if timeout >>>> + template<typename _Clock, typename _Duration> >>>> + __atomic_wait_status >>>> + __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock, >>>> + const chrono::time_point<_Clock, _Duration>& __atime) >>>> + { >>>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >>>> + using __clock_t = chrono::steady_clock; >>>> +#else >>>> + using __clock_t = chrono::system_clock; >>>> +#endif >>>> + const typename _Clock::time_point __c_entry = _Clock::now(); >>>> + const __clock_t::time_point __s_entry = __clock_t::now(); >>>> + const auto __delta = __atime - __c_entry; >>>> + const auto __s_atime = __s_entry + __delta; >>>> + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) >>>> + return __atomic_wait_status::__no_timeout; >>>> + // 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. >>>> + if (_Clock::now() < __atime) >>>> + return __atomic_wait_status::__no_timeout; >>>> + return __atomic_wait_status::__timeout; >>>> + } >>>> + >>>> + struct __timed_waiters : __waiters >>>> + { >>>> + template<typename _Clock, typename _Duration> >>>> + __atomic_wait_status >>>> + _M_do_wait_until(int32_t __version, >>>> + const chrono::time_point<_Clock, _Duration>& __atime) >>>> + { >>>> + int32_t __cur = 0; >>>> + __waiters::__lock_t __l(_M_mtx); >>>> + while (__cur <= __version) >>>> + { >>>> + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) >>>> + return __atomic_wait_status::__timeout; >>>> + >>>> + int32_t __last = __cur; >>>> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >>>> + if (__cur < __last) >>>> + break; // break the loop if version overflows >>>> + } >>>> + return __atomic_wait_status::__no_timeout; >>>> + } >>>> + >>>> + static __timed_waiters& >>>> + _S_timed_for(void* __t) >>>> + { >>>> + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); >>>> + return (__timed_waiters&) __waiters::_S_for(__t); >>>> + } >>>> + }; >>>> + } // namespace __detail >>>> + >>>> + template<typename _Tp, typename _Pred, >>>> + typename _Clock, typename _Duration> >>>> + bool >>>> + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >>>> + const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>>> + { >>>> + using namespace __detail; >>>> + >>>> + if (__atomic_spin(__pred)) >>> >>> Qualify to prevent ADL. >>> >>>> + return true; >>>> + >>>> + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >>>> + auto __version = __w._M_enter_wait(); >>>> + do >>>> + { >>>> + __atomic_wait_status __res; >>>> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >>>> + { >>>> + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old, >>>> + __atime); >>>> + } >>>> + else >>>> + { >>>> + __res = __w._M_do_wait_until(__version, __atime); >>>> + } >>>> + if (__res == __atomic_wait_status::__timeout) >>>> + return false; >>>> + } >>>> + while (!__pred() && __atime < _Clock::now()); >>>> + __w._M_leave_wait(); >>>> + >>>> + // if timed out, return false >>>> + return (_Clock::now() < __atime); >>>> + } >>>> + >>>> + template<typename _Tp, typename _Pred, >>>> + typename _Rep, typename _Period> >>>> + bool >>>> + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, >>>> + const chrono::duration<_Rep, _Period>& __rtime) noexcept >>>> + { >>>> + using namespace __detail; >>>> + >>>> + if (__atomic_spin(__pred)) >>>> + return true; >>>> + >>>> + if (!__rtime.count()) >>>> + return false; // no rtime supplied, and spin did not acquire >>>> + >>>> + using __dur = chrono::steady_clock::duration; >>>> + auto __reltime = chrono::duration_cast<__dur>(__rtime); >>>> + if (__reltime < __rtime) >>>> + ++__reltime; >>>> + >>>> + >>>> + return __atomic_wait_until(__addr, __old, std::move(__pred), >>>> + chrono::steady_clock::now() + __reltime); >>>> + } >>>> +_GLIBCXX_END_NAMESPACE_VERSION >>>> +} // namespace std >>>> +#endif >>>> diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >>>> new file mode 100644 >>>> index 00000000000..32070a54f40 >>>> --- /dev/null >>>> +++ b/libstdc++-v3/include/bits/atomic_wait.h >>>> @@ -0,0 +1,280 @@ >>>> +// -*- C++ -*- header. >>>> + >>>> +// Copyright (C) 2020 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 >>>> + >>>> +#pragma GCC system_header >>>> + >>>> +#include <bits/c++config.h> >>>> +#include <bits/functional_hash.h> >>>> +#include <bits/gthr.h> >>>> +#include <bits/std_mutex.h> >>>> +#include <bits/unique_lock.h> >>>> + >>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>>> +#include <climits> >>>> +#include <unistd.h> >>>> +#include <syscall.h> >>>> +#endif >>>> + >>>> +#define _GLIBCXX_SPIN_COUNT_1 16 >>>> +#define _GLIBCXX_SPIN_COUNT_2 12 >>>> + >>>> +// TODO get this from Autoconf >>>> +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >>>> + >>>> +namespace std _GLIBCXX_VISIBILITY(default) >>>> +{ >>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> + namespace __detail >>>> + { >>>> + using __platform_wait_t = int; >>>> + >>>> + template<class _Tp> >>> >>> This should be typename not class. >>> >>>> + struct __platform_wait_uses_type >>>> + { >>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>>> + enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, >>> >>> This should be remove_cv_t. >>> >>>> + __platform_wait_t>::value }; >>>> +#else >>>> + enum { __value = std::false_type::value }; >>>> +#endif >>> >>> There's no need to use the C++03 enum hack here, it should just derive >>> from true_type or false_type. >>> >>> template<typename _Tp> >>> struct __platform_wait_uses_type >>> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> : is_same<std::remove_cv_t<_Tp>, __platform_wait_t> >>> #else >>> : false_type >>> #endif >>> { }; >>> >>> Or better yet, just use a variable template: >>> >>> template<typename _Tp> >>> inline constexpr bool __platform_wait_uses_type >>> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>> = is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>; >>> #else >>> = false; >>> #endif >>> >>> >>> >>>> + }; >>>> + >>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>>> + enum >>>> + { >>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >>>> + __futex_private_flag = 128, >>>> +#else >>>> + __futex_private_flag = 0, >>>> +#endif >>>> + __futex_wait = 0, >>>> + __futex_wake = 1, >>>> + __futex_wait_bitset = 9, >>>> + __futex_wake_bitset = 10, >>>> + __futex_wait_private = __futex_wait | __futex_private_flag, >>>> + __futex_wake_private = __futex_wake | __futex_private_flag >>>> + }; >>>> + >>>> + void >>>> + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept >>>> + { >>>> + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); >>>> + if (__e && !(errno == EINTR || errno == EAGAIN)) >>>> + std::terminate(); >>>> + } >>>> + >>>> + void >>>> + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept >>>> + { >>>> + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); >>>> + } >>>> +#endif >>>> + >>>> + struct alignas(64) __waiters >>> >>> Isn't alignas(64) already implied by the first data member? >>> >>>> + { >>>> + int32_t alignas(64) _M_ver = 0; >>>> + int32_t alignas(64) _M_wait = 0; >>>> + >>>> + // TODO make this used only where we don't have futexes >>> >>> Don't we always need these even with futexes, for the types that don't >>> use a futex? >>> >>>> + using __lock_t = std::unique_lock<std::mutex>; >>>> + mutable __lock_t::mutex_type _M_mtx; >>>> + >>>> +#ifdef __GTHREAD_COND_INIT >>>> + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >>>> + __waiters() noexcept = default; >>> >>> If we moved std::condition_variable into its own header (or >>> <bits/std_mutex.h>, could we reuse that here instead of using >>> __gthread_cond_t directly? >>> >>>> +#else >>>> + mutable __gthread_cond_t _M_cv; >>>> + __waiters() noexcept >>>> + { >>>> + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); >>>> + } >>>> +#endif >>>> + >>>> + int32_t >>>> + _M_enter_wait() noexcept >>>> + { >>>> + int32_t __res; >>>> + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); >>>> + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); >>>> + return __res; >>>> + } >>>> + >>>> + void >>>> + _M_leave_wait() noexcept >>>> + { >>>> + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); >>>> + } >>>> + >>>> + void >>>> + _M_do_wait(int32_t __version) const noexcept >>>> + { >>>> + int32_t __cur = 0; >>>> + while (__cur <= __version) >>>> + { >>>> + __waiters::__lock_t __l(_M_mtx); >>>> + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); >>>> + if (__e) >>>> + std::terminate(); >>>> + int32_t __last = __cur; >>>> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >>>> + if (__cur < __last) >>>> + break; // break the loop if version overflows >>>> + } >>>> + } >>>> + >>>> + int32_t >>>> + _M_waiting() const noexcept >>>> + { >>>> + int32_t __res; >>>> + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >>>> + return __res; >>>> + } >>>> + >>>> + void >>>> + _M_notify(bool __all) noexcept >>>> + { >>>> + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >>>> + auto __e = __gthread_cond_broadcast(&_M_cv); >>>> + if (__e) >>>> + __throw_system_error(__e); >>>> + } >>>> + >>>> + static __waiters& >>>> + _S_for(void* __t) >>>> + { >>>> + const unsigned char __mask = 0xf; >>>> + static __waiters __w[__mask + 1]; >>>> + >>>> + auto __key = _Hash_impl::hash(__t) & __mask; >>>> + return __w[__key]; >>>> + } >>>> + }; >>>> + >>>> + struct __waiter >>>> + { >>>> + __waiters& _M_w; >>>> + int32_t _M_version; >>>> + >>>> + template<typename _Tp> >>>> + __waiter(const _Tp* __addr) noexcept >>>> + : _M_w(__waiters::_S_for((void*) __addr)) >>>> + , _M_version(_M_w._M_enter_wait()) >>>> + { } >>>> + >>>> + ~__waiter() >>>> + { _M_w._M_leave_wait(); } >>>> + >>>> + void _M_do_wait() noexcept >>>> + { _M_w._M_do_wait(_M_version); } >>>> + }; >>>> + >>>> + void >>>> + __thread_relax() noexcept >>>> + { >>>> +#if defined __i386__ || defined __x86_64__ >>>> + __builtin_ia32_pause(); >>>> +#elif defined _GLIBCXX_USE_SCHED_YIELD >>>> + __gthread_yield(); >>>> +#endif >>>> + } >>>> + >>>> + void >>>> + __thread_yield() noexcept >>>> + { >>>> +#if defined _GLIBCXX_USE_SCHED_YIELD >>>> + __gthread_yield(); >>>> +#endif >>>> + } >>>> + >>>> + } // namespace __detail >>>> + >>>> + template<class _Pred> >>> >>> s/class/template/ >>> >>>> + bool >>>> + __atomic_spin(_Pred __pred) noexcept >>>> + { >>>> + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >>>> + { >>>> + if (__pred()) >>>> + return true; >>>> + >>>> + if (__i < _GLIBCXX_SPIN_COUNT_2) >>>> + __detail::__thread_relax(); >>>> + else >>>> + __detail::__thread_yield(); >>>> + } >>>> + return false; >>>> + } >>>> + >>>> + template<class _Tp, class _Pred> >>> >>> s/class/template/ >>> >>>> + void >>>> + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >>>> + { >>>> + using namespace __detail; >>>> + if (__atomic_spin(__pred)) >>>> + return; >>>> + >>>> + __waiter __w(__addr); >>>> + while (!__pred()) >>>> + { >>>> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >>>> + { >>>> + __platform_wait((__platform_wait_t*)(void*) __addr, __old); >>>> + } >>>> + else >>>> + { >>>> + // TODO support timed backoff when this can be moved into the lib >>>> + __w._M_do_wait(); >>>> + } >>>> + } >>>> + } >>>> + >>>> + template<class _Tp> >>> >>> s/class/template/ >>> >>>> + void >>>> + __atomic_notify(const _Tp* __addr, bool __all) noexcept >>>> + { >>>> + using namespace __detail; >>>> + auto& __w = __waiters::_S_for((void*)__addr); >>>> + if (!__w._M_waiting()) >>> >>> When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting() >>> ever be true? Won't this always return before notifying? >>> >>> Is there meant to be a __waiter constructed here? >>> >>>> + return; >>>> + >>>> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >>>> + { >>>> + __platform_notify((__platform_wait_t*)(void*) __addr, __all); >>>> + } >>>> + else >>>> + { >>>> + __w._M_notify(__all); >>>> + } >>>> + } >>>> +_GLIBCXX_END_NAMESPACE_VERSION >>>> +} // namespace std >>>> +#endif >>>> diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >>>> new file mode 100644 >>>> index 00000000000..b3c83bbc70b >>>> --- /dev/null >>>> +++ b/libstdc++-v3/include/bits/semaphore_base.h >>>> @@ -0,0 +1,270 @@ >>>> +// -*- C++ -*- header. >>>> + >>>> +// Copyright (C) 2020 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/semaphore.h >>> >>> Should be bits/semaphore_base.h >>> >>>> + * This is an internal header file, included by other library headers. >>>> + * Do not attempt to use it directly. @headername{atomic} >>> >>> Should be @headername{semaphore} >>> >>>> + */ >>>> + >>>> +#ifndef _GLIBCXX_SEMAPHORE_BASE_H >>>> +#define _GLIBCXX_SEMAPHORE_BASE_H 1 >>>> + >>>> +#pragma GCC system_header >>>> + >>>> +#include <bits/c++config.h> >>>> +#include <bits/atomic_base.h> >>>> +#include <bits/atomic_timed_wait.h> >>>> + >>>> +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) >>>> +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >>>> +#include <semaphore.h> >>>> +#endif >>>> + >>>> +#include <chrono> >>>> +#include <type_traits> >>>> +#include <limits> >>> >>> <ext/numeric_traits.h> is much smaller than <limits> and should be >>> used for limits of integer types. (I recently added >>> <bits/int_limits.h> too but that was a mistake that I need to fix). >>> >>> >>>> +namespace std _GLIBCXX_VISIBILITY(default) >>>> +{ >>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> + >>>> +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >>>> + template<ptrdiff_t __least_max_t> >>> >>> __least_max_t isn't a type so shouldn't have the _t suffix. >>> >>>> + struct __platform_semaphore >>>> + { >>>> + using __clock_t = chrono::system_clock; >>>> + >>>> + __platform_semaphore(ptrdiff_t __count) noexcept >>> >>> Should this constructor be explicit? >>> >>>> + { >>>> + static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX"); >>> >>> Our static_assert messages should state the positive condition, not >>> the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX", >>> which is what the real condition is anyway, so you might as well omit >>> the string literal. >>> >>>> + auto __e = sem_init(&_M_semaphore, 0, __count); >>>> + if (__e) >>>> + std::terminate(); >>>> + } >>>> + >>>> + ~__platform_semaphore() >>>> + { >>>> + auto __e = sem_destroy(&_M_semaphore); >>>> + if (__e) >>>> + std::terminate(); >>>> + } >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + acquire() noexcept >>>> + { >>>> + auto __err = sem_wait(&_M_semaphore); >>>> + if (__err) >>>> + std::terminate(); >>>> + } >>>> + >>>> + template<typename _Duration> >>>> + _GLIBCXX_ALWAYS_INLINE bool >>> >>> Do we really need this to be always_inline? >>> >>>> + __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); >>>> + >>>> + struct timespec __ts = >>>> + { >>>> + static_cast<std::time_t>(__s.time_since_epoch().count()), >>>> + static_cast<long>(__ns.count()) >>>> + }; >>>> + >>>> + auto __err = sem_timedwait(&_M_semaphore, &__ts); >>>> + if (__err && (errno == ETIMEDOUT)) >>>> + return false; >>>> + else if (__err) >>>> + std::terminate(); >>>> + return true; >>>> + } >>>> + >>>> + template<typename _Clock, typename _Duration> >>>> + _GLIBCXX_ALWAYS_INLINE bool >>> >>> always_inline? >>> >>>> + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>>> + { >>>> + if constexpr (std::is_same<__clock_t, _Clock>::value) >>> >>> is_same_v >>> >>>> + { >>>> + return __try_acquire_until_impl(__atime); >>>> + } >>>> + else >>>> + { >>>> + const typename _Clock::time_point __c_entry = _Clock::now(); >>>> + const __clock_t __s_entry = __clock_t::now(); >>>> + const auto __delta = __atime - __c_entry; >>>> + const auto __s_atime = __s_entry + __delta; >>>> + if (__try_acquire_until_impl(__s_atime)) >>>> + return true; >>>> + >>>> + // 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 >>>> + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>>> + { return try_acquire_until(__clock_t::now() + __rtime); } >>>> + >>>> + template<typename _Clock, typename _Duration> >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + release(ptrdiff_t __update) noexcept >>>> + { >>>> + do >>>> + { >>>> + auto __err = sem_post(&_M_semaphore); >>>> + if (__err) >>>> + std::terminate(); >>>> + } while (--__update); >>>> + } >>>> + >>>> + private: >>>> + sem_t _M_semaphore; >>>> + }; >>>> +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE >>>> + >>>> + template<typename _Tp> >>>> + struct __atomic_semaphore >>>> + { >>>> + static constexpr size_t _S_alignment = __alignof__(_Tp); >>>> + >>>> + __atomic_semaphore(_Tp __count) >>> >>> Should this be explicit? >>> >>>> + : _M_a(__count) >>>> + { } >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + acquire() noexcept >>>> + { >>>> + auto const __pred = [this] >>>> + { >>>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>>> + if (__old == 0) >>>> + return false; >>>> + return __atomic_impl::compare_exchange_strong(&this->_M_a, >>>> + __old, __old - 1, >>>> + memory_order::acquire, >>>> + memory_order::release); >>>> + }; >>>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>>> + __atomic_wait(&_M_a, __old, __pred); >>>> + } >>>> + >>>> + bool >>>> + try_acquire() noexcept >>>> + { >>>> + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); >>>> + if (__old == 0) >>>> + return false; >>>> + >>>> + return __atomic_spin([this, &__old] >>>> + { >>>> + return __atomic_impl::compare_exchange_weak(&this->_M_a, >>>> + __old, __old - 1, >>>> + memory_order::acquire, >>>> + memory_order::release); >>>> + }); >>>> + } >>>> + >>>> + template<typename _Clock, typename _Duration> >>>> + _GLIBCXX_ALWAYS_INLINE bool >>>> + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >>>> + { >>>> + auto const __pred = [this] >>>> + { >>>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>>> + if (__old == 0) >>>> + return false; >>>> + return __atomic_impl::compare_exchange_strong(&this->_M_a, >>>> + __old, __old - 1, >>>> + memory_order::acquire, >>>> + memory_order::release); >>>> + }; >>>> + >>>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>>> + return __atomic_wait_until(&_M_a, __old, __pred, __atime); >>>> + } >>>> + >>>> + template<typename _Rep, typename _Period> >>>> + _GLIBCXX_ALWAYS_INLINE bool >>>> + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>>> + { >>>> + auto const __pred = [this] >>>> + { >>>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire); >>>> + if (__old == 0) >>>> + return false; >>>> + return __atomic_impl::compare_exchange_strong(&this->_M_a, >>>> + __old, __old - 1, >>>> + memory_order::acquire, >>>> + memory_order::release); >>>> + }; >>>> + >>>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >>>> + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); >>>> + } >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + release(ptrdiff_t __update) noexcept >>>> + { >>>> + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) >>>> + return; >>>> + if (__update > 1) >>>> + __atomic_impl::notify_all(&_M_a); >>>> + else >>>> + __atomic_impl::notify_one(&_M_a); >>>> + } >>>> + >>>> + private: >>>> + alignas(_S_alignment) _Tp _M_a; >>> >>> Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no >>> need for the _S_alignment constant if it's only used in one place. >>> >>>> + }; >>>> + >>>> +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE >>>> + template<ptrdiff_t __least_max_t> >>> >>> Rename __least_max_t here too. >>> >>>> + using __semaphore_base = __platform_semaphore<__least_max_t>; >>>> +#else >>>> +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>>> + template<ptrdiff_t __least_max_t> >>>> + using __semaphore_base = std::conditional<(__least_max_t > 0 >>> >>> This should use conditional_t<> not conditional<>::type. >>> >>> The least-max_value can't be negative. If it's zero, can't we use a >>> futex or semaphore? So the '__least_max_t > 0' condition is wrong? >>> >>>> + && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()), >>> >>> Should that be <= rather than < ? >>> >>>> + __atomic_semaphore<__detail::__platform_wait_t>, >>>> + __atomic_semaphore<ptrdiff_t>>::type; >>>> + // __platform_semaphore >>>> +# else >>>> +# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >>> >>> Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid >>> an extra level of #if nesting. >>> >>>> + template<ptrdiff_t __least_max_t> >>>> + using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX), >>>> + __platform_semaphore<__least_max_t>, >>>> + __atomic_semaphore<ptrdiff_t>>::type; >>>> +# else >>>> + template<ptrdiff_t __least_max_t> >>>> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; >>>> +# endif >>>> +# endif >>>> +#endif >>>> + >>>> +_GLIBCXX_END_NAMESPACE_VERSION >>>> +} // namespace std >>>> + >>>> +#endif >>>> diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic >>>> index a455286a784..3f18774031d 100644 >>>> --- a/libstdc++-v3/include/std/atomic >>>> +++ b/libstdc++-v3/include/std/atomic >>>> @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> compare_exchange_strong(bool& __i1, bool __i2, >>>> memory_order __m = memory_order_seq_cst) volatile noexcept >>>> { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >>>> + >>>> +#if __cplusplus > 201703L >>>> + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept >>>> + { _M_base.wait(__old, __m); } >>>> + >>>> + // TODO add const volatile overload >>>> + >>>> + void notify_one() const noexcept >>>> + { _M_base.notify_one(); } >>>> + >>>> + void notify_all() const noexcept >>>> + { _M_base.notify_all(); } >>>> +#endif >>>> }; >>>> >>>> #if __cplusplus <= 201703L >>>> @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> memory_order __m = memory_order_seq_cst) volatile noexcept >>>> { return compare_exchange_strong(__e, __i, __m, >>>> __cmpexch_failure_order(__m)); } >>>> +#if __cplusplus > 201703L >>>> + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept >>>> + { _M_i.wait(__old, __m); } >>>> + >>>> + // TODO add const volatile overload >>>> + >>>> + void notify_one() const noexcept >>>> + { _M_i.notify_one(); } >>>> + >>>> + void notify_all() const noexcept >>>> + { _M_i.notify_all(); } >>>> +#endif >>>> + >>>> }; >>>> #undef _GLIBCXX20_INIT >>>> >>>> @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> __cmpexch_failure_order(__m)); >>>> } >>>> >>>> +#if __cplusplus > 201703L >>>> + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >>>> + { _M_b.wait(__old, __m); } >>>> + >>>> + // TODO add const volatile overload >>>> + >>>> + void notify_one() const noexcept >>>> + { _M_b.notify_one(); } >>>> + >>>> + void notify_all() const noexcept >>>> + { _M_b.notify_all(); } >>>> +#endif >>>> __pointer_type >>>> fetch_add(ptrdiff_t __d, >>>> memory_order __m = memory_order_seq_cst) noexcept >>>> @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> memory_order_seq_cst); >>>> } >>>> >>>> + >>>> +#if __cplusplus > 201703L >>>> + template<typename _Tp> >>>> + inline void atomic_wait(const atomic<_Tp>* __a, >>>> + typename std::atomic<_Tp>::value_type __old) noexcept >>>> + { __a->wait(__old); } >>>> + >>>> + template<typename _Tp> >>>> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, >>>> + typename std::atomic<_Tp>::value_type __old, >>>> + std::memory_order __m) noexcept >>>> + { __a->wait(__old, __m); } >>>> + >>>> + template<typename _Tp> >>>> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >>>> + { __a->notify_one(); } >>>> + >>>> + template<typename _Tp> >>>> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept >>>> + { __a->notify_all(); } >>>> + >>>> +#endif // C++2a >>>> + >>>> // Function templates for atomic_integral and atomic_pointer operations only. >>>> // Some operations (and, or, xor) are only available for atomic integrals, >>>> // which is implemented by taking a parameter of type __atomic_base<_ITp>*. >>>> diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch >>>> new file mode 100644 >>>> index 00000000000..0099877416e >>>> --- /dev/null >>>> +++ b/libstdc++-v3/include/std/latch >>>> @@ -0,0 +1,91 @@ >>>> +//<latch> -*- C++ -*- >>> >>> A space before <latch>. >>> >>>> + >>>> +// Copyright (C) 2020 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 include/latch >>>> + * This is a Standard C++ Library header. >>> >>> Align "This" with "@file" here. >>> >>>> + */ >>>> + >>>> +#ifndef _GLIBCXX_LATCH >>>> +#define _GLIBCXX_LATCH >>>> + >>>> +#pragma GCC system_header >>>> + >>>> +#if __cplusplus > 201703L >>>> +#define __cpp_lib_latch 201907L >>>> + >>>> +#include <bits/atomic_base.h> >>>> +#include <limits> >>> >>> Use <ext/numeric_traits.h> here too. >>> >>>> +namespace std _GLIBCXX_VISIBILITY(default) >>>> +{ >>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> + >>>> + class latch >>>> + { >>>> + static constexpr size_t _S_alignment = __alignof__(ptrdiff_t); >>>> + public: >>>> + static constexpr >>>> + _GLIBCXX_ALWAYS_INLINE ptrdiff_t >>>> + max() noexcept >>>> + { return numeric_limits<ptrdiff_t>::max(); } >>>> + >>>> + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } >>>> + >>>> + ~latch() = default; >>>> + latch(const latch&) = delete; >>>> + latch& operator=(const latch&) = delete; >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + count_down(ptrdiff_t __update = 1) >>>> + { >>>> + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); >>>> + if (__old == __update) >>>> + __atomic_impl::notify_all(&_M_a); >>>> + } >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE bool >>>> + try_wait() const noexcept >>>> + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + wait() const >>>> + { >>>> + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >>>> + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); >>>> + } >>>> + >>>> + _GLIBCXX_ALWAYS_INLINE void >>>> + arrive_and_wait(ptrdiff_t __update = 1) >>>> + { >>>> + count_down(); >>>> + wait(); >>>> + } >>>> + >>>> + private: >>>> + alignas(_S_alignment) ptrdiff_t _M_a; >>> >>> Just use __alignof__ directly here and get rid of _S_alignment? >>> >>>> + }; >>>> +_GLIBCXX_END_NAMESPACE_VERSION >>>> +} // namespace >>>> +#endif // __cplusplus > 201703L >>>> +#endif // _GLIBCXX_LATCH >>>> diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore >>>> new file mode 100644 >>>> index 00000000000..b51940b46ac >>>> --- /dev/null >>>> +++ b/libstdc++-v3/include/std/semaphore >>>> @@ -0,0 +1,81 @@ >>>> +//<semaphore> -*- C++ -*- >>> >>> A space before <semaphore>. >>> >>>> + >>>> +// Copyright (C) 2020 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 include/semaphore >>>> + * This is a Standard C++ Library header. >>> >>> Align "This" with "@file" here. >>> >>>> + */ >>>> + >>>> +#ifndef _GLIBCXX_SEMAPHORE >>>> +#define _GLIBCXX_SEMAPHORE >>>> + >>>> +#pragma GCC system_header >>>> + >>>> +#if __cplusplus > 201703L >>>> +#define __cpp_lib_semaphore 201907L >>>> +#include <bits/semaphore_base.h> >>>> + >>>> +namespace std _GLIBCXX_VISIBILITY(default) >>>> +{ >>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> + >>>> + template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()> >>>> + class counting_semaphore >>>> + { >>> >>> I don't see a static_assert making it ill-formed to use a negative >>> value for __least_max_value. Is that enforced somewhere else? >>> >>> The standard says it's ill-formed, so we should also have a _neg.cc >>> test checking that we reject it. >>> >>>> + __semaphore_base<__least_max_value> _M_sem; >>> >>> Blank line after this please. >>> >>>> + public: >>>> + explicit counting_semaphore(ptrdiff_t __desired) noexcept >>>> + : _M_sem(__desired) >>>> + { } >>>> + >>>> + ~counting_semaphore() = default; >>>> + >>>> + counting_semaphore(const counting_semaphore&) = delete; >>>> + counting_semaphore& operator=(const counting_semaphore&) = delete; >>>> + >>>> + static constexpr ptrdiff_t max() noexcept >>>> + { return __least_max_value; } >>>> + >>>> + void release(ptrdiff_t __update = 1) >>>> + { _M_sem.release(__update); } >>>> + >>>> + void acquire() >>>> + { _M_sem.acquire(); } >>>> + >>>> + bool try_acquire() noexcept >>>> + { return _M_sem.try_acquire(); } >>>> + >>>> + template<class _Rep, class _Period> >>> >>> s/class/template/ >>> >>>> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) >>>> + { return _M_sem.try_acquire_for(__rel_time); } >>>> + >>>> + template<class _Clock, class _Duration> >>> >>> s/class/template/ >>> >>>> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) >>>> + { return _M_sem.try_acquire_until(__abs_time); } >>>> + }; >>>> + >>>> + using binary_semaphore = std::counting_semaphore<1>; >>>> +_GLIBCXX_END_NAMESPACE_VERSION >>>> +} // namespace >>>> +#endif // __cplusplus > 201703L >>>> +#endif // _GLIBCXX_SEMAPHORE >>>> diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >>>> index c3a5bd26e63..390990282b0 100644 >>>> --- a/libstdc++-v3/include/std/version >>>> +++ b/libstdc++-v3/include/std/version >>>> @@ -188,6 +188,8 @@ >>>> #endif >>>> #define __cpp_lib_type_identity 201806L >>>> #define __cpp_lib_unwrap_ref 201811L >>>> +#define __cpp_lib_semaphore 201907L >>>> +#define __cpp_lib_latch 201907L >>> >>> These features aren't supported in a freestanding implementation, so >>> should be in the #if _GLIBCXX_HOSTED block (and the macros should be >>> in alphabetical order). >>> >>>> >>>> #if _GLIBCXX_HOSTED >>>> #undef __cpp_lib_array_constexpr >>>> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >>>> new file mode 100644 >>>> index 00000000000..1ced9d44b20 >>>> --- /dev/null >>>> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >>>> @@ -0,0 +1,103 @@ >>>> +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >>> >>> Use { dg-add-options libatomic } instead of adding -latomic -L... >> > ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] Add C++2a synchronization support 2020-05-23 22:52 ` Thomas Rodgers 2020-05-24 17:41 ` Thomas Rodgers @ 2020-06-06 0:29 ` Thomas Rodgers 2020-07-08 16:43 ` Jonathan Wakely 2020-08-03 14:09 ` Jonathan Wakely 1 sibling, 2 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-06-06 0:29 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers Add support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 161 +++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 282 +++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 291 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 272 ++++++++++++++++ libstdc++-v3/include/std/atomic | 61 ++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 86 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 +++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.h | 88 ++++++ .../atomic/wait_notify/integrals.cc | 56 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 28 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_futex.cc | 51 +++ .../30_threads/semaphore/try_acquire_posix.cc | 169 ++++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 27 files changed, 2291 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 80aeb3f8959..b3ac1a3365f 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 3b66b040976..68d9e7e3756 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -562,6 +565,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -823,6 +851,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -911,6 +963,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1164,6 +1242,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1301,6 +1396,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + private: _Tp* _M_ptr; }; @@ -1396,6 +1507,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1551,6 +1678,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1660,6 +1803,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..adef80aca61 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,282 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { __no_timeout, __timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, + __futex_bitset_match_any = 0xffffffff + }; + + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, + nullptr, __futex_bitset_match_any); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (std::is_same_v<__platform_wait_clock_t, _Clock>) + { + return __platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) + return __atomic_wait_status::__no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::__no_timeout + : __atomic_wait_status::__timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::__no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::__no_timeout; + return __atomic_wait_status::__timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) + return __atomic_wait_status::__timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::__no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return (__timed_waiters&) __waiters::_S_for(__t); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::__timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..92c1e2526ed --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,291 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + +#define _GLIBCXX_SPIN_COUNT_1 16 +#define _GLIBCXX_SPIN_COUNT_2 12 + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__numeric_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __futex_private_flag = 128, +#else + __futex_private_flag = 0, +#endif + __futex_wait = 0, + __futex_wake = 1, + __futex_wait_bitset = 9, + __futex_wake_bitset = 10, + __futex_wait_private = __futex_wait | __futex_private_flag, + __futex_wake_private = __futex_wake | __futex_private_flag + }; + + void + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + void + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept + { + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for((void*) __addr)) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) + { + if (__pred()) + return true; + + if (__i < _GLIBCXX_SPIN_COUNT_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..f0c4235d91c --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,272 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + + explicit __platform_semaphore(ptrdiff_t __count) noexcept + { + static_assert( __least_max_value <= SEM_VALUE_MAX, ""); + auto __e = sem_init(&_M_semaphore, 0, __count); + if (__e) + std::terminate(); + } + + ~__platform_semaphore() + { + auto __e = sem_destroy(&_M_semaphore); + if (__e) + std::terminate(); + } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto __err = sem_wait(&_M_semaphore); + if (__err) + std::terminate(); + } + + template<typename _Duration> + bool + __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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err) + std::terminate(); + return true; + } + + template<typename _Clock, typename _Duration> + bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + if constexpr (std::is_same<__clock_t, _Clock>::value) + { + return __try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__try_acquire_until_impl(__s_atime)) + return true; + + // 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 + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { return try_acquire_until(__clock_t::now() + __rtime); } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + do + { + auto __err = sem_post(&_M_semaphore); + if (__err) + std::terminate(); + } while (--__update); + } + + private: + sem_t _M_semaphore; + }; +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE + + template<typename _Tp> + struct __atomic_semaphore + { + explicit __atomic_semaphore(_Tp __count) + : _M_a(__count) + { } + + _GLIBCXX_ALWAYS_INLINE void + acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = __platform_semaphore<__least_max_value>; +#else +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= __detail::__platform_wait_max_value), + __atomic_semaphore<__detail::__platform_wait_t>, + __atomic_semaphore<ptrdiff_t>>; + +// __platform_semaphore +# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE + template<ptrdiff_t __least_max_value> + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= SEM_VALUE_MAX), + __platform_semaphore<__least_max_value>, + __atomic_semaphore<ptrdiff_t>>; +# else + template<ptrdiff_t __least_max_value> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>; +# endif +#endif + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index a455286a784..3f18774031d 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_i.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_i.notify_one(); } + + void notify_all() const noexcept + { _M_i.notify_all(); } +#endif + }; #undef _GLIBCXX20_INIT @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..aa5299d9fdd --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr + _GLIBCXX_ALWAYS_INLINE ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..90cf3244647 --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,86 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >=0, ""); + + __semaphore_base<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t max() noexcept + { return __least_max_value; } + + void release(ptrdiff_t __update = 1) + { _M_sem.release(__update); } + + void acquire() + { _M_sem.acquire(); } + + bool try_acquire() noexcept + { return _M_sem.try_acquire(); } + + template<class _Rep, class _Period> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return _M_sem.try_acquire_for(__rel_time); } + + template<class _Clock, class _Duration> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) + { return _M_sem.try_acquire_until(__abs_time); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index c6bde2cfbda..f09da3344f7 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -189,6 +189,8 @@ #endif #define __cpp_lib_type_identity 201806L #define __cpp_lib_unwrap_ref 201811L +#define __cpp_lib_semaphore 201907L +#define __cpp_lib_latch 201907L #if _GLIBCXX_HOSTED #undef __cpp_lib_array_constexpr diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..0da374ece87 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,88 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + VERIFY(check_atomic_wait_notify(a, b) == b); + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..2afd19a7d14 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..756727f33b3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..10bb500d261 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..b96b8a59c64 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..1ac9d261ca5 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..d38cef86cfc --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..965554a3c28 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc new file mode 100644 index 00000000000..5e05606e97f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +void test01() +{ + // the implementation optimizes for values of least_max_t that can fit + // in a futex, make sure we cover the case where least_max_t doesn't + auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max(); + std::counting_semaphore<least_max_t> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + test01(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..bf99fd3cf8f --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + // The implementation supports posix as an implementation strategy + // make sure we cover that case +#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test03() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +void test04() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test05() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} +#endif + +int main() +{ +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + test01(); + test02(); + test03(); + test04(); + test05(); +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..cc67c5c0bf0 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2019-2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-06-06 0:29 ` Thomas Rodgers @ 2020-07-08 16:43 ` Jonathan Wakely 2020-08-03 14:09 ` Jonathan Wakely 1 sibling, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-07-08 16:43 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 05/06/20 17:29 -0700, Thomas Rodgers wrote: >Add support for - > atomic wait/notify_one/notify_all > counting_semaphore > binary_semaphore > latch > > * include/Makefile.am (bits_headers): Add new header. > * include/Makefile.in: Regenerate. > * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. > (__atomic_base<_Itp>::notify_one): Likewise. > (__atomic_base<_Itp>::notify_all): Likewise. > (__atomic_base<_Ptp*>::wait): Likewise. > (__atomic_base<_Ptp*>::notify_one): Likewise. > (__atomic_base<_Ptp*>::notify_all): Likewise. > (__atomic_impl::wait): Likewise. > (__atomic_impl::notify_one): Likewise. > (__atomic_impl::notify_all): Likewise. > (__atomic_float<_Fp>::wait): Likewise. > (__atomic_float<_Fp>::notify_one): Likewise. > (__atomic_float<_Fp>::notify_all): Likewise. > (__atomic_ref<_Tp>::wait): Likewise. > (__atomic_ref<_Tp>::notify_one): Likewise. > (__atomic_ref<_Tp>::notify_all): Likewise. > (atomic_wait<_Tp>): Likewise. > (atomic_wait_explicit<_Tp>): Likewise. > (atomic_notify_one<_Tp>): Likewise. > (atomic_notify_all<_Tp>): Likewise. > * include/bits/atomic_wait.h: New file. > * include/bits/atomic_timed_wait.h: New file. > * include/bits/semaphore_base.h: New file. > * include/std/atomic (atomic<bool>::wait): Define. > (atomic<bool>::wait_one): Likewise. > (atomic<bool>::wait_all): Likewise. > (atomic<_Tp>::wait): Likewise. > (atomic<_Tp>::wait_one): Likewise. > (atomic<_Tp>::wait_all): Likewise. > (atomic<_Tp*>::wait): Likewise. > (atomic<_Tp*>::wait_one): Likewise. > (atomic<_Tp*>::wait_all): Likewise. > * include/std/latch: New file. > * include/std/semaphore: New file. > * include/std/version: Add __cpp_lib_semaphore and > __cpp_lib_latch defines. > * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. > * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. > * testsuite/30_thread/semaphore/1.cc: New test. > * testsuite/30_thread/semaphore/2.cc: Likewise. > * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. > * testsuite/30_thread/latch/1.cc: New test. > * testsuite/30_thread/latch/2.cc: New test. > * testsuite/30_thread/latch/3.cc: New test. >diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am >index 80aeb3f8959..b3ac1a3365f 100644 >--- a/libstdc++-v3/include/Makefile.am >+++ b/libstdc++-v3/include/Makefile.am >@@ -52,6 +52,7 @@ std_headers = \ > ${std_srcdir}/iostream \ > ${std_srcdir}/istream \ > ${std_srcdir}/iterator \ >+ ${std_srcdir}/latch\ Missing space before the backslash here. > ${std_srcdir}/limits \ > ${std_srcdir}/list \ > ${std_srcdir}/locale \ >--- a/libstdc++-v3/include/bits/atomic_base.h >+++ b/libstdc++-v3/include/bits/atomic_base.h >@@ -823,6 +851,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > int(__m1), int(__m2)); > } > >+#if __cplusplus > 201703L >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept This line should be < 80 cols. >+ { >@@ -911,6 +963,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > int(__success), int(__failure)); > } > >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept And this one. >@@ -1164,6 +1242,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(&_M_fp, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(&_M_fp); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(&_M_fp); } >+ >+ // TODO add const volatile overload Please add a newline after this comment. > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) noexcept >@@ -1301,6 +1396,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ The TODO comment seems to be missing here, and after some notify_all cases below. Please either add one after every non-volatile function that's missing a volatile overload, or just put one "TODO volatile overloads of wait and notify_{one,all}" comment in each class. >diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >new file mode 100644 >index 00000000000..adef80aca61 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >@@ -0,0 +1,282 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/atomic_wait.h> >+ >+#include <chrono> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <sys/time.h> >+#endif >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+ _GLIBCXX_BEGIN_NAMESPACE_VERSION No indentation for these BEGIN/END macros. >+ >+ enum class __atomic_wait_status { __no_timeout, __timeout }; It seems a shame to have yet another status macro when we already have cv_status and future_status which define enumerators with the same names, but I suppose it's a bit confusing to reuse them. Maybe you could just do: using __atomic_wait_status = cv_status; and then refer to __atomic_wait_status::no_timeout and __atomic_wait_status::timeout, what do you think? Even if it's a new distinct enum type, you could use no_timeout and timeout as the enumerator names, because those are reserved names anyway. >+ namespace __detail >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ enum I think unnamed enum types in headers cause problems for modules. >+ { >+ __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag, >+ __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag, >+ __futex_bitset_match_any = 0xffffffff >+ }; >+ >+ using __platform_wait_clock_t = chrono::steady_clock; >+ >+ template<typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val, >+ const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ struct timespec __rt = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt, >+ nullptr, __futex_bitset_match_any); >+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >+ std::terminate(); >+ return (__platform_wait_clock_t::now() < __atime) >+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >+ } >+ >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+ if constexpr (std::is_same_v<__platform_wait_clock_t, _Clock>) >+ { >+ return __platform_wait_until_impl(__addr, __val, __atime); Since this is calling a free function with arguments of program-defined types (the clock and duration), it needs to be qualified (whereas the is_same_v above doesn't need to be qualified, although that's harmless). >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __platform_wait_clock_t::time_point __s_entry = >+ __platform_wait_clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout) >+ return __atomic_wait_status::__no_timeout; >+ >+ // 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. >+ if (_Clock::now() < __atime) >+ return __atomic_wait_status::__no_timeout; >+ return __atomic_wait_status::__timeout; >+ } >+ } >+#endif >+ >+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >+ template<typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until_impl(__gthread_cond_t* __cv, >+ unique_lock<mutex>& __lock, >+ const chrono::time_point<chrono::steady_clock, _Duration>& __atime) >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ __gthread_time_t __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), >+ CLOCK_MONOTONIC, >+ &__ts); >+ return (chrono::steady_clock::now() < __atime) >+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout; >+ } >+#endif >+ >+ template<typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until_impl(__gthread_cond_t* __cv, >+ unique_lock<std::mutex>& __lock, >+ const chrono::time_point<chrono::system_clock, _Duration>& __atime) >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ __gthread_time_t __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), >+ &__ts); >+ return (chrono::system_clock::now() < __atime) >+ ? __atomic_wait_status::__no_timeout >+ : __atomic_wait_status::__timeout; >+ } >+ >+ // return true if timeout >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until(__gthread_cond_t* __cv, >+ unique_lock<std::mutex>& __lock, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >+ using __clock_t = chrono::steady_clock; >+#else >+ using __clock_t = chrono::system_clock; >+#endif >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t::time_point __s_entry = __clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (__cond_wait_until_impl(__cv, __lock, __s_atime)) __cond_wait_until_impl should be qualified. >+ return __atomic_wait_status::__no_timeout; >+ // 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. >+ if (_Clock::now() < __atime) >+ return __atomic_wait_status::__no_timeout; >+ return __atomic_wait_status::__timeout; >+ } >+ >+ struct __timed_waiters : __waiters >+ { >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ _M_do_wait_until(__platform_wait_t __version, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ return __platform_wait_until(&_M_ver, __version, __atime); >+#else >+ __platform_wait_t __cur = 0; >+ __waiters::__lock_t __l(_M_mtx); >+ while (__cur <= __version) >+ { >+ if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout) Qualify. >+ return __atomic_wait_status::__timeout; >+ >+ __platform_wait_t __last = __cur; >+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >+ if (__cur < __last) >+ break; // break the loop if version overflows >+ } >+ return __atomic_wait_status::__no_timeout; >+#endif >+ } >+ >+ static __timed_waiters& >+ _S_timed_for(void* __t) >+ { >+ static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); >+ return (__timed_waiters&) __waiters::_S_for(__t); I'd be more comfortable with a static_cast here. >+ } >+ }; >+ } // namespace __detail >+ >+ template<typename _Tp, typename _Pred, >+ typename _Clock, typename _Duration> >+ bool >+ __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ using namespace __detail; >+ >+ if (std::__atomic_spin(__pred)) >+ return true; >+ >+ auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >+ auto __version = __w._M_enter_wait(); >+ do >+ { >+ __atomic_wait_status __res; >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, >+ __old, >+ __atime); >+ } >+ else >+ { >+ __res = __w._M_do_wait_until(__version, __atime); >+ } >+ if (__res == __atomic_wait_status::__timeout) >+ return false; >+ } >+ while (!__pred() && __atime < _Clock::now()); >+ __w._M_leave_wait(); >+ >+ // if timed out, return false >+ return (_Clock::now() < __atime); >+ } >+ >+ template<typename _Tp, typename _Pred, >+ typename _Rep, typename _Period> >+ bool >+ __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { >+ using namespace __detail; >+ >+ if (std::__atomic_spin(__pred)) >+ return true; >+ >+ if (!__rtime.count()) >+ return false; // no rtime supplied, and spin did not acquire >+ >+ using __dur = chrono::steady_clock::duration; >+ auto __reltime = chrono::duration_cast<__dur>(__rtime); >+ if (__reltime < __rtime) >+ ++__reltime; >+ >+ >+ return __atomic_wait_until(__addr, __old, std::move(__pred), >+ chrono::steady_clock::now() + __reltime); >+ } >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+#endif >diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >new file mode 100644 >index 00000000000..92c1e2526ed >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_wait.h >@@ -0,0 +1,291 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/gthr.h> >+#include <bits/std_mutex.h> >+#include <bits/unique_lock.h> >+#include <ext/numeric_traits.h> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <climits> >+#include <unistd.h> >+#include <syscall.h> >+#endif >+ >+#define _GLIBCXX_SPIN_COUNT_1 16 >+#define _GLIBCXX_SPIN_COUNT_2 12 >+ Yuck, do these have to be macros? >+// TODO get this from Autoconf >+#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ namespace __detail >+ { >+ using __platform_wait_t = int; >+ >+ inline constexpr >+ auto __platform_wait_max_value = >+ __gnu_cxx::__numeric_traits<__platform_wait_t>::__max; You can usee the new __gnu_cxx::__int_traits alias here, since you know __platform_wait_t is an integer, not a floating-point type. That alias just saves instantiating __numeric_traits to figure out whether to use __numeric_traits_integer or __numeric_traits_floating. >+ >+ template<typename _Tp> >+ inline constexpr bool __platform_wait_uses_type >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; >+#else >+ = false; >+#endif >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ enum See earlier comment about unnamed enum types. >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >+ __futex_private_flag = 128, >+#else >+ __futex_private_flag = 0, >+#endif >+ __futex_wait = 0, >+ __futex_wake = 1, >+ __futex_wait_bitset = 9, >+ __futex_wake_bitset = 10, >+ __futex_wait_private = __futex_wait | __futex_private_flag, >+ __futex_wake_private = __futex_wake | __futex_private_flag >+ }; >+ >+ void >+ __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept >+ { >+ auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); >+ if (__e && !(errno == EINTR || errno == EAGAIN)) >+ std::terminate(); >+ } >+ >+ void >+ __platform_notify(__platform_wait_t* __addr, bool __all) noexcept >+ { >+ syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); >+ } >+#endif >+ >+ struct __waiters >+ { >+ __platform_wait_t alignas(64) _M_ver = 0; >+ __platform_wait_t alignas(64) _M_wait = 0; >+ >+#ifndef _GLIBCXX_HAVE_LINUX_FUTEX >+ using __lock_t = std::unique_lock<std::mutex>; >+ mutable __lock_t::mutex_type _M_mtx; >+ >+# ifdef __GTHREAD_COND_INIT >+ mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; Not being able to use std::condition_variable here makes me sad. >+ __waiters() noexcept = default; >+# else >+ mutable __gthread_cond_t _M_cv; >+ __waiters() noexcept >+ { >+ __GTHREAD_COND_INIT_FUNCTION(&_M_cond); >+ } >+# endif >+#endif >+ >+ __platform_wait_t >+ _M_enter_wait() noexcept >+ { >+ __platform_wait_t __res; >+ __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); >+ __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); >+ return __res; >+ } >+ >+ void >+ _M_leave_wait() noexcept >+ { >+ __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); >+ } >+ >+ void >+ _M_do_wait(__platform_wait_t __version) noexcept >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ __platform_wait(&_M_ver, __version); >+#else >+ __platform_wait_t __cur = 0; >+ while (__cur <= __version) >+ { >+ __waiters::__lock_t __l(_M_mtx); >+ auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); >+ if (__e) >+ std::terminate(); >+ __platform_wait_t __last = __cur; >+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >+ if (__cur < __last) >+ break; // break the loop if version overflows >+ } >+#endif >+ } >+ >+ __platform_wait_t >+ _M_waiting() const noexcept >+ { >+ __platform_wait_t __res; >+ __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >+ return __res; >+ } >+ >+ void >+ _M_notify(bool __all) noexcept >+ { >+ __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ __platform_notify(&_M_ver, __all); >+#else >+ auto __e = __gthread_cond_broadcast(&_M_cv); >+ if (__e) >+ __throw_system_error(__e); >+#endif >+ } >+ >+ static __waiters& >+ _S_for(void* __t) >+ { >+ const unsigned char __mask = 0xf; >+ static __waiters __w[__mask + 1]; >+ >+ auto __key = _Hash_impl::hash(__t) & __mask; >+ return __w[__key]; >+ } >+ }; >+ >+ struct __waiter >+ { >+ __waiters& _M_w; >+ __platform_wait_t _M_version; >+ >+ template<typename _Tp> >+ __waiter(const _Tp* __addr) noexcept >+ : _M_w(__waiters::_S_for((void*) __addr)) >+ , _M_version(_M_w._M_enter_wait()) >+ { } >+ >+ ~__waiter() >+ { _M_w._M_leave_wait(); } >+ >+ void _M_do_wait() noexcept >+ { _M_w._M_do_wait(_M_version); } >+ }; >+ >+ void >+ __thread_relax() noexcept >+ { >+#if defined __i386__ || defined __x86_64__ >+ __builtin_ia32_pause(); >+#elif defined _GLIBCXX_USE_SCHED_YIELD >+ __gthread_yield(); >+#endif Should this and the identical code in <stop_token> be consolidated somewhere? >+ } >+ >+ void >+ __thread_yield() noexcept >+ { >+#if defined _GLIBCXX_USE_SCHED_YIELD >+ __gthread_yield(); >+#endif >+ } >+ >+ } // namespace __detail >+ >+ template<typename _Pred> >+ bool >+ __atomic_spin(_Pred __pred) noexcept >+ { >+ for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >+ { >+ if (__pred()) >+ return true; >+ >+ if (__i < _GLIBCXX_SPIN_COUNT_2) >+ __detail::__thread_relax(); >+ else >+ __detail::__thread_yield(); >+ } >+ return false; >+ } >+ >+ template<typename _Tp, typename _Pred> >+ void >+ __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >+ { >+ using namespace __detail; >+ if (__atomic_spin(__pred)) >+ return; >+ >+ __waiter __w(__addr); >+ while (!__pred()) >+ { >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __platform_wait((__platform_wait_t*)(void*) __addr, __old); This has an implicit conversion from _To to __platform_wait_t, is that safe? Can we make it explicit with a static_cast? >+ } >+ else >+ { >+ // TODO support timed backoff when this can be moved into the lib >+ __w._M_do_wait(); >+ } >+ } >+ } >+ >+ template<typename _Tp> >+ void >+ __atomic_notify(const _Tp* __addr, bool __all) noexcept >+ { >+ using namespace __detail; >+ auto& __w = __waiters::_S_for((void*)__addr); >+ if (!__w._M_waiting()) >+ return; >+ >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __platform_notify((__platform_wait_t*)(void*) __addr, __all); >+ } >+ else >+ { >+ __w._M_notify(__all); >+ } >+ } >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+#endif >diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >new file mode 100644 >index 00000000000..f0c4235d91c >--- /dev/null >+++ b/libstdc++-v3/include/bits/semaphore_base.h I'll continue reviewing from here ASAP ... ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-06-06 0:29 ` Thomas Rodgers 2020-07-08 16:43 ` Jonathan Wakely @ 2020-08-03 14:09 ` Jonathan Wakely 2020-08-03 20:19 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-08-03 14:09 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 05/06/20 17:29 -0700, Thomas Rodgers wrote: >diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >new file mode 100644 >index 00000000000..f0c4235d91c >--- /dev/null >+++ b/libstdc++-v3/include/bits/semaphore_base.h >@@ -0,0 +1,272 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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/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 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/atomic_base.h> >+#include <bits/atomic_timed_wait.h> >+ >+#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) >+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >+#include <semaphore.h> >+#endif >+ >+#include <chrono> >+#include <type_traits> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ template<ptrdiff_t __least_max_value> >+ struct __platform_semaphore >+ { I think we want to delete the copy constructor and copy assignment operator for this type and __atomic_semaphore. >+ using __clock_t = chrono::system_clock; >+ >+ explicit __platform_semaphore(ptrdiff_t __count) noexcept >+ { >+ static_assert( __least_max_value <= SEM_VALUE_MAX, ""); >+ auto __e = sem_init(&_M_semaphore, 0, __count); >+ if (__e) >+ std::terminate(); I don't think we should bother checking the return value. The failure conditions are EINVAL if __count > SEM_VALUE_MAX, and ENOSYS if the second argument is non-zero. Both conditions should be impossible. >+ } >+ >+ ~__platform_semaphore() >+ { >+ auto __e = sem_destroy(&_M_semaphore); >+ if (__e) >+ std::terminate(); Likewise here. The only error condition is EINVAL, which is impossible if _M_semaphore is a valid semaphore, which the constructor already ensured. If somehow _M_semaphore is invalid it means the user broke something (e.g. by scribbling over *this) and we don't need to detect and handle that case. >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ acquire() noexcept >+ { >+ auto __err = sem_wait(&_M_semaphore); >+ if (__err) >+ std::terminate(); What about the EINTR case where the wait is interrupted by a signal? I think that should retry, but it shouldn't terminate. >+ template<typename _Duration> >+ bool >+ __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); >+ >+ struct timespec __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ auto __err = sem_timedwait(&_M_semaphore, &__ts); >+ if (__err && (errno == ETIMEDOUT)) >+ return false; >+ else if (__err) >+ std::terminate(); I think this needs to handle both EINTR and EINVAL: EINVAL The value of abs_timeout.tv_nsecs is less than 0, or greater than or equal to 1000 million. Alternatively, just return immediately without waiting for a time before the epoch, i.e. __abs_time < time_point<__clock_t>{} Otherwise time_point(-20ns) will cause EINVAL and terminate. >+ return true; >+ } >+ >+ template<typename _Clock, typename _Duration> >+ bool >+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ if constexpr (std::is_same<__clock_t, _Clock>::value) >+ { >+ return __try_acquire_until_impl(__atime); >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t __s_entry = __clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (__try_acquire_until_impl(__s_atime)) >+ return true; >+ >+ // 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 >+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { return try_acquire_until(__clock_t::now() + __rtime); } >+ >+ template<typename _Clock, typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE void >+ release(ptrdiff_t __update) noexcept >+ { >+ do >+ { >+ auto __err = sem_post(&_M_semaphore); >+ if (__err) >+ std::terminate(); >+ } while (--__update); >+ } >+ >+ private: >+ sem_t _M_semaphore; >+ }; >+#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ >+ template<typename _Tp> >+ struct __atomic_semaphore >+ { Do we want a static_assert(is_integral_v<_Tp>) or similar? >+ explicit __atomic_semaphore(_Tp __count) Add noexcept? >+ : _M_a(__count) >+ { } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ acquire() noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, >+ memory_order::acquire); Would it be simpler to just use a local atomic_ref to manipulate _M_a here, and in the other members performing atomic ops on that member? >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ __atomic_wait(&_M_a, __old, __pred); Strictly speaking, this __atomic_wait would violate the "all accesses to that object shall exclusively occur through those atomic_ref instances" rule. But we know that it's actually OK because __atomic_wait is only performing atomic ops on the same location. >+ } >+ >+ bool >+ try_acquire() noexcept >+ { >+ auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ if (__old == 0) >+ return false; >+ >+ return __atomic_spin([this, &__old] >+ { >+ return __atomic_impl::compare_exchange_weak(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }); >+ } >+ >+ template<typename _Clock, typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, >+ memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ return __atomic_wait_until(&_M_a, __old, __pred, __atime); >+ } >+ >+ template<typename _Rep, typename _Period> >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, >+ memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ return __atomic_wait_for(&_M_a, __old, __pred, __rtime); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ release(ptrdiff_t __update) noexcept >+ { >+ if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) >+ return; >+ if (__update > 1) >+ __atomic_impl::notify_all(&_M_a); >+ else >+ __atomic_impl::notify_one(&_M_a); >+ } >+ >+ private: >+ alignas(__alignof__(_Tp)) _Tp _M_a; >+ }; >+ >+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE What is this case for? This macro seems to only exist for use by a single testcase. Is it supposed to be something users can set? If so, it needs to be documented as ABI-breaking and as implying that counting_semaphore cannot be use with counts higher than SEM_VALUE_MAX. >+ template<ptrdiff_t __least_max_value> >+ using __semaphore_base = __platform_semaphore<__least_max_value>; >+#else >+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ template<ptrdiff_t __least_max_value> >+ using __semaphore_base = conditional_t<( >+ __least_max_value >= 0 This condition is redundant, we already know that counting_semaphore has checked the value is non-negative. >+ && __least_max_value <= __detail::__platform_wait_max_value), >+ __atomic_semaphore<__detail::__platform_wait_t>, >+ __atomic_semaphore<ptrdiff_t>>; >+ >+// __platform_semaphore >+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ template<ptrdiff_t __least_max_value> >+ using __semaphore_base = conditional_t<( >+ __least_max_value >= 0 Redundant condition again. >+ && __least_max_value <= SEM_VALUE_MAX), >+ __platform_semaphore<__least_max_value>, >+ __atomic_semaphore<ptrdiff_t>>; >+# else >+ template<ptrdiff_t __least_max_value> >+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>; >+# endif >+#endif >+ >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+ >+#endif >diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic >index a455286a784..3f18774031d 100644 >--- a/libstdc++-v3/include/std/atomic >+++ b/libstdc++-v3/include/std/atomic >@@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > compare_exchange_strong(bool& __i1, bool __i2, > memory_order __m = memory_order_seq_cst) volatile noexcept > { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >+ >+#if __cplusplus > 201703L >+ void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { _M_base.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_base.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_base.notify_all(); } >+#endif > }; > > #if __cplusplus <= 201703L >@@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > memory_order __m = memory_order_seq_cst) volatile noexcept > { return compare_exchange_strong(__e, __i, __m, > __cmpexch_failure_order(__m)); } >+#if __cplusplus > 201703L >+ void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept >+ { _M_i.wait(__old, __m); } This can't be right. _M_i is of type _Tp in std::atomic<_Tp>, it doesn't have a wait member function. If this compiles it suggests there are no tests for these members on the primary template, e.g. something like: void test01() { struct S { int i; }; std:::atomic<S> s; s.wait(); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_i.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_i.notify_all(); } >+#endif >+ > }; > #undef _GLIBCXX20_INIT > >@@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__m)); > } > >+#if __cplusplus > 201703L >+ void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >+ { _M_b.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_b.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_b.notify_all(); } >+#endif > __pointer_type > fetch_add(ptrdiff_t __d, > memory_order __m = memory_order_seq_cst) noexcept >@@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > memory_order_seq_cst); > } > >+ >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ inline void atomic_wait(const atomic<_Tp>* __a, Newline after the return type. >+ typename std::atomic<_Tp>::value_type __old) noexcept >+ { __a->wait(__old); } >+ >+ template<typename _Tp> >+ inline void atomic_wait_explicit(const atomic<_Tp>* __a, And here. >+ typename std::atomic<_Tp>::value_type __old, >+ std::memory_order __m) noexcept >+ { __a->wait(__old, __m); } >+ >+ template<typename _Tp> >+ inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >+ { __a->notify_one(); } >+ >+ template<typename _Tp> >+ inline void atomic_notify_all(atomic<_Tp>* __a) noexcept >+ { __a->notify_all(); } >+ >+#endif // C++2a >+ > // Function templates for atomic_integral and atomic_pointer operations only. > // Some operations (and, or, xor) are only available for atomic integrals, > // which is implemented by taking a parameter of type __atomic_base<_ITp>*. >diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch >new file mode 100644 >index 00000000000..aa5299d9fdd >--- /dev/null >+++ b/libstdc++-v3/include/std/latch >@@ -0,0 +1,90 @@ >+// <latch> -*- C++ -*- >+ >+// Copyright (C) 2020 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 include/latch >+ * This is a Standard C++ Library header. >+ */ >+ >+#ifndef _GLIBCXX_LATCH >+#define _GLIBCXX_LATCH >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_latch 201907L >+ >+#include <bits/atomic_base.h> >+#include <ext/numeric_traits.h> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ class latch >+ { >+ public: >+ static constexpr >+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t I don't think we need _GLIBCXX_ALWAYS_INLINE here. >+ max() noexcept >+ { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } >+ >+ constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { } Add noexcept. >+ >+ ~latch() = default; >+ latch(const latch&) = delete; >+ latch& operator=(const latch&) = delete; >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ count_down(ptrdiff_t __update = 1) >+ { >+ auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); It looks like this could use atomic_ref too, although there'd be less benefit here. >+ if (__old == __update) >+ __atomic_impl::notify_all(&_M_a); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_wait() const noexcept >+ { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ wait() const >+ { >+ auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ arrive_and_wait(ptrdiff_t __update = 1) >+ { >+ count_down(); >+ wait(); >+ } >+ >+ private: >+ alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; >+ }; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_LATCH >diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore >new file mode 100644 >index 00000000000..90cf3244647 >--- /dev/null >+++ b/libstdc++-v3/include/std/semaphore >@@ -0,0 +1,86 @@ >+// <semaphore> -*- C++ -*- >+ >+// Copyright (C) 2020 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 include/semaphore >+ * This is a Standard C++ Library header. >+ */ >+ >+#ifndef _GLIBCXX_SEMAPHORE >+#define _GLIBCXX_SEMAPHORE >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_semaphore 201907L >+#include <bits/semaphore_base.h> >+#include <ext/numeric_traits.h> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ template<ptrdiff_t __least_max_value = >+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> >+ class counting_semaphore >+ { >+ static_assert(__least_max_value >=0, ""); Add a space before the 0. Get rid of the empty string literal. >+ >+ __semaphore_base<__least_max_value> _M_sem; "base" seems a bit misleading here when it's a member not a base class. Would __semaphore_impl be better? >+ >+ public: >+ explicit counting_semaphore(ptrdiff_t __desired) noexcept >+ : _M_sem(__desired) >+ { } >+ >+ ~counting_semaphore() = default; >+ >+ counting_semaphore(const counting_semaphore&) = delete; >+ counting_semaphore& operator=(const counting_semaphore&) = delete; >+ >+ static constexpr ptrdiff_t max() noexcept >+ { return __least_max_value; } >+ >+ void release(ptrdiff_t __update = 1) We can add noexcept here, since we know neither of the __semaphore_base types will throw. If you envision alternative implementations in future, then it would be futureproof to use noexcept(noexcept(_M_sem.release(1))) >+ { _M_sem.release(__update); } >+ >+ void acquire() Same here. >+ { _M_sem.acquire(); } >+ >+ bool try_acquire() noexcept >+ { return _M_sem.try_acquire(); } >+ >+ template<class _Rep, class _Period> >+ bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) Newline after the return type (the line is too long otherwise). This one can potentially throw because of the duration arithmetic on _Rep objects. >+ { return _M_sem.try_acquire_for(__rel_time); } >+ >+ template<class _Clock, class _Duration> >+ bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) Newline after the return type (I think it's still going to be too long). >+ { return _M_sem.try_acquire_until(__abs_time); } >+ }; >+ >+ using binary_semaphore = std::counting_semaphore<1>; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_SEMAPHORE >diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >index c6bde2cfbda..f09da3344f7 100644 >--- a/libstdc++-v3/include/std/version >+++ b/libstdc++-v3/include/std/version >@@ -189,6 +189,8 @@ > #endif > #define __cpp_lib_type_identity 201806L > #define __cpp_lib_unwrap_ref 201811L >+#define __cpp_lib_semaphore 201907L >+#define __cpp_lib_latch 201907L You're adding these macros in the freestanding section, and these aren't freestanding headers. They need to be in the _GLIBCXX_HOSTED block, and the macros need to be in alphabetical order. > #if _GLIBCXX_HOSTED > #undef __cpp_lib_array_constexpr >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >new file mode 100644 >index 00000000000..1ced9d44b20 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >@@ -0,0 +1,103 @@ >+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <chrono> >+#include <type_traits> >+ >+#include <testsuite_hooks.h> >+ >+template<typename Tp> >+Tp check_wait_notify(Tp val1, Tp val2) >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ Tp aa = val1; >+ std::atomic_ref<Tp> a(aa); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(val1); >+ if (a.load() != val2) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ a.notify_one(); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp, >+ bool = std::is_integral_v<Tp> >+ || std::is_floating_point_v<Tp>> >+struct check; >+ >+template<typename Tp> >+struct check<Tp, true> >+{ >+ check() >+ { >+ Tp a = 0; >+ Tp b = 42; >+ VERIFY(check_wait_notify(a, b) == b); >+ } >+}; >+ >+template<typename Tp> >+struct check<Tp, false> >+{ >+ check(Tp b) >+ { >+ Tp a; >+ VERIFY(check_wait_notify(a, b) == b); >+ } >+}; >+ >+struct foo >+{ >+ long a = 0; >+ long b = 0; >+ >+ foo& operator=(foo const&) = default; >+ >+ friend bool >+ operator==(foo const& rhs, foo const& lhs) >+ { return rhs.a == lhs.a && rhs.b == lhs.b; } >+}; >+ >+int >+main () >+{ >+ check<long>(); >+ check<double>(); >+ check<foo>({42, 48}); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >new file mode 100644 >index 00000000000..b9fc063c66f >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >@@ -0,0 +1,59 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <type_traits> >+#include <chrono> >+ >+#include <testsuite_hooks.h> >+ >+int >+main () >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<bool> a(false); >+ std::atomic<bool> b(false); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(false); >+ if (a.load()) >+ { >+ b.store(true); >+ } >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(true); >+ a.notify_one(); >+ t.join(); >+ VERIFY( b.load() ); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >new file mode 100644 >index 00000000000..1d032085752 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >@@ -0,0 +1,32 @@ >+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include "generic.h" >+ >+int >+main () >+{ >+ check<float> f; >+ check<double> d; >+ check<long double> l; >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >new file mode 100644 >index 00000000000..0da374ece87 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >@@ -0,0 +1,88 @@ >+// -*- C++ -*- header. This isn't a header. I don't think this modeline should be here. The test seems to be missing any dg-options, target selectors etc. >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <chrono> >+ >+#include <testsuite_hooks.h> >+ >+template<typename Tp> >+Tp check_wait_notify(Tp val1, Tp val2) >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<Tp> a(val1); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(val1); >+ if (a.load() != val2) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ a.notify_one(); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp> >+Tp check_atomic_wait_notify(Tp val1, Tp val2) >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<Tp> a(val1); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ std::atomic_wait(&a, val1); >+ if (a.load() != val2) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ std::atomic_notify_one(&a); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp> >+struct check >+{ >+ check() >+ { >+ Tp a = 0; >+ Tp b = 42; >+ VERIFY(check_wait_notify(a, b) == b); >+ VERIFY(check_atomic_wait_notify(a, b) == b); >+ } >+}; >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >new file mode 100644 >index 00000000000..2afd19a7d14 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >@@ -0,0 +1,56 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include "generic.h" >+ >+int >+main () >+{ >+ // check<bool> bb; >+ check<char> ch; >+ check<signed char> sch; >+ check<unsigned char> uch; >+ check<short> s; >+ check<unsigned short> us; >+ check<int> i; >+ check<unsigned int> ui; >+ check<long> l; >+ check<unsigned long> ul; >+ check<long long> ll; >+ check<unsigned long long> ull; >+ >+ check<wchar_t> wch; >+ check<char8_t> ch8; >+ check<char16_t> ch16; >+ check<char32_t> ch32; >+ >+ check<int8_t> i8; >+ check<int16_t> i16; >+ check<int32_t> i32; >+ check<int64_t> i64; >+ >+ check<uint8_t> u8; >+ check<uint16_t> u16; >+ check<uint32_t> u32; >+ check<uint64_t> u64; >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >new file mode 100644 >index 00000000000..8531bb2e788 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >@@ -0,0 +1,59 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <type_traits> >+#include <chrono> >+ >+#include <testsuite_hooks.h> >+ >+int >+main () >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ long aa; >+ long bb; >+ >+ std::atomic<long*> a(nullptr); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(nullptr); >+ if (a.load() == &aa) >+ a.store(&bb); >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(&aa); >+ a.notify_one(); >+ t.join(); >+ VERIFY( a.load() == &bb); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc >new file mode 100644 >index 00000000000..aa203cdf525 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <latch> >+ >+#ifndef __cpp_lib_latch >+# error "Feature-test macro for latch missing in <latch>" >+#elif __cpp_lib_latch!= 201907L >+# error "Feature-test macro for latch has wrong value in <latch>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc >new file mode 100644 >index 00000000000..756727f33b3 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <version> >+ >+#ifndef __cpp_lib_latch >+# error "Feature-test macro for latch missing in <version>" >+#elif __cpp_lib_latch != 201907L >+# error "Feature-test macro for latch has wrong value in <version>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc >new file mode 100644 >index 00000000000..10bb500d261 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc >@@ -0,0 +1,50 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. >+// Just 2020. >+// 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+// >+#include <latch> >+#include <atomic> >+#include <thread> >+#include <testsuite_hooks.h> >+ >+int main() >+{ >+ std::atomic<int> a(0); >+ >+ std::latch l(3); >+ >+ VERIFY( !l.try_wait() ); >+ >+ auto fn = [&] >+ { >+ ++a; >+ l.count_down(); >+ }; >+ >+ std::thread t0(fn); >+ std::thread t1(fn); >+ >+ l.arrive_and_wait(); >+ t0.join(); >+ t1.join(); >+ >+ VERIFY( l.try_wait() ); >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc >new file mode 100644 >index 00000000000..1bbca687fc3 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <semaphore> >+ >+#ifndef __cpp_lib_semaphore >+# error "Feature-test macro for semaphore missing in <semaphore>" >+#elif __cpp_lib_semaphore != 201907L >+# error "Feature-test macro for semaphore has wrong value in <semaphore>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc >new file mode 100644 >index 00000000000..b96b8a59c64 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <version> >+ >+#ifndef __cpp_lib_semaphore >+# error "Feature-test macro for semaphore missing in <version>" >+#elif __cpp_lib_semaphore != 201907L >+# error "Feature-test macro for semaphore has wrong value in <version>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc >new file mode 100644 >index 00000000000..1ac9d261ca5 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc >@@ -0,0 +1,28 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <semaphore> >+ >+int main() >+{ >+ std::counting_semaphore<-1> sem(2); >+ return 0; >+} >+// { dg-error "static assertion failed" "" { target *-*-* } 0 } >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc >new file mode 100644 >index 00000000000..d38cef86cfc >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc >@@ -0,0 +1,55 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <limits> >+#include <cstddef> >+#include <testsuite_hooks.h> >+ >+void test01() >+{ >+ std::counting_semaphore<10> s(3); >+ >+ s.acquire(); >+ VERIFY( s.try_acquire() ); >+ VERIFY( s.try_acquire() ); >+ VERIFY( !s.try_acquire() ); >+ s.release(); >+ VERIFY( s.try_acquire() ); >+} >+ >+void test02() >+{ >+ std::binary_semaphore s(1); >+ >+ s.acquire(); >+ VERIFY( !s.try_acquire() ); >+ s.release(); >+ VERIFY( s.try_acquire() ); >+} >+ >+ >+int main() >+{ >+ test01(); >+ test02(); >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc >new file mode 100644 >index 00000000000..965554a3c28 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc >@@ -0,0 +1,85 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <chrono> >+#include <thread> >+#include <atomic> >+#include <testsuite_hooks.h> >+ >+void test01() >+{ >+ using namespace std::chrono_literals; >+ std::counting_semaphore<10> s(2); >+ s.acquire(); >+ >+ auto const dur = 250ms; >+ { >+ auto const t0 = std::chrono::steady_clock::now(); >+ VERIFY( s.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.try_acquire_for(dur) ); >+ auto const diff = std::chrono::steady_clock::now() - t0; >+ VERIFY( diff >= dur ); >+ } >+} >+ >+void test02() >+{ >+ using namespace std::chrono_literals; >+ std::binary_semaphore s(1); >+ std::atomic<int> a(0), b(0); >+ std::thread t([&] { >+ a.wait(0); >+ auto const dur = 250ms; >+ VERIFY( !s.try_acquire_for(dur) ); >+ b++; >+ b.notify_one(); >+ >+ a.wait(1); >+ VERIFY( s.try_acquire_for(dur) ); >+ b++; >+ b.notify_one(); >+ }); >+ t.detach(); >+ >+ s.acquire(); >+ a++; >+ a.notify_one(); >+ b.wait(0); >+ s.release(); >+ a++; >+ a.notify_one(); >+ >+ b.wait(1); >+} >+ >+int main() >+{ >+ test01(); >+ test02(); >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc >new file mode 100644 >index 00000000000..5e05606e97f >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc >@@ -0,0 +1,51 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <limits> >+#include <cstddef> >+#include <testsuite_hooks.h> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+void test01() >+{ >+ // the implementation optimizes for values of least_max_t that can fit >+ // in a futex, make sure we cover the case where least_max_t doesn't >+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max(); >+ std::counting_semaphore<least_max_t> s(3); >+ >+ s.acquire(); >+ VERIFY( s.try_acquire() ); >+ VERIFY( s.try_acquire() ); >+ VERIFY( !s.try_acquire() ); >+ s.release(); >+ VERIFY( s.try_acquire() ); >+} >+ >+#endif >+ >+int main() >+{ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ test01(); >+#endif >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc >new file mode 100644 >index 00000000000..bf99fd3cf8f >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc >@@ -0,0 +1,169 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <chrono> >+#include <thread> >+#include <atomic> >+#include <testsuite_hooks.h> >+ >+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ // The implementation supports posix as an implementation strategy >+ // make sure we cover that case >+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE This macro is defined too late to do anything. It would ned to be defined before including <semaphore> to have any effect. >+void test01() >+{ >+ std::counting_semaphore<10> s(3); >+ >+ s.acquire(); >+ VERIFY( s.try_acquire() ); >+ VERIFY( s.try_acquire() ); >+ VERIFY( !s.try_acquire() ); >+ s.release(); >+ VERIFY( s.try_acquire() ); >+} >+ >+void test02() >+{ >+ using namespace std::chrono_literals; >+ std::counting_semaphore<10> s(2); >+ s.acquire(); >+ >+ auto const dur = 250ms; >+ { >+ auto const t0 = std::chrono::steady_clock::now(); >+ VERIFY( s.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.try_acquire_for(dur) ); >+ auto const diff = std::chrono::steady_clock::now() - t0; >+ VERIFY( diff >= dur ); >+ } >+} >+ >+void test03() >+{ >+ using namespace std::chrono_literals; >+ std::binary_semaphore s(1); >+ std::atomic<int> a(0), b(0); >+ std::thread t([&] { >+ a.wait(0); >+ auto const dur = 250ms; >+ VERIFY( !s.try_acquire_for(dur) ); >+ b++; >+ b.notify_one(); >+ >+ a.wait(1); >+ VERIFY( s.try_acquire_for(dur) ); >+ b++; >+ b.notify_one(); >+ }); >+ t.detach(); >+ >+ s.acquire(); >+ a++; >+ a.notify_one(); >+ b.wait(0); >+ s.release(); >+ a++; >+ a.notify_one(); >+ >+ b.wait(1); >+} >+ >+void test04() >+{ >+ using namespace std::chrono_literals; >+ std::counting_semaphore<10> s(2); >+ s.acquire(); >+ >+ auto const dur = 250ms; >+ { >+ auto const at = std::chrono::system_clock::now() + dur; >+ auto const t0 = std::chrono::steady_clock::now(); >+ VERIFY( s.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.try_acquire_until(at) ); >+ auto const diff = std::chrono::steady_clock::now() - t0; >+ VERIFY( diff >= dur ); >+ } >+} >+ >+void test05() >+{ >+ using namespace std::chrono_literals; >+ std::binary_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.try_acquire_until(at) ); >+ >+ b++; >+ b.notify_one(); >+ } >+ >+ a.wait(1); >+ { >+ auto const at = std::chrono::system_clock::now() + dur; >+ VERIFY( s.try_acquire_until(at) ); >+ } >+ b++; >+ b.notify_one(); >+ }); >+ t.detach(); >+ >+ s.acquire(); >+ a++; >+ a.notify_one(); >+ b.wait(0); >+ s.release(); >+ a++; >+ a.notify_one(); >+ >+ b.wait(1); >+} >+#endif >+ >+int main() >+{ >+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ test01(); >+ test02(); >+ test03(); >+ test04(); >+ test05(); >+#endif >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc >new file mode 100644 >index 00000000000..cc67c5c0bf0 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc >@@ -0,0 +1,94 @@ >+// Copyright (C) 2019-2020 Free Software Foundation, Inc. Just 2020. >+// >+// 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <chrono> >+#include <thread> >+#include <atomic> >+#include <testsuite_hooks.h> >+ >+void test01() >+{ >+ using namespace std::chrono_literals; >+ std::counting_semaphore<10> s(2); >+ s.acquire(); >+ >+ auto const dur = 250ms; >+ { >+ auto const at = std::chrono::system_clock::now() + dur; >+ auto const t0 = std::chrono::steady_clock::now(); >+ VERIFY( s.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.try_acquire_until(at) ); >+ auto const diff = std::chrono::steady_clock::now() - t0; >+ VERIFY( diff >= dur ); >+ } >+} >+ >+void test02() >+{ >+ using namespace std::chrono_literals; >+ std::binary_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.try_acquire_until(at) ); >+ >+ b++; >+ b.notify_one(); >+ } >+ >+ a.wait(1); >+ { >+ auto const at = std::chrono::system_clock::now() + dur; >+ VERIFY( s.try_acquire_until(at) ); >+ } >+ b++; >+ b.notify_one(); >+ }); >+ t.detach(); >+ >+ s.acquire(); >+ a++; >+ a.notify_one(); >+ b.wait(0); >+ s.release(); >+ a++; >+ a.notify_one(); >+ >+ b.wait(1); >+} >+ >+int main() >+{ >+ test01(); >+ test02(); >+} >-- >2.26.2 > ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support 2020-08-03 14:09 ` Jonathan Wakely @ 2020-08-03 20:19 ` Jonathan Wakely 2020-09-03 0:47 ` Thomas Rodgers 0 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-08-03 20:19 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 03/08/20 15:09 +0100, Jonathan Wakely wrote: >On 05/06/20 17:29 -0700, Thomas Rodgers wrote: >>diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >>new file mode 100644 >>index 00000000000..f0c4235d91c >>--- /dev/null >>+++ b/libstdc++-v3/include/bits/semaphore_base.h >>@@ -0,0 +1,272 @@ >>+// -*- C++ -*- header. >>+ >>+// Copyright (C) 2020 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/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 >>+ >>+#pragma GCC system_header >>+ >>+#include <bits/c++config.h> >>+#include <bits/atomic_base.h> >>+#include <bits/atomic_timed_wait.h> >>+ >>+#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>) >>+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >>+#include <semaphore.h> >>+#endif >>+ >>+#include <chrono> >>+#include <type_traits> >>+ >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ >>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >>+ template<ptrdiff_t __least_max_value> >>+ struct __platform_semaphore Why is this a template? It means we'll instantiate identical code for counting_semaphore<3> and counting_semaphore<4>, but they're distinct types that will bloat the binary. Can't we just have a single __platform_semaphore type for all max values, and just make sure we don't instantiate it for values larger than SEM_VALUE_MAX? >>+ { > >I think we want to delete the copy constructor and copy assignment >operator for this type and __atomic_semaphore. > >>+ using __clock_t = chrono::system_clock; >>+ >>+ explicit __platform_semaphore(ptrdiff_t __count) noexcept >>+ { >>+ static_assert( __least_max_value <= SEM_VALUE_MAX, ""); I think it would be useful for __platform_semaphore to define a static constexpr member like: static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; And then __atomic_semaphore could define: static constexpr _Tp _S_max = __gnu_cxx::__int_traits<_Tp>::__max; And then the alias __semaphore_base could be defined as: #if _GLIBCXX_HAVE_LINUX_FUTEX && ! _GLIBCXX_REQUIRE_POSIX_SEMAPHORE // Use futex if available and user didn't force use of POSIX: using __fast_semaphore_base = __atomic_semaphore<__platform_wait_t>; #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE // Otherwise, use POSIX if available: using __fast_semaphore_base = __platform_semaphore<SEM_VALUE_MAX>; #else // Otherwise, use atomics: using __fast_semaphore_base = __atomic_semaphore<ptrdiff_t>; #endif template<ptrdiff_t __least_max_value> using __semaphore_base = conditional_t< __least_max_value <= __fast_semaphore_base::_S_max, __fast_semaphore_base, __atomic_semaphore<ptrdiff_t>; Would that make sense? Do the comments above reflect the intent? >>+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE > >What is this case for? This macro seems to only exist for use by a >single testcase. > >Is it supposed to be something users can set? If so, it needs to be >documented as ABI-breaking and as implying that counting_semaphore >cannot be use with counts higher than SEM_VALUE_MAX. > >>+ template<ptrdiff_t __least_max_value> >>+ using __semaphore_base = __platform_semaphore<__least_max_value>; >>+#else >>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ template<ptrdiff_t __least_max_value> >>+ using __semaphore_base = conditional_t<( >>+ __least_max_value >= 0 > >This condition is redundant, we already know that counting_semaphore >has checked the value is non-negative. > >>+ && __least_max_value <= __detail::__platform_wait_max_value), >>+ __atomic_semaphore<__detail::__platform_wait_t>, >>+ __atomic_semaphore<ptrdiff_t>>; >>+ >>+// __platform_semaphore >>+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE >>+ template<ptrdiff_t __least_max_value> >>+ using __semaphore_base = conditional_t<( >>+ __least_max_value >= 0 > >Redundant condition again. > >>+ && __least_max_value <= SEM_VALUE_MAX), >>+ __platform_semaphore<__least_max_value>, >>+ __atomic_semaphore<ptrdiff_t>>; >>+# else >>+ template<ptrdiff_t __least_max_value> >>+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>; >>+# endif >>+#endif >>+ >>+_GLIBCXX_END_NAMESPACE_VERSION >>+} // namespace std >>+ >>+#endif ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] Add C++2a synchronization support 2020-08-03 20:19 ` Jonathan Wakely @ 2020-09-03 0:47 ` Thomas Rodgers 2020-09-03 0:54 ` Thomas Rodgers 0 siblings, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-09-03 0:47 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers Adds support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 172 +++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ libstdc++-v3/include/std/atomic | 73 +++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 92 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ .../atomic/wait_notify/integrals.cc | 65 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 30 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 27 files changed, 2387 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 1dff3862e35..ef8acd4a389 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 16371015071..0dfb2df9ab3 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -398,6 +398,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch\ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -415,6 +416,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -446,6 +448,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -520,6 +524,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 015acef83c4..c121d993fee 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -562,6 +565,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -823,6 +851,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -911,6 +964,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1164,6 +1244,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1301,6 +1399,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1396,6 +1512,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1551,6 +1685,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1660,6 +1812,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..2f57356b366 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,281 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, + _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags::__wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags::__bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__platform_wait_until_impl(__addr, __val, __s_atime) == + __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) == + __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..21ec3d38c94 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,301 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..ed127a7a953 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,283 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +#include <iostream> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == EINTR)) + continue; + else if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err && (errno == EINVAL)) + return false; // caller supplied an invalid __atime + else if (__err) + std::terminate(); + else + break; + } + 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<__clock_t, _Clock>::value) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_a(__count) + { } + + __atomic_semaphore(const __atomic_semaphore&) = delete; + __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..c15909d9ccb 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +376,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we can do that + return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +635,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1399,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..bd06db5aa7f --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..865d6c4aecb --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<class _Rep, class _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<class _Clock, class _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index f64aff4f520..72d7769ebcf 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -214,12 +214,14 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..d15b9c86ae6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..115cb79a040 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..cf1a31f996b --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] Add C++2a synchronization support 2020-09-03 0:47 ` Thomas Rodgers @ 2020-09-03 0:54 ` Thomas Rodgers 2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers 0 siblings, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-09-03 0:54 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers Note - ignore previous version of this patch, didn't filter out Makefile.in Adds support for - atomic wait/notify_one/notify_all counting_semaphore binary_semaphore latch * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>::wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 172 +++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ libstdc++-v3/include/std/atomic | 73 +++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 92 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ .../atomic/wait_notify/integrals.cc | 65 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 30 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 27 files changed, 2387 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 1dff3862e35..ef8acd4a389 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -100,6 +102,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -174,6 +178,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 015acef83c4..c121d993fee 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -562,6 +565,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -823,6 +851,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -911,6 +964,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1164,6 +1244,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1301,6 +1399,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1396,6 +1512,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1551,6 +1685,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1660,6 +1812,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..2f57356b366 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,281 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, + _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags::__wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags::__bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__platform_wait_until_impl(__addr, __val, __s_atime) == + __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) == + __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..21ec3d38c94 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,301 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..ed127a7a953 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,283 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +#include <iostream> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == EINTR)) + continue; + else if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err && (errno == EINVAL)) + return false; // caller supplied an invalid __atime + else if (__err) + std::terminate(); + else + break; + } + 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<__clock_t, _Clock>::value) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_a(__count) + { } + + __atomic_semaphore(const __atomic_semaphore&) = delete; + __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..c15909d9ccb 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +376,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we can do that + return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +635,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1399,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..bd06db5aa7f --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..865d6c4aecb --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<class _Rep, class _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<class _Clock, class _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index f64aff4f520..72d7769ebcf 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -214,12 +214,14 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..d15b9c86ae6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..115cb79a040 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..cf1a31f996b --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] libstdc++: Add C++2a synchronization support 2020-09-03 0:54 ` Thomas Rodgers @ 2020-09-11 23:58 ` Thomas Rodgers 2020-09-28 13:25 ` Jonathan Wakely ` (2 more replies) 0 siblings, 3 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-09-11 23:58 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers From: Thomas Rodgers <trodgers@redhat.com> This patch supercedes both the Add C++2a synchronization support patch being replied to *and* the patch adding wait/notify_* to atomic_flag. Add support for - * atomic_flag::wait/notify_one/notify_all * atomic::wait/notify_one/notify_all * counting_semaphore * binary_semaphore * latch libstdc++-v3/ChangeLog: * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_flag::wait): Define. (__atomic_flag::notify_one): Likewise. (__atomic_flag::notify_all): Likewise. (__atomic_base<_Itp>::wait): Likewise. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ libstdc++-v3/include/std/atomic | 73 +++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 92 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ .../atomic/wait_notify/integrals.cc | 65 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 30 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 28 files changed, 2471 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index c9df9a9d6c6..9b5b6ed0005 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -101,6 +103,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -175,6 +179,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 2cdd2bd6cae..dd4db926592 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __atomic_load(&_M_i, &__v, int(__m)); return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } + + _GLIBCXX_ALWAYS_INLINE void + wait(bool __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->test(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..2f57356b366 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,281 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, + _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags::__wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags::__bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__platform_wait_until_impl(__addr, __val, __s_atime) == + __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) == + __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..21ec3d38c94 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,301 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..ed127a7a953 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,283 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +#include <iostream> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == EINTR)) + continue; + else if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err && (errno == EINVAL)) + return false; // caller supplied an invalid __atime + else if (__err) + std::terminate(); + else + break; + } + 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<__clock_t, _Clock>::value) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_a(__count) + { } + + __atomic_semaphore(const __atomic_semaphore&) = delete; + __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..c15909d9ccb 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +376,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we can do that + return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +635,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1399,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..bd06db5aa7f --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..865d6c4aecb --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<class _Rep, class _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<class _Clock, class _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index d5d42ed0a72..b9c7c6c62a8 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -216,12 +216,14 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..d15b9c86ae6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..115cb79a040 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc new file mode 100644 index 00000000000..6de7873ecc2 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -0,0 +1,61 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +int +main() +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic_flag a; + std::atomic_flag b; + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + b.test_and_set(); + b.notify_one(); + }); + + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.test_and_set(); + a.notify_one(); + b.wait(false); + t.join(); + + VERIFY( a.test() ); + VERIFY( b.test() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..cf1a31f996b --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers @ 2020-09-28 13:25 ` Jonathan Wakely 2020-10-01 23:37 ` Thomas Rodgers 2020-09-28 13:30 ` Jonathan Wakely 2020-09-28 13:36 ` Jonathan Wakely 2 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-09-28 13:25 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 11/09/20 16:58 -0700, Thomas Rodgers wrote: >From: Thomas Rodgers <trodgers@redhat.com> > >This patch supercedes both the Add C++2a synchronization support patch >being replied to *and* the patch adding wait/notify_* to atomic_flag. > >Add support for - > * atomic_flag::wait/notify_one/notify_all > * atomic::wait/notify_one/notify_all > * counting_semaphore > * binary_semaphore > * latch > >libstdc++-v3/ChangeLog: > > * include/Makefile.am (bits_headers): Add new header. > * include/Makefile.in: Regenerate. > * include/bits/atomic_base.h (__atomic_flag::wait): Define. > (__atomic_flag::notify_one): Likewise. > (__atomic_flag::notify_all): Likewise. > (__atomic_base<_Itp>::wait): Likewise. > (__atomic_base<_Itp>::notify_one): Likewise. > (__atomic_base<_Itp>::notify_all): Likewise. > (__atomic_base<_Ptp*>::wait): Likewise. > (__atomic_base<_Ptp*>::notify_one): Likewise. > (__atomic_base<_Ptp*>::notify_all): Likewise. > (__atomic_impl::wait): Likewise. > (__atomic_impl::notify_one): Likewise. > (__atomic_impl::notify_all): Likewise. > (__atomic_float<_Fp>::wait): Likewise. > (__atomic_float<_Fp>::notify_one): Likewise. > (__atomic_float<_Fp>::notify_all): Likewise. > (__atomic_ref<_Tp>::wait): Likewise. > (__atomic_ref<_Tp>::notify_one): Likewise. > (__atomic_ref<_Tp>::notify_all): Likewise. > (atomic_wait<_Tp>): Likewise. > (atomic_wait_explicit<_Tp>): Likewise. > (atomic_notify_one<_Tp>): Likewise. > (atomic_notify_all<_Tp>): Likewise. > * include/bits/atomic_wait.h: New file. > * include/bits/atomic_timed_wait.h: New file. > * include/bits/semaphore_base.h: New file. > * include/std/atomic (atomic<bool>::wait): Define. > (atomic<bool>::wait_one): Likewise. > (atomic<bool>::wait_all): Likewise. > (atomic<_Tp>::wait): Likewise. > (atomic<_Tp>::wait_one): Likewise. > (atomic<_Tp>::wait_all): Likewise. > (atomic<_Tp*>::wait): Likewise. > (atomic<_Tp*>::wait_one): Likewise. > (atomic<_Tp*>::wait_all): Likewise. > * include/std/latch: New file. > * include/std/semaphore: New file. > * include/std/version: Add __cpp_lib_semaphore and > __cpp_lib_latch defines. > * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. > * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. > * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. > * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. > * testsuite/30_thread/semaphore/1.cc: New test. > * testsuite/30_thread/semaphore/2.cc: Likewise. > * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. > * testsuite/30_thread/latch/1.cc: New test. > * testsuite/30_thread/latch/2.cc: New test. > * testsuite/30_thread/latch/3.cc: New test. >--- > libstdc++-v3/include/Makefile.am | 5 + > libstdc++-v3/include/Makefile.in | 5 + > libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- > libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ > libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ > libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ > libstdc++-v3/include/std/atomic | 73 +++++ > libstdc++-v3/include/std/latch | 90 ++++++ > libstdc++-v3/include/std/semaphore | 92 ++++++ > libstdc++-v3/include/std/version | 2 + > .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ > .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ > .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ > .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ > .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ > .../atomic/wait_notify/integrals.cc | 65 ++++ > .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ > .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ > libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ > libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ > libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ > .../testsuite/30_threads/semaphore/1.cc | 27 ++ > .../testsuite/30_threads/semaphore/2.cc | 27 ++ > .../semaphore/least_max_value_neg.cc | 30 ++ > .../30_threads/semaphore/try_acquire.cc | 55 ++++ > .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ > .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ > .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ > 28 files changed, 2471 insertions(+), 1 deletion(-) > create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h > create mode 100644 libstdc++-v3/include/bits/atomic_wait.h > create mode 100644 libstdc++-v3/include/bits/semaphore_base.h > create mode 100644 libstdc++-v3/include/std/latch > create mode 100644 libstdc++-v3/include/std/semaphore > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc > >diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am >index c9df9a9d6c6..9b5b6ed0005 100644 >--- a/libstdc++-v3/include/Makefile.am >+++ b/libstdc++-v3/include/Makefile.am >@@ -52,6 +52,7 @@ std_headers = \ > ${std_srcdir}/iostream \ > ${std_srcdir}/istream \ > ${std_srcdir}/iterator \ >+ ${std_srcdir}/latch \ > ${std_srcdir}/limits \ > ${std_srcdir}/list \ > ${std_srcdir}/locale \ >@@ -69,6 +70,7 @@ std_headers = \ > ${std_srcdir}/ratio \ > ${std_srcdir}/regex \ > ${std_srcdir}/scoped_allocator \ >+ ${std_srcdir}/semaphore \ > ${std_srcdir}/set \ > ${std_srcdir}/shared_mutex \ > ${std_srcdir}/span \ >@@ -101,6 +103,8 @@ bits_headers = \ > ${bits_srcdir}/allocated_ptr.h \ > ${bits_srcdir}/allocator.h \ > ${bits_srcdir}/atomic_base.h \ >+ ${bits_srcdir}/atomic_wait.h \ >+ ${bits_srcdir}/atomic_timed_wait.h \ > ${bits_srcdir}/atomic_futex.h \ > ${bits_srcdir}/basic_ios.h \ > ${bits_srcdir}/basic_ios.tcc \ >@@ -175,6 +179,7 @@ bits_headers = \ > ${bits_srcdir}/regex_compiler.tcc \ > ${bits_srcdir}/regex_executor.h \ > ${bits_srcdir}/regex_executor.tcc \ >+ ${bits_srcdir}/semaphore_base.h \ > ${bits_srcdir}/shared_ptr.h \ > ${bits_srcdir}/shared_ptr_atomic.h \ > ${bits_srcdir}/shared_ptr_base.h \ >diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >index 2cdd2bd6cae..dd4db926592 100644 >--- a/libstdc++-v3/include/bits/atomic_base.h >+++ b/libstdc++-v3/include/bits/atomic_base.h >@@ -37,6 +37,10 @@ > #include <bits/atomic_lockfree_defines.h> > #include <bits/move.h> > >+#if __cplusplus > 201703L >+#include <bits/atomic_wait.h> >+#endif >+ > #ifndef _GLIBCXX_ALWAYS_INLINE > #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) > #endif >@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return __ret; > } > >- > // Base types for atomics. > template<typename _IntTp> > struct __atomic_base; >@@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __atomic_load(&_M_i, &__v, int(__m)); > return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; > } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(bool __old, >+ memory_order __m = memory_order_seq_cst) const noexcept >+ { >+ std::__atomic_wait(&_M_i, __old, >+ [__m, this, __old]() >+ { return this->test(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { std::__atomic_notify(&_M_i, false); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { std::__atomic_notify(&_M_i, true); } >+ >+ // TODO add const volatile overload > #endif // C++20 > > _GLIBCXX_ALWAYS_INLINE void >@@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__m)); > } > >+#if __cplusplus > 201703L >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(__int_type __old, >+ memory_order __m = memory_order_seq_cst) const noexcept >+ { >+ std::__atomic_wait(&_M_i, __old, >+ [__m, this, __old] >+ { return this->load(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { std::__atomic_notify(&_M_i, false); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { std::__atomic_notify(&_M_i, true); } >+ >+ // TODO add const volatile overload >+#endif // C++2a >+ > _GLIBCXX_ALWAYS_INLINE __int_type > fetch_add(__int_type __i, > memory_order __m = memory_order_seq_cst) noexcept >@@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > int(__m1), int(__m2)); > } > >+#if __cplusplus > 201703L >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(__pointer_type __old, >+ memory_order __m = memory_order_seq_cst) noexcept >+ { >+ std::__atomic_wait(&_M_p, __old, >+ [__m, this, __old]() >+ { return this->load(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { std::__atomic_notify(&_M_p, false); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { std::__atomic_notify(&_M_p, true); } >+ >+ // TODO add const volatile overload >+#endif // C++2a >+ > _GLIBCXX_ALWAYS_INLINE __pointer_type > fetch_add(ptrdiff_t __d, > memory_order __m = memory_order_seq_cst) noexcept >@@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > int(__success), int(__failure)); > } > >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(const _Tp* __ptr, _Val<_Tp> __old, >+ memory_order __m = memory_order_seq_cst) noexcept >+ { >+ std::__atomic_wait(__ptr, __old, >+ [=]() { return load(__ptr, __m) == __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one(const _Tp* __ptr) noexcept >+ { std::__atomic_notify(__ptr, false); } >+ >+ // TODO add const volatile overload >+ >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all(const _Tp* __ptr) noexcept >+ { std::__atomic_notify(__ptr, true); } >+ >+ // TODO add const volatile overload >+#endif // C++2a >+ > template<typename _Tp> > _GLIBCXX_ALWAYS_INLINE _Tp > fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept >@@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(&_M_fp, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(&_M_fp); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(&_M_fp); } >+ >+ // TODO add const volatile overload >+ > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) noexcept >@@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > private: > _Tp* _M_ptr; > }; >@@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) const noexcept >@@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) const noexcept >@@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > _GLIBCXX_ALWAYS_INLINE value_type > fetch_add(difference_type __d, > memory_order __m = memory_order_seq_cst) const noexcept >diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >new file mode 100644 >index 00000000000..2f57356b366 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >@@ -0,0 +1,281 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/atomic_wait.h> >+ >+#include <chrono> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <sys/time.h> >+#endif >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ enum class __atomic_wait_status { no_timeout, timeout }; >+ >+ namespace __detail >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ using __platform_wait_clock_t = chrono::steady_clock; >+ >+ template<typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until_impl(__platform_wait_t* __addr, >+ __platform_wait_t __val, >+ const chrono::time_point<__platform_wait_clock_t, >+ _Duration>& __atime) noexcept >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ struct timespec __rt = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ auto __e = syscall (SYS_futex, __addr, >+ static_cast<int>(__futex_wait_flags::__wait_bitset_private), >+ __val, &__rt, nullptr, >+ static_cast<int>(__futex_wait_flags::__bitset_match_any)); >+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >+ std::terminate(); >+ return (__platform_wait_clock_t::now() < __atime) >+ ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; Newline before the ': __atomic_wait_status::timeout' to keep it fitting in 80 columns. >+ } >+ >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+ if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) >+ { >+ return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); I askjed for this to be qualified, but just __detail:: is enough, you don't need std::__detail. That should make the line fit in 80 columns. There are still dozens of lines that don't fit in 80 cols in this revision of the patch. >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __platform_wait_clock_t::time_point __s_entry = >+ __platform_wait_clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (std::__detail::__platform_wait_until_impl(__addr, __val, __s_atime) == Just __detail:: again for the qualification. It doesn't need to be std::__detail::. Line breaks should be before operators please, i.e. value_with_a_long_name == bar or: long_condition && another_condition It's much easier to find the operator when it's at the start of the line, at a predictable indentation, rather than wandering off on the right somewhere. >+ __atomic_wait_status::no_timeout) >+ return __atomic_wait_status::no_timeout; >+ >+ // 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. >+ if (_Clock::now() < __atime) >+ return __atomic_wait_status::no_timeout; >+ return __atomic_wait_status::timeout; >+ } >+ } >+#endif >+ >+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >+ template<typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until_impl(__gthread_cond_t* __cv, >+ unique_lock<mutex>& __lock, >+ const chrono::time_point<chrono::steady_clock, _Duration>& __atime) >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ __gthread_time_t __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), >+ CLOCK_MONOTONIC, >+ &__ts); >+ return (chrono::steady_clock::now() < __atime) >+ ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; >+ } >+#endif >+ >+ template<typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until_impl(__gthread_cond_t* __cv, >+ unique_lock<std::mutex>& __lock, >+ const chrono::time_point<chrono::system_clock, _Duration>& __atime) >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ __gthread_time_t __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), >+ &__ts); >+ return (chrono::system_clock::now() < __atime) >+ ? __atomic_wait_status::no_timeout >+ : __atomic_wait_status::timeout; >+ } >+ >+ // return true if timeout >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __cond_wait_until(__gthread_cond_t* __cv, >+ unique_lock<std::mutex>& __lock, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT >+ using __clock_t = chrono::steady_clock; >+#else >+ using __clock_t = chrono::system_clock; >+#endif >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t::time_point __s_entry = __clock_t::now(); >+ const auto __delta = __atime - __c_entry; >+ const auto __s_atime = __s_entry + __delta; >+ if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) >+ return __atomic_wait_status::no_timeout; >+ // 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. >+ if (_Clock::now() < __atime) >+ return __atomic_wait_status::no_timeout; >+ return __atomic_wait_status::timeout; >+ } >+ >+ struct __timed_waiters : __waiters >+ { >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ _M_do_wait_until(__platform_wait_t __version, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ return __platform_wait_until(&_M_ver, __version, __atime); >+#else >+ __platform_wait_t __cur = 0; >+ __waiters::__lock_t __l(_M_mtx); >+ while (__cur <= __version) >+ { >+ if (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) == >+ __atomic_wait_status::timeout) >+ return __atomic_wait_status::timeout; >+ >+ __platform_wait_t __last = __cur; >+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >+ if (__cur < __last) >+ break; // break the loop if version overflows >+ } >+ return __atomic_wait_status::no_timeout; >+#endif >+ } >+ >+ static __timed_waiters& >+ _S_timed_for(void* __t) >+ { >+ static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); >+ return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); >+ } >+ }; >+ } // namespace __detail >+ >+ template<typename _Tp, typename _Pred, >+ typename _Clock, typename _Duration> >+ bool >+ __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ using namespace __detail; >+ >+ if (std::__atomic_spin(__pred)) >+ return true; >+ >+ auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >+ auto __version = __w._M_enter_wait(); >+ do >+ { >+ __atomic_wait_status __res; >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, >+ __old, >+ __atime); >+ } >+ else >+ { >+ __res = __w._M_do_wait_until(__version, __atime); >+ } >+ if (__res == __atomic_wait_status::timeout) >+ return false; >+ } >+ while (!__pred() && __atime < _Clock::now()); >+ __w._M_leave_wait(); >+ >+ // if timed out, return false >+ return (_Clock::now() < __atime); >+ } >+ >+ template<typename _Tp, typename _Pred, >+ typename _Rep, typename _Period> >+ bool >+ __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { >+ using namespace __detail; >+ >+ if (std::__atomic_spin(__pred)) >+ return true; >+ >+ if (!__rtime.count()) >+ return false; // no rtime supplied, and spin did not acquire >+ >+ using __dur = chrono::steady_clock::duration; >+ auto __reltime = chrono::duration_cast<__dur>(__rtime); >+ if (__reltime < __rtime) >+ ++__reltime; >+ >+ >+ return __atomic_wait_until(__addr, __old, std::move(__pred), >+ chrono::steady_clock::now() + __reltime); >+ } >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+#endif >diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >new file mode 100644 >index 00000000000..21ec3d38c94 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_wait.h >@@ -0,0 +1,301 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/gthr.h> >+#include <bits/std_mutex.h> >+#include <bits/unique_lock.h> >+#include <ext/numeric_traits.h> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <climits> >+#include <unistd.h> >+#include <syscall.h> >+#endif >+ >+ >+// TODO get this from Autoconf >+#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ namespace __detail >+ { >+ using __platform_wait_t = int; >+ >+ constexpr auto __atomic_spin_count_1 = 16; >+ constexpr auto __atomic_spin_count_2 = 12; >+ >+ inline constexpr >+ auto __platform_wait_max_value = >+ __gnu_cxx::__int_traits<__platform_wait_t>::__max; >+ >+ template<typename _Tp> >+ inline constexpr bool __platform_wait_uses_type >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; >+#else >+ = false; >+#endif >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ enum class __futex_wait_flags : int >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >+ __private_flag = 128, >+#else >+ __private_flag = 0, >+#endif >+ __wait = 0, >+ __wake = 1, >+ __wait_bitset = 9, >+ __wake_bitset = 10, >+ __wait_private = __wait | __private_flag, >+ __wake_private = __wake | __private_flag, >+ __wait_bitset_private = __wait_bitset | __private_flag, >+ __wake_bitset_private = __wake_bitset | __private_flag, >+ __bitset_match_any = -1 >+ }; >+ >+ template<typename _Tp> >+ void >+ __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept >+ { >+ auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), >+ static_cast<int>(__futex_wait_flags::__wait_private), >+ __val, nullptr); >+ if (__e && !(errno == EINTR || errno == EAGAIN)) >+ std::terminate(); This treats EINTR and EGAIN as a spurious wakeup, right? So callers cannot assume that this function has actually waited for the value to change. I'll come back to that below. >+ struct __waiters >+ { >+ __platform_wait_t alignas(64) _M_ver = 0; >+ __platform_wait_t alignas(64) _M_wait = 0; The alignment-specifier needs to come before the type name: a.cc:3:7: warning: attribute ignored [-Wattributes] 3 | int alignas(64) i = 0; | ^~~~~~~ a.cc:3:7: note: an attribute that appertains to a type-specifier is ignored >+ __platform_wait_t >+ _M_waiting() const noexcept Should this function return a bool? It looks like the only caller of it just tests if it's zero. If it means "number of waiters" rather than "there are threads waiting" then I think it should be renamed to make that more clear (and similarly for _M_wait, which sounds like a boolean flag not a count). >+ { >+ __platform_wait_t __res; >+ __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >+ return __res; >+ } >+ >+ void >+ _M_notify(bool __all) noexcept >+ { >+ __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ __platform_notify(&_M_ver, __all); >+#else >+ auto __e = __gthread_cond_broadcast(&_M_cv); >+ if (__e) >+ __throw_system_error(__e); This will terminate since the function is noexcept, which is fine. Maybe we should consistently use __throw_system_error(__e) which allows the verbose terminate handler to print a message: terminate called after throwing an instance of 'std::system_error' what(): Resource temporarily unavailable Aborted (core dumped) So the other places that currently call std::terminate() should maybe change to __throw_system_error(__e). What do you think? >+#endif >+ } >+ >+ static __waiters& >+ _S_for(const void* __t) >+ { >+ const unsigned char __mask = 0xf; >+ static __waiters __w[__mask + 1]; >+ >+ auto __key = _Hash_impl::hash(__t) & __mask; >+ return __w[__key]; >+ } >+ }; >+ >+ struct __waiter >+ { >+ __waiters& _M_w; >+ __platform_wait_t _M_version; >+ >+ template<typename _Tp> >+ __waiter(const _Tp* __addr) noexcept >+ : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) >+ , _M_version(_M_w._M_enter_wait()) >+ { } >+ >+ ~__waiter() >+ { _M_w._M_leave_wait(); } >+ >+ void _M_do_wait() noexcept >+ { _M_w._M_do_wait(_M_version); } >+ }; >+ >+ void >+ __thread_relax() noexcept >+ { >+#if defined __i386__ || defined __x86_64__ >+ __builtin_ia32_pause(); >+#elif defined _GLIBCXX_USE_SCHED_YIELD >+ __gthread_yield(); >+#endif >+ } >+ >+ void >+ __thread_yield() noexcept >+ { >+#if defined _GLIBCXX_USE_SCHED_YIELD >+ __gthread_yield(); >+#endif >+ } >+ >+ } // namespace __detail >+ >+ template<typename _Pred> >+ bool >+ __atomic_spin(_Pred __pred) noexcept Should this take its argument by (non-const) reference? >+ { >+ for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) >+ { >+ if (__pred()) >+ return true; >+ >+ if (__i < __detail::__atomic_spin_count_2) >+ __detail::__thread_relax(); >+ else >+ __detail::__thread_yield(); >+ } >+ return false; >+ } >+ >+ template<typename _Tp, typename _Pred> >+ void >+ __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >+ { >+ using namespace __detail; >+ if (__atomic_spin(__pred)) >+ return; >+ >+ __waiter __w(__addr); >+ while (!__pred()) >+ { >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __platform_wait(__addr, __old); If this gets a spurious wakeup due to EINTR or EAGAIN then we'll loop and test the predicate again. >+ } >+ else >+ { >+ // TODO support timed backoff when this can be moved into the lib >+ __w._M_do_wait(); >+ } >+ } >+ } >+ >+ template<typename _Tp> >+ void >+ __atomic_notify(const _Tp* __addr, bool __all) noexcept >+ { >+ using namespace __detail; >+ auto& __w = __waiters::_S_for((void*)__addr); >+ if (!__w._M_waiting()) >+ return; >+ >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __platform_notify((__platform_wait_t*)(void*) __addr, __all); >+ } >+ else >+ { >+ __w._M_notify(__all); >+ } >+ } >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+#endif >diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >new file mode 100644 >index 00000000000..ed127a7a953 >--- /dev/null >+++ b/libstdc++-v3/include/bits/semaphore_base.h >@@ -0,0 +1,283 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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/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 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/atomic_base.h> >+#include <bits/atomic_timed_wait.h> >+ >+#if __has_include(<semaphore.h>) >+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >+#include <semaphore.h> >+#endif >+ >+#include <chrono> >+#include <type_traits> >+ >+#include <iostream> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ struct __platform_semaphore >+ { >+ using __clock_t = chrono::system_clock; >+ static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; >+ >+ 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 >+ { >+ for (;;) >+ { >+ auto __err = sem_wait(&_M_semaphore); >+ if (__err && (errno == EINTR)) >+ continue; >+ else if (__err) >+ std::terminate(); >+ else >+ break; >+ } >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ _M_release(std::ptrdiff_t __update) noexcept >+ { >+ for(; __update != 0; --__update) >+ { >+ auto __err = sem_post(&_M_semaphore); >+ if (__err) >+ 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); >+ >+ struct timespec __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ for (;;) >+ { >+ auto __err = sem_timedwait(&_M_semaphore, &__ts); >+ if (__err && (errno == EINTR)) >+ continue; >+ else if (__err && (errno == ETIMEDOUT)) >+ return false; >+ else if (__err && (errno == EINVAL)) >+ return false; // caller supplied an invalid __atime >+ else if (__err) >+ std::terminate(); >+ else >+ break; >+ } >+ 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<__clock_t, _Clock>::value) >+ { >+ return _M_try_acquire_until_impl(__atime); >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t __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; >+ >+ // 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 >+ >+ template<typename _Tp> >+ struct __atomic_semaphore >+ { >+ static_assert(std::is_integral_v<_Tp>); >+ static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; Do we need a check that ptrdiff_t can represent this value? __atomic_semaphore<size_t> would overflow here. >+ >+ explicit __atomic_semaphore(_Tp __count) noexcept >+ : _M_a(__count) >+ { } This should use __glibcxx_assert to check the preconditions: __glibcxx_assert(__count >= 0 && __count <= max()); >+ >+ __atomic_semaphore(const __atomic_semaphore&) = delete; >+ __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ _M_acquire() noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, >+ memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ __atomic_wait(&_M_a, __old, __pred); >+ } >+ >+ bool >+ _M_try_acquire() noexcept >+ { >+ auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ if (__old == 0) >+ return false; >+ >+ return __atomic_spin([this, &__old] >+ { Doesn't the check for __old==0 need to be inside the predicate? Otherwise consider the case where _M_a=1 initially. Two threads call try_acquire() concurrently. Both see that __old==0 is false, so both call __atomic_spin with this CAS "predicate" (*). The first thread replaces the value with 0, the second thread fails the CAS because the value changed, but it replaces __old with the new value and tries again (because __atomic_spin loops) and this time it succeeds, changing the value to -1. The second thread should have had no effect and returned immediately. (*) I find it a bit disconcerting that these predicates are not const, they modify the variable, which implies a precondition that the predicate is never evaluated again after it returns true. >+ return __atomic_impl::compare_exchange_weak(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }); >+ } >+ >+ template<typename _Clock, typename _Duration> >+ _GLIBCXX_ALWAYS_INLINE bool >+ _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, >+ memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ return __atomic_wait_until(&_M_a, __old, __pred, __atime); >+ } >+ >+ template<typename _Rep, typename _Period> >+ _GLIBCXX_ALWAYS_INLINE bool >+ _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_a, >+ memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_a, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ >+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); >+ return __atomic_wait_for(&_M_a, __old, __pred, __rtime); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ _M_release(ptrdiff_t __update) noexcept >+ { >+ if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) >+ return; >+ if (__update > 1) >+ __atomic_impl::notify_all(&_M_a); >+ else >+ __atomic_impl::notify_one(&_M_a); >+ } >+ >+ private: >+ alignas(__alignof__(_Tp)) _Tp _M_a; I'd prefer a better name than _M_a here. The standard calls it "counter" so _M_counter maybe? >+ }; >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE I still don't like this macro, but nevermind. Could you add a comment saying that the macro can be used to force the use of POSIX sem_t but that doing so alters the ABI? >+ // Use futex if available and didn't force use of POSIX >+ using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; >+#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ using __fast_semaphore = __platform_semaphore; >+#else >+ using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; >+#endif >+ >+template<ptrdiff_t __least_max_value> >+ using __semaphore_impl = conditional_t< >+ (__least_max_value > 1), >+ conditional_t< >+ (__least_max_value <= __fast_semaphore::_S_max), >+ __fast_semaphore, >+ __atomic_semaphore<ptrdiff_t>>, >+ __fast_semaphore>; >+ >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace std >+ >+#endif >diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic >index 1a304261fe7..c15909d9ccb 100644 >--- a/libstdc++-v3/include/std/atomic >+++ b/libstdc++-v3/include/std/atomic >@@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > compare_exchange_strong(bool& __i1, bool __i2, > memory_order __m = memory_order_seq_cst) volatile noexcept > { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >+ >+#if __cplusplus > 201703L >+ void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { _M_base.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_base.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_base.notify_all(); } >+#endif > }; > > #if __cplusplus <= 201703L >@@ -363,6 +376,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > memory_order __m = memory_order_seq_cst) volatile noexcept > { return compare_exchange_strong(__e, __i, __m, > __cmpexch_failure_order(__m)); } Blank line here before the next member function please. >+#if __cplusplus > 201703L >+ void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { >+ std::__atomic_wait(&_M_i, __old, >+ [__m, this, __old] >+ { >+ const auto __v = this->load(__m); >+ // TODO make this ignore padding bits when we can do that >+ return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0; >+ }); >+ } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { std::__atomic_notify(&_M_i, false); } >+ >+ void notify_all() const noexcept >+ { std::__atomic_notify(&_M_i, true); } >+#endif >+ > }; > #undef _GLIBCXX20_INIT > >@@ -601,6 +635,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__m)); > } > >+#if __cplusplus > 201703L >+ void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept >+ { _M_b.wait(__old, __m); } >+ >+ // TODO add const volatile overload >+ >+ void notify_one() const noexcept >+ { _M_b.notify_one(); } >+ >+ void notify_all() const noexcept >+ { _M_b.notify_all(); } >+#endif > __pointer_type > fetch_add(ptrdiff_t __d, > memory_order __m = memory_order_seq_cst) noexcept >@@ -1353,6 +1399,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > memory_order_seq_cst); > } > >+ >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ inline void >+ atomic_wait(const atomic<_Tp>* __a, >+ typename std::atomic<_Tp>::value_type __old) noexcept >+ { __a->wait(__old); } >+ >+ template<typename _Tp> >+ inline void >+ atomic_wait_explicit(const atomic<_Tp>* __a, >+ typename std::atomic<_Tp>::value_type __old, >+ std::memory_order __m) noexcept >+ { __a->wait(__old, __m); } >+ >+ template<typename _Tp> >+ inline void >+ atomic_notify_one(atomic<_Tp>* __a) noexcept >+ { __a->notify_one(); } >+ >+ template<typename _Tp> >+ inline void >+ atomic_notify_all(atomic<_Tp>* __a) noexcept >+ { __a->notify_all(); } >+ >+#endif // C++2a >+ > // Function templates for atomic_integral and atomic_pointer operations only. > // Some operations (and, or, xor) are only available for atomic integrals, > // which is implemented by taking a parameter of type __atomic_base<_ITp>*. >diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch >new file mode 100644 >index 00000000000..bd06db5aa7f >--- /dev/null >+++ b/libstdc++-v3/include/std/latch >@@ -0,0 +1,90 @@ >+// <latch> -*- C++ -*- >+ >+// Copyright (C) 2020 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 include/latch >+ * This is a Standard C++ Library header. >+ */ >+ >+#ifndef _GLIBCXX_LATCH >+#define _GLIBCXX_LATCH >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_latch 201907L >+ >+#include <bits/atomic_base.h> >+#include <ext/numeric_traits.h> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ class latch >+ { >+ public: >+ static constexpr ptrdiff_t >+ max() noexcept >+ { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } You can use __gnu_cxx::__int_traits<ptrdiff_t> here, since we already know ptrdiff_t is an integer type and so don't need to do the selection between __numeric_traits for integers or floats. >+ constexpr explicit latch(ptrdiff_t __expected) noexcept >+ : _M_a(__expected) { } >+ >+ ~latch() = default; >+ latch(const latch&) = delete; >+ latch& operator=(const latch&) = delete; >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ count_down(ptrdiff_t __update = 1) >+ { >+ auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); This line is too long, >+ if (__old == __update) >+ __atomic_impl::notify_all(&_M_a); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_wait() const noexcept >+ { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ wait() const >+ { >+ auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); This function is not noexcept, but it only calls noexcept functions. That suggests either __atomic_wait should not be noexcept, or this one could be. >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ arrive_and_wait(ptrdiff_t __update = 1) >+ { >+ count_down(); This needs to pass the __udpate parameter to count_down. This suggests we're missing a test using arrive_and_wait(2) or greater values. >+ wait(); >+ } >+ >+ private: >+ alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; >+ }; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_LATCH >diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore >new file mode 100644 >index 00000000000..865d6c4aecb >--- /dev/null >+++ b/libstdc++-v3/include/std/semaphore >@@ -0,0 +1,92 @@ >+// <semaphore> -*- C++ -*- >+ >+// Copyright (C) 2020 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 include/semaphore >+ * This is a Standard C++ Library header. >+ */ >+ >+#ifndef _GLIBCXX_SEMAPHORE >+#define _GLIBCXX_SEMAPHORE >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_semaphore 201907L >+#include <bits/semaphore_base.h> >+#include <ext/numeric_traits.h> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ template<ptrdiff_t __least_max_value = >+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> This can use __gnu_cxx::__int_traits too. >+ class counting_semaphore >+ { >+ static_assert(__least_max_value >= 0); >+ >+ __semaphore_impl<__least_max_value> _M_sem; >+ >+ public: >+ explicit counting_semaphore(ptrdiff_t __desired) noexcept >+ : _M_sem(__desired) >+ { } >+ >+ ~counting_semaphore() = default; >+ >+ counting_semaphore(const counting_semaphore&) = delete; >+ counting_semaphore& operator=(const counting_semaphore&) = delete; >+ >+ static constexpr ptrdiff_t >+ max() noexcept >+ { return __least_max_value; } >+ >+ void >+ release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) >+ { _M_sem._M_release(__update); } >+ >+ void >+ acquire() noexcept(noexcept(_M_sem._M_acquire())) >+ { _M_sem._M_acquire(); } >+ >+ bool >+ try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) >+ { return _M_sem._M_try_acquire(); } >+ >+ template<class _Rep, class _Period> typename, not class >+ bool >+ try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) >+ { return _M_sem._M_try_acquire_for(__rtime); } >+ >+ template<class _Clock, class _Dur> typename, not class >+ bool >+ try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) >+ { return _M_sem._M_try_acquire_until(__atime); } >+ }; >+ >+ using binary_semaphore = std::counting_semaphore<1>; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_SEMAPHORE >diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version >index d5d42ed0a72..b9c7c6c62a8 100644 >--- a/libstdc++-v3/include/std/version >+++ b/libstdc++-v3/include/std/version >@@ -216,12 +216,14 @@ > #ifdef _GLIBCXX_HAS_GTHREADS > # define __cpp_lib_jthread 201911L > #endif >+#define __cpp_lib_latch 201907L > #define __cpp_lib_list_remove_return_type 201806L > #define __cpp_lib_math_constants 201907L > #define __cpp_lib_polymorphic_allocator 201902L > #if __cpp_lib_concepts > # define __cpp_lib_ranges 201911L > #endif >+#define __cpp_lib_semaphore 201907L > #define __cpp_lib_shift 201806L > #define __cpp_lib_span 202002L > #define __cpp_lib_ssize 201902L >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >new file mode 100644 >index 00000000000..1ced9d44b20 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc This file should be under testsuite/29_atomics/atomic_ref somewhere, not under testsuite/29_atomics/atomic >@@ -0,0 +1,103 @@ >+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <chrono> >+#include <type_traits> >+ >+#include <testsuite_hooks.h> >+ >+template<typename Tp> >+Tp check_wait_notify(Tp val1, Tp val2) >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ Tp aa = val1; >+ std::atomic_ref<Tp> a(aa); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(val1); >+ if (a.load() != val2) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ a.notify_one(); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp, >+ bool = std::is_integral_v<Tp> >+ || std::is_floating_point_v<Tp>> >+struct check; >+ >+template<typename Tp> >+struct check<Tp, true> >+{ >+ check() >+ { >+ Tp a = 0; >+ Tp b = 42; >+ VERIFY(check_wait_notify(a, b) == b); >+ } >+}; >+ >+template<typename Tp> >+struct check<Tp, false> >+{ >+ check(Tp b) >+ { >+ Tp a; >+ VERIFY(check_wait_notify(a, b) == b); >+ } >+}; >+ >+struct foo >+{ >+ long a = 0; >+ long b = 0; >+ >+ foo& operator=(foo const&) = default; >+ >+ friend bool >+ operator==(foo const& rhs, foo const& lhs) >+ { return rhs.a == lhs.a && rhs.b == lhs.b; } >+}; >+ >+int >+main () >+{ >+ check<long>(); >+ check<double>(); >+ check<foo>({42, 48}); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >new file mode 100644 >index 00000000000..b9fc063c66f >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >@@ -0,0 +1,59 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <type_traits> >+#include <chrono> >+ >+#include <testsuite_hooks.h> >+ >+int >+main () >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<bool> a(false); >+ std::atomic<bool> b(false); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(false); >+ if (a.load()) >+ { >+ b.store(true); >+ } >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(true); >+ a.notify_one(); >+ t.join(); >+ VERIFY( b.load() ); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >new file mode 100644 >index 00000000000..1d032085752 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc And this should be under testsuite/29_atomics/atomic_float Please take another look at the dir structure under testsuite/29_atomics/ and locate the new tests with the relevant types. testsuite/29_atomics/atomic/ is for the generic std::atomic template. >@@ -0,0 +1,32 @@ >+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include "generic.h" >+ >+int >+main () >+{ >+ check<float> f; >+ check<double> d; >+ check<long double> l; >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc >new file mode 100644 >index 00000000000..d15b9c86ae6 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc >@@ -0,0 +1,31 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include "generic.h" >+ >+int >+main () >+{ >+ struct S{ int i; }; >+ check<S> check_s{S{0},S{42}}; >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >new file mode 100644 >index 00000000000..a319e8b60a6 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >@@ -0,0 +1,160 @@ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <chrono> >+#include <condition_variable> >+#include <concepts> >+#include <mutex> >+#include <thread> >+ >+#include <testsuite_hooks.h> >+ >+#include <iostream> >+ >+template<typename Tp> >+Tp check_wait_notify(Tp val1, Tp val2) >+ requires std::equality_comparable<Tp> >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<Tp> a(val1); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(val1); >+ if (a.load() != val2) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ a.notify_one(); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp> >+Tp check_wait_notify(Tp val1, Tp val2) >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<Tp> a(val1); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(val1); >+ auto v = a.load(); >+ // TODO this needs to zero padding bits when we can do that >+ if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ a.notify_one(); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp> >+Tp check_atomic_wait_notify(Tp val1, Tp val2) >+ requires std::equality_comparable<Tp> >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<Tp> a(val1); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ std::atomic_wait(&a, val1); >+ if (a.load() != val2) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ std::atomic_notify_one(&a); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp> >+Tp check_atomic_wait_notify(Tp val1, Tp val2) >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic<Tp> a(val1); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ std::atomic_wait(&a, val1); >+ auto v = a.load(); >+ // TODO this needs to zero padding bits when we can do that >+ if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) >+ a = val1; >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(val2); >+ std::atomic_notify_one(&a); >+ t.join(); >+ return a.load(); >+} >+ >+template<typename Tp> >+struct check >+{ >+ check(Tp a = 0, Tp b = 42) >+ { >+ if constexpr (std::equality_comparable<Tp>) >+ { >+ VERIFY( check_wait_notify(a, b) == b); >+ VERIFY( check_atomic_wait_notify(a, b) == b); >+ } >+ else >+ { >+ { >+ // TODO this needs to zero padding bits when we can do that >+ auto v = check_wait_notify(a, b); >+ VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); >+ } >+ >+ { >+ // TODO this needs to zero padding bits when we can do that >+ auto v = check_atomic_wait_notify(a, b); >+ VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); >+ } >+ } >+ } >+}; >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >new file mode 100644 >index 00000000000..115cb79a040 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >@@ -0,0 +1,65 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include "generic.h" >+ >+void >+test01() >+{ >+ struct S{ int i; }; >+ std::atomic<S> s; >+ >+ s.wait(S{42}); >+} >+ >+int >+main () >+{ >+ // check<bool> bb; >+ check<char> ch; >+ check<signed char> sch; >+ check<unsigned char> uch; >+ check<short> s; >+ check<unsigned short> us; >+ check<int> i; >+ check<unsigned int> ui; >+ check<long> l; >+ check<unsigned long> ul; >+ check<long long> ll; >+ check<unsigned long long> ull; >+ >+ check<wchar_t> wch; >+ check<char8_t> ch8; >+ check<char16_t> ch16; >+ check<char32_t> ch32; >+ >+ check<int8_t> i8; >+ check<int16_t> i16; >+ check<int32_t> i32; >+ check<int64_t> i64; >+ >+ check<uint8_t> u8; >+ check<uint16_t> u16; >+ check<uint32_t> u32; >+ check<uint64_t> u64; >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >new file mode 100644 >index 00000000000..8531bb2e788 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >@@ -0,0 +1,59 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <thread> >+#include <mutex> >+#include <condition_variable> >+#include <type_traits> >+#include <chrono> >+ >+#include <testsuite_hooks.h> >+ >+int >+main () >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ long aa; >+ long bb; >+ >+ std::atomic<long*> a(nullptr); >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(nullptr); >+ if (a.load() == &aa) >+ a.store(&bb); >+ }); >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.store(&aa); >+ a.notify_one(); >+ t.join(); >+ VERIFY( a.load() == &bb); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc >new file mode 100644 >index 00000000000..6de7873ecc2 >--- /dev/null >+++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc >@@ -0,0 +1,61 @@ >+// { dg-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+// Copyright (C) 2020 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/>. >+ >+#include <atomic> >+#include <chrono> >+#include <condition_variable> >+#include <concepts> >+#include <mutex> >+#include <thread> >+ >+#include <testsuite_hooks.h> >+ >+int >+main() >+{ >+ using namespace std::literals::chrono_literals; >+ >+ std::mutex m; >+ std::condition_variable cv; >+ >+ std::atomic_flag a; >+ std::atomic_flag b; >+ std::thread t([&] >+ { >+ cv.notify_one(); >+ a.wait(false); >+ b.test_and_set(); >+ b.notify_one(); >+ }); >+ >+ std::unique_lock<std::mutex> l(m); >+ cv.wait(l); >+ std::this_thread::sleep_for(100ms); >+ a.test_and_set(); >+ a.notify_one(); >+ b.wait(false); >+ t.join(); >+ >+ VERIFY( a.test() ); >+ VERIFY( b.test() ); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc >new file mode 100644 >index 00000000000..aa203cdf525 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <latch> >+ >+#ifndef __cpp_lib_latch >+# error "Feature-test macro for latch missing in <latch>" >+#elif __cpp_lib_latch!= 201907L >+# error "Feature-test macro for latch has wrong value in <latch>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc >new file mode 100644 >index 00000000000..318a859ee21 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <version> >+ >+#ifndef __cpp_lib_latch >+# error "Feature-test macro for latch missing in <version>" >+#elif __cpp_lib_latch != 201907L >+# error "Feature-test macro for latch has wrong value in <version>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc >new file mode 100644 >index 00000000000..cf1a31f996b >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc >@@ -0,0 +1,50 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+// >+#include <latch> >+#include <atomic> >+#include <thread> >+#include <testsuite_hooks.h> >+ >+int main() >+{ >+ std::atomic<int> a(0); >+ >+ std::latch l(3); >+ >+ VERIFY( !l.try_wait() ); >+ >+ auto fn = [&] >+ { >+ ++a; >+ l.count_down(); >+ }; >+ >+ std::thread t0(fn); >+ std::thread t1(fn); >+ >+ l.arrive_and_wait(); >+ t0.join(); >+ t1.join(); >+ >+ VERIFY( l.try_wait() ); >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc >new file mode 100644 >index 00000000000..1bbca687fc3 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <semaphore> >+ >+#ifndef __cpp_lib_semaphore >+# error "Feature-test macro for semaphore missing in <semaphore>" >+#elif __cpp_lib_semaphore != 201907L >+# error "Feature-test macro for semaphore has wrong value in <semaphore>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc >new file mode 100644 >index 00000000000..98743f5e27c >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc >@@ -0,0 +1,27 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+ >+#include <version> >+ >+#ifndef __cpp_lib_semaphore >+# error "Feature-test macro for semaphore missing in <version>" >+#elif __cpp_lib_semaphore != 201907L >+# error "Feature-test macro for semaphore has wrong value in <version>" >+#endif >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc >new file mode 100644 >index 00000000000..d74cfad53e9 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc >@@ -0,0 +1,30 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a" } >+// { dg-do compile { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+ >+int main() >+{ >+ std::counting_semaphore<-1> sem(2); >+ return 0; >+} >+// { dg-error "static assertion failed" "" { target *-*-* } 0 } >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc >new file mode 100644 >index 00000000000..25280441d07 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc >@@ -0,0 +1,55 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <limits> >+#include <cstddef> >+#include <testsuite_hooks.h> >+ >+void test01() >+{ >+ std::counting_semaphore<10> s(3); >+ >+ s.acquire(); >+ VERIFY( s.try_acquire() ); >+ VERIFY( s.try_acquire() ); >+ VERIFY( !s.try_acquire() ); >+ s.release(); >+ VERIFY( s.try_acquire() ); >+} >+ >+void test02() >+{ >+ std::binary_semaphore s(1); >+ >+ s.acquire(); >+ VERIFY( !s.try_acquire() ); >+ s.release(); >+ VERIFY( s.try_acquire() ); >+} >+ >+ >+int main() >+{ >+ test01(); >+ test02(); >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc >new file mode 100644 >index 00000000000..3f450e74661 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc >@@ -0,0 +1,85 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <chrono> >+#include <thread> >+#include <atomic> >+#include <testsuite_hooks.h> >+ >+void test01() >+{ >+ using namespace std::chrono_literals; >+ std::counting_semaphore<10> s(2); >+ s.acquire(); >+ >+ auto const dur = 250ms; >+ { >+ auto const t0 = std::chrono::steady_clock::now(); >+ VERIFY( s.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.try_acquire_for(dur) ); >+ auto const diff = std::chrono::steady_clock::now() - t0; >+ VERIFY( diff >= dur ); >+ } >+} >+ >+void test02() >+{ >+ using namespace std::chrono_literals; >+ std::binary_semaphore s(1); >+ std::atomic<int> a(0), b(0); >+ std::thread t([&] { >+ a.wait(0); >+ auto const dur = 250ms; >+ VERIFY( !s.try_acquire_for(dur) ); >+ b++; >+ b.notify_one(); >+ >+ a.wait(1); >+ VERIFY( s.try_acquire_for(dur) ); >+ b++; >+ b.notify_one(); >+ }); >+ t.detach(); >+ >+ s.acquire(); >+ a++; >+ a.notify_one(); >+ b.wait(0); >+ s.release(); >+ a++; >+ a.notify_one(); >+ >+ b.wait(1); >+} >+ >+int main() >+{ >+ test01(); >+ test02(); >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc >new file mode 100644 >index 00000000000..13bd7487d56 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc >@@ -0,0 +1,153 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#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; >+} >diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc >new file mode 100644 >index 00000000000..af7ab7bac39 >--- /dev/null >+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc >@@ -0,0 +1,94 @@ >+// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } >+// { dg-do run { target c++2a } } >+// { dg-require-effective-target pthread } >+// { dg-require-gthreads "" } >+ >+#include <semaphore> >+#include <chrono> >+#include <thread> >+#include <atomic> >+#include <testsuite_hooks.h> >+ >+void test01() >+{ >+ using namespace std::chrono_literals; >+ std::counting_semaphore<10> s(2); >+ s.acquire(); >+ >+ auto const dur = 250ms; >+ { >+ auto const at = std::chrono::system_clock::now() + dur; >+ auto const t0 = std::chrono::steady_clock::now(); >+ VERIFY( s.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.try_acquire_until(at) ); >+ auto const diff = std::chrono::steady_clock::now() - t0; >+ VERIFY( diff >= dur ); >+ } >+} >+ >+void test02() >+{ >+ using namespace std::chrono_literals; >+ std::binary_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.try_acquire_until(at) ); >+ >+ b++; >+ b.notify_one(); >+ } >+ >+ a.wait(1); >+ { >+ auto const at = std::chrono::system_clock::now() + dur; >+ VERIFY( s.try_acquire_until(at) ); >+ } >+ b++; >+ b.notify_one(); >+ }); >+ t.detach(); >+ >+ s.acquire(); >+ a++; >+ a.notify_one(); >+ b.wait(0); >+ s.release(); >+ a++; >+ a.notify_one(); >+ >+ b.wait(1); >+} >+ >+int main() >+{ >+ test01(); >+ test02(); >+} >-- >2.26.2 > > ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] libstdc++: Add C++2a synchronization support 2020-09-28 13:25 ` Jonathan Wakely @ 2020-10-01 23:37 ` Thomas Rodgers 2020-10-02 15:40 ` Thomas Rodgers 2020-10-05 22:54 ` [PATCH] t/trodgers/c2a_synchronization Thomas Rodgers 0 siblings, 2 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-10-01 23:37 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers From: Thomas Rodgers <trodgers@redhat.com> Updated patch incorporating latest feedback. Add support for - * atomic_flag::wait/notify_one/notify_all * atomic::wait/notify_one/notify_all * counting_semaphore * binary_semaphore * latch libstdc++-v3/ChangeLog: * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_flag::wait): Define. (__atomic_flag::notify_one): Likewise. (__atomic_flag::notify_all): Likewise. (__atomic_base<_Itp>::wait): Likewise. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ libstdc++-v3/include/std/atomic | 73 +++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 92 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ .../atomic/wait_notify/integrals.cc | 65 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 30 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 28 files changed, 2471 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index c9df9a9d6c6..9b5b6ed0005 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -101,6 +103,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -175,6 +179,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 2cdd2bd6cae..dd4db926592 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __atomic_load(&_M_i, &__v, int(__m)); return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } + + _GLIBCXX_ALWAYS_INLINE void + wait(bool __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->test(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..2f57356b366 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,281 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, + _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags::__wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags::__bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__platform_wait_until_impl(__addr, __val, __s_atime) == + __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) == + __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..21ec3d38c94 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,301 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..ed127a7a953 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,283 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +#include <iostream> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == EINTR)) + continue; + else if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err && (errno == EINVAL)) + return false; // caller supplied an invalid __atime + else if (__err) + std::terminate(); + else + break; + } + 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<__clock_t, _Clock>::value) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_a(__count) + { } + + __atomic_semaphore(const __atomic_semaphore&) = delete; + __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..c15909d9ccb 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +376,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we can do that + return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +635,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1399,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..bd06db5aa7f --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..865d6c4aecb --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<class _Rep, class _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<class _Clock, class _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index d5d42ed0a72..b9c7c6c62a8 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -216,12 +216,14 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..d15b9c86ae6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..115cb79a040 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc new file mode 100644 index 00000000000..6de7873ecc2 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -0,0 +1,61 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +int +main() +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic_flag a; + std::atomic_flag b; + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + b.test_and_set(); + b.notify_one(); + }); + + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.test_and_set(); + a.notify_one(); + b.wait(false); + t.join(); + + VERIFY( a.test() ); + VERIFY( b.test() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..cf1a31f996b --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] libstdc++: Add C++2a synchronization support 2020-10-01 23:37 ` Thomas Rodgers @ 2020-10-02 15:40 ` Thomas Rodgers 2020-10-05 22:54 ` [PATCH] t/trodgers/c2a_synchronization Thomas Rodgers 1 sibling, 0 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-10-02 15:40 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers From: Thomas Rodgers <trodgers@redhat.com> Updated patch incorporating latest feedback (revised). Add support for - * atomic_flag::wait/notify_one/notify_all * atomic::wait/notify_one/notify_all * counting_semaphore * binary_semaphore * latch libstdc++-v3/ChangeLog: * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_flag::wait): Define. (__atomic_flag::notify_one): Likewise. (__atomic_flag::notify_all): Likewise. (__atomic_base<_Itp>::wait): Likewise. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ libstdc++-v3/include/std/atomic | 73 +++++ libstdc++-v3/include/std/latch | 90 ++++++ libstdc++-v3/include/std/semaphore | 92 ++++++ libstdc++-v3/include/std/version | 2 + .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ .../atomic/wait_notify/integrals.cc | 65 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 30 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ 28 files changed, 2471 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index c9df9a9d6c6..9b5b6ed0005 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -101,6 +103,8 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ + ${bits_srcdir}/atomic_timed_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ @@ -175,6 +179,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 2cdd2bd6cae..dd4db926592 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __atomic_load(&_M_i, &__v, int(__m)); return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } + + _GLIBCXX_ALWAYS_INLINE void + wait(bool __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->test(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..2f57356b366 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,281 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point<__platform_wait_clock_t, + _Duration>& __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags::__wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags::__bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__platform_wait_until_impl(__addr, __val, __s_atime) == + __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) == + __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..21ec3d38c94 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,301 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (__e && !(errno == EINTR || errno == EAGAIN)) + std::terminate(); + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + __platform_wait_t alignas(64) _M_ver = 0; + __platform_wait_t alignas(64) _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + __platform_wait_t + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..ed127a7a953 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,283 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +#include <iostream> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == EINTR)) + continue; + else if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err && (errno == EINVAL)) + return false; // caller supplied an invalid __atime + else if (__err) + std::terminate(); + else + break; + } + 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<__clock_t, _Clock>::value) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_a(__count) + { } + + __atomic_semaphore(const __atomic_semaphore&) = delete; + __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + __atomic_wait(&_M_a, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire); + if (__old == 0) + return false; + + return __atomic_spin([this, &__old] + { + return __atomic_impl::compare_exchange_weak(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_until(&_M_a, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_a, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_a, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed); + return __atomic_wait_for(&_M_a, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_a); + else + __atomic_impl::notify_one(&_M_a); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_a; + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..c15909d9ccb 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +376,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we can do that + return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +635,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1399,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..bd06db5aa7f --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,90 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..865d6c4aecb --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__numeric_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<class _Rep, class _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<class _Clock, class _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index d5d42ed0a72..b9c7c6c62a8 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -216,12 +216,14 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..d15b9c86ae6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..115cb79a040 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc new file mode 100644 index 00000000000..6de7873ecc2 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -0,0 +1,61 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +int +main() +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic_flag a; + std::atomic_flag b; + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + b.test_and_set(); + b.notify_one(); + }); + + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.test_and_set(); + a.notify_one(); + b.wait(false); + t.join(); + + VERIFY( a.test() ); + VERIFY( b.test() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..cf1a31f996b --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +int main() +{ + std::atomic<int> a(0); + + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + ++a; + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] t/trodgers/c2a_synchronization 2020-10-01 23:37 ` Thomas Rodgers 2020-10-02 15:40 ` Thomas Rodgers @ 2020-10-05 22:54 ` Thomas Rodgers 2020-10-23 10:28 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-10-05 22:54 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers From: Thomas Rodgers <trodgers@redhat.com> This *should* be the correct patch this time. Add support for - * atomic_flag::wait/notify_one/notify_all * atomic::wait/notify_one/notify_all * counting_semaphore * binary_semaphore * latch libstdc++-v3/ChangeLog: * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_flag::wait): Define. (__atomic_flag::notify_one): Likewise. (__atomic_flag::notify_all): Likewise. (__atomic_base<_Itp>::wait): Likewise. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/bool.cc: New test. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise. * testsuite/29_atomic/atomic_float/wait_notify.cc: Likewise. * testsuite/29_atomic/atomic_integral/wait_notify.cc: Likewise. * testsuite/29_atomic/atomic_ref/wait_notify.cc: Likewise. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. * testsuite/util/atomic/wait_notify_util.h: New File. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 195 ++++++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 288 +++++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 306 +++++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 298 ++++++++++++++++++++ libstdc++-v3/include/std/atomic | 78 ++++++ libstdc++-v3/include/std/latch | 91 ++++++ libstdc++-v3/include/std/semaphore | 92 +++++++ libstdc++-v3/include/std/version | 2 + .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/generic.cc | 31 +++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ .../29_atomics/atomic_float/wait_notify.cc | 32 +++ .../29_atomics/atomic_integral/wait_notify.cc | 65 +++++ .../testsuite/29_atomics/atomic_ref/wait_notify.cc | 103 +++++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 69 +++++ libstdc++-v3/testsuite/30_threads/semaphore/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/semaphore/2.cc | 27 ++ .../30_threads/semaphore/least_max_value_neg.cc | 30 ++ .../testsuite/30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 ++++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 +++++++ .../testsuite/util/atomic/wait_notify_util.h | 160 +++++++++++ 28 files changed, 2523 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc create mode 100644 libstdc++-v3/testsuite/util/atomic/wait_notify_util.h diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index c9df9a9d6c6..784e4426a06 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -102,6 +104,8 @@ bits_headers = \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ ${bits_srcdir}/atomic_futex.h \ + ${bits_srcdir}/atomic_timed_wait.h \ + ${bits_srcdir}/atomic_wait.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ ${bits_srcdir}/basic_string.h \ @@ -175,6 +179,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 2cdd2bd6cae..dd4db926592 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __atomic_load(&_M_i, &__v, int(__m)); return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } + + _GLIBCXX_ALWAYS_INLINE void + wait(bool __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->test(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..cd5b6aabc44 --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,288 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point< + __platform_wait_clock_t, _Duration>& + __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags:: + __wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags:: + __bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& + __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return __detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__detail::__platform_wait_until_impl(__addr, __val, __s_atime) + == __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__detail::__cond_wait_until(&_M_cv, __l, __atime) + == __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& + __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, + __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..30f682e828a --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,306 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + for(;;) + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (!__e) + break; + else if (!(errno == EINTR || errno == EAGAIN)) + __throw_system_error(__e); + } + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + alignas(64) __platform_wait_t _M_ver = 0; + alignas(64) __platform_wait_t _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + __throw_system_error(__e); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + bool + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred& __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..a32a6b12a1b --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,298 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#include <ext/numeric_traits.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +#include <iostream> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == EINTR)) + continue; + else if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err && (errno == EINVAL)) + return false; // caller supplied an invalid __atime + else if (__err) + std::terminate(); + else + break; + } + 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<__clock_t, _Clock>::value) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static_assert(__gnu_cxx::__int_traits<_Tp>::__max + <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __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; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + __atomic_wait(&_M_counter, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire); + auto const __pred = [this, __old] + { + if (__old == 0) + return false; + + auto __prev = __old; + return __atomic_impl::compare_exchange_weak(&this->_M_counter, + __prev, __prev - 1, + memory_order::acquire, + memory_order::release); + }; + return __atomic_spin(__pred); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, + _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + return __atomic_wait_until(&_M_counter, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) + noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + return __atomic_wait_for(&_M_counter, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_counter); + else + __atomic_impl::notify_one(&_M_counter); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_counter; + }; + +// Note: the _GLIBCXX_REQUIRE_POSIX_SEMAPHORE macro can be used to force the +// use of Posix semaphores (sem_t). Doing so however, alters the ABI. +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..5afe33b41d9 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const + noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +377,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } + +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we + // can do that + return __builtin_memcmp(&__old, &__v, + sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +639,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1404,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..9725a68040c --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,91 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__int_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, + __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const noexcept + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) noexcept + { + count_down(__update); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..b4facde4ea1 --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__int_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<typename _Rep, typename _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<typename _Clock, typename _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index d5d42ed0a72..b9c7c6c62a8 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -216,12 +216,14 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..b9fc063c66f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..0249341055c --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "atomic/wait_notify_util.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc new file mode 100644 index 00000000000..6de7873ecc2 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -0,0 +1,61 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +int +main() +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic_flag a; + std::atomic_flag b; + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + b.test_and_set(); + b.notify_one(); + }); + + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.test_and_set(); + a.notify_one(); + b.wait(false); + t.join(); + + VERIFY( a.test() ); + VERIFY( b.test() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc new file mode 100644 index 00000000000..b46eb603304 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "atomic/wait_notify_util.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc new file mode 100644 index 00000000000..6e9ee7dbf93 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "atomic/wait_notify_util.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..5d08000f430 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,69 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +void +test01() +{ + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} + +void +test02() +{ + std::latch l(3); + std::thread t([&] + { + l.count_down(); + }); + + l.arrive_and_wait(2); + t.join(); + VERIFY( l.try_wait() ); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h b/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; -- tg: (64a5fb5f438..) t/trodgers/c2a_synchronization (depends on: master) ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] t/trodgers/c2a_synchronization 2020-10-05 22:54 ` [PATCH] t/trodgers/c2a_synchronization Thomas Rodgers @ 2020-10-23 10:28 ` Jonathan Wakely 2020-10-26 21:48 ` [PATCH] libstdc++: Add C++2a synchronization support Thomas Rodgers 0 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-10-23 10:28 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 05/10/20 15:54 -0700, Thomas Rodgers wrote: >From: Thomas Rodgers <trodgers@redhat.com> > >This *should* be the correct patch this time. There are still some review comments not addressed. I'll keep all my comments on this version in a single reply, so they aren't lost among multiple replies. I think the significant issues like the handling of spurious wakeups and the missing "no effects" case in _M_try_acquire() are resolved, thanks. >diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >new file mode 100644 >index 00000000000..cd5b6aabc44 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h [...] >+ struct __timed_waiters : __waiters >+ { >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ _M_do_wait_until(__platform_wait_t __version, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ return __platform_wait_until(&_M_ver, __version, __atime); Qualify to prevent ADL in associated namespaces of the _Clock and _Duration types. >+ template<typename _Tp, typename _Pred, >+ typename _Clock, typename _Duration> >+ bool >+ __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, >+ const chrono::time_point<_Clock, _Duration>& >+ __atime) noexcept >+ { >+ using namespace __detail; >+ >+ if (std::__atomic_spin(__pred)) >+ return true; >+ >+ auto& __w = __timed_waiters::_S_timed_for((void*)__addr); >+ auto __version = __w._M_enter_wait(); >+ do >+ { >+ __atomic_wait_status __res; >+ if constexpr (__platform_wait_uses_type<_Tp>) >+ { >+ __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, Qualify to prevent ADL. >diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h >new file mode 100644 >index 00000000000..30f682e828a >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_wait.h >+ template<typename _Tp, typename _Pred> >+ void >+ __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >+ { >+ using namespace __detail; >+ if (__atomic_spin(__pred)) Qualify to prevent ADL. >diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >new file mode 100644 >index 00000000000..a32a6b12a1b >--- /dev/null >+++ b/libstdc++-v3/include/bits/semaphore_base.h >@@ -0,0 +1,298 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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/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 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/atomic_base.h> >+#include <bits/atomic_timed_wait.h> >+ >+#include <ext/numeric_traits.h> >+ >+#if __has_include(<semaphore.h>) >+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >+#include <semaphore.h> >+#endif >+ >+#include <chrono> >+#include <type_traits> >+ >+#include <iostream> This shouldn't be here. >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE >+ struct __platform_semaphore >+ { >+ using __clock_t = chrono::system_clock; >+ static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; >+ >+ 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 >+ { >+ for (;;) >+ { >+ auto __err = sem_wait(&_M_semaphore); >+ if (__err && (errno == EINTR)) >+ continue; >+ else if (__err) >+ std::terminate(); >+ else >+ break; >+ } >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ _M_release(std::ptrdiff_t __update) noexcept >+ { >+ for(; __update != 0; --__update) >+ { >+ auto __err = sem_post(&_M_semaphore); >+ if (__err) >+ 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); >+ >+ struct timespec __ts = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ for (;;) >+ { >+ auto __err = sem_timedwait(&_M_semaphore, &__ts); >+ if (__err && (errno == EINTR)) >+ continue; >+ else if (__err && (errno == ETIMEDOUT)) >+ return false; >+ else if (__err && (errno == EINVAL)) >+ return false; // caller supplied an invalid __atime >+ else if (__err) >+ std::terminate(); >+ else >+ break; It looks like this would be simpler with just one check for __err: auto __err = sem_timedwait(&_M_semaphore, &__ts); if (__err) { if (errno == EINTR) continue; if (errno == ETIMEDOUT || errno == EINVAL) return false; else std::terminate(); } else break; Hopefully the compiler wouldn't actually keep testing __err but I think it reads better this way anyway. >+ } >+ 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<__clock_t, _Clock>::value) is_same_v (it's not just stylistic, the variable template is faster to compile than the class template). >+ { >+ return _M_try_acquire_until_impl(__atime); >+ } >+ else >+ { >+ const typename _Clock::time_point __c_entry = _Clock::now(); >+ const __clock_t __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; >+ >+ // 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 >+ >+ template<typename _Tp> >+ struct __atomic_semaphore >+ { >+ static_assert(std::is_integral_v<_Tp>); >+ static_assert(__gnu_cxx::__int_traits<_Tp>::__max >+ <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); >+ static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; >+ >+ explicit __atomic_semaphore(_Tp __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; >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ _M_acquire() noexcept >+ { >+ auto const __pred = [this] >+ { >+ auto __old = __atomic_impl::load(&this->_M_counter, >+ memory_order::acquire); >+ if (__old == 0) >+ return false; >+ return __atomic_impl::compare_exchange_strong(&this->_M_counter, >+ __old, __old - 1, >+ memory_order::acquire, >+ memory_order::release); >+ }; >+ auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); >+ __atomic_wait(&_M_counter, __old, __pred); Qualify to prevent ADL. >--- /dev/null >+++ b/libstdc++-v3/include/std/latch >@@ -0,0 +1,91 @@ >+// <latch> -*- C++ -*- >+ >+// Copyright (C) 2020 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 include/latch >+ * This is a Standard C++ Library header. >+ */ >+ >+#ifndef _GLIBCXX_LATCH >+#define _GLIBCXX_LATCH >+ >+#pragma GCC system_header >+ >+#if __cplusplus > 201703L >+#define __cpp_lib_latch 201907L >+ >+#include <bits/atomic_base.h> >+#include <ext/numeric_traits.h> >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ class latch >+ { >+ public: >+ static constexpr ptrdiff_t >+ max() noexcept >+ { return __gnu_cxx::__int_traits<ptrdiff_t>::__max; } >+ >+ constexpr explicit latch(ptrdiff_t __expected) noexcept >+ : _M_a(__expected) { } >+ >+ ~latch() = default; >+ latch(const latch&) = delete; >+ latch& operator=(const latch&) = delete; >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ count_down(ptrdiff_t __update = 1) >+ { >+ auto const __old = __atomic_impl::fetch_sub(&_M_a, >+ __update, memory_order::release); >+ if (__old == __update) >+ __atomic_impl::notify_all(&_M_a); >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE bool >+ try_wait() const noexcept >+ { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ wait() const noexcept >+ { >+ auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); >+ __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); Qualify to prevent ADL. >+ } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ arrive_and_wait(ptrdiff_t __update = 1) noexcept >+ { >+ count_down(__update); >+ wait(); >+ } >+ >+ private: >+ alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; >+ }; >+_GLIBCXX_END_NAMESPACE_VERSION >+} // namespace >+#endif // __cplusplus > 201703L >+#endif // _GLIBCXX_LATCH ^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] libstdc++: Add C++2a synchronization support 2020-10-23 10:28 ` Jonathan Wakely @ 2020-10-26 21:48 ` Thomas Rodgers 2020-10-27 10:23 ` Jonathan Wakely 2020-11-21 15:16 ` Andreas Schwab 0 siblings, 2 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-10-26 21:48 UTC (permalink / raw) To: gcc-patches, libstdc++; +Cc: trodgers From: Thomas Rodgers <trodgers@redhat.com> Add support for - * atomic_flag::wait/notify_one/notify_all * atomic::wait/notify_one/notify_all * counting_semaphore * binary_semaphore * latch libstdc++-v3/ChangeLog: * include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_flag::wait): Define. (__atomic_flag::notify_one): Likewise. (__atomic_flag::notify_all): Likewise. (__atomic_base<_Itp>::wait): Likewise. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/bits/atomic_timed_wait.h: New file. * include/bits/semaphore_base.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * include/std/latch: New file. * include/std/semaphore: New file. * include/std/version: Add __cpp_lib_semaphore and __cpp_lib_latch defines. * testsuite/29_atomic/atomic/wait_notify/bool.cc: New test. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise. * testsuite/29_atomic/atomic_float/wait_notify.cc: Likewise. * testsuite/29_atomic/atomic_integral/wait_notify.cc: Likewise. * testsuite/29_atomic/atomic_ref/wait_notify.cc: Likewise. * testsuite/30_thread/semaphore/1.cc: New test. * testsuite/30_thread/semaphore/2.cc: Likewise. * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. * testsuite/30_thread/latch/1.cc: New test. * testsuite/30_thread/latch/2.cc: New test. * testsuite/30_thread/latch/3.cc: New test. * testsuite/util/atomic/wait_notify_util.h: New File. --- libstdc++-v3/include/Makefile.am | 5 + libstdc++-v3/include/Makefile.in | 5 + libstdc++-v3/include/bits/atomic_base.h | 195 ++++++++++- libstdc++-v3/include/bits/atomic_timed_wait.h | 287 ++++++++++++++++ libstdc++-v3/include/bits/atomic_wait.h | 306 ++++++++++++++++++ libstdc++-v3/include/bits/semaphore_base.h | 296 +++++++++++++++++ libstdc++-v3/include/std/atomic | 78 +++++ libstdc++-v3/include/std/latch | 91 ++++++ libstdc++-v3/include/std/semaphore | 92 ++++++ libstdc++-v3/include/std/version | 2 + .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ .../29_atomics/atomic_float/wait_notify.cc | 32 ++ .../29_atomics/atomic_integral/wait_notify.cc | 65 ++++ .../29_atomics/atomic_ref/wait_notify.cc | 103 ++++++ libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ libstdc++-v3/testsuite/30_threads/latch/3.cc | 69 ++++ .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../semaphore/least_max_value_neg.cc | 30 ++ .../30_threads/semaphore/try_acquire.cc | 55 ++++ .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ .../testsuite/util/atomic/wait_notify_util.h | 160 +++++++++ 28 files changed, 2520 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/include/bits/semaphore_base.h create mode 100644 libstdc++-v3/include/std/latch create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc create mode 100644 libstdc++-v3/testsuite/util/atomic/wait_notify_util.h diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index c90ac555e15..382e94322c1 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -52,6 +52,7 @@ std_headers = \ ${std_srcdir}/iostream \ ${std_srcdir}/istream \ ${std_srcdir}/iterator \ + ${std_srcdir}/latch \ ${std_srcdir}/limits \ ${std_srcdir}/list \ ${std_srcdir}/locale \ @@ -69,6 +70,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ @@ -102,6 +104,8 @@ bits_headers = \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ ${bits_srcdir}/atomic_futex.h \ + ${bits_srcdir}/atomic_timed_wait.h \ + ${bits_srcdir}/atomic_wait.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ ${bits_srcdir}/basic_string.h \ @@ -177,6 +181,7 @@ bits_headers = \ ${bits_srcdir}/regex_compiler.tcc \ ${bits_srcdir}/regex_executor.h \ ${bits_srcdir}/regex_executor.tcc \ + ${bits_srcdir}/semaphore_base.h \ ${bits_srcdir}/shared_ptr.h \ ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 2cdd2bd6cae..dd4db926592 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,10 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __atomic_load(&_M_i, &__v, int(__m)); return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } + + _GLIBCXX_ALWAYS_INLINE void + wait(bool __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old]() + { return this->test(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(&_M_p, __old, + [__m, this, __old]() + { return this->load(__m) != __old; }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { std::__atomic_notify(&_M_p, false); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { std::__atomic_notify(&_M_p, true); } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, + memory_order __m = memory_order_seq_cst) noexcept + { + std::__atomic_wait(__ptr, __old, + [=]() { return load(__ptr, __m) == __old; }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + private: _Tp* _M_ptr; }; @@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + + // TODO add const volatile overload + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h new file mode 100644 index 00000000000..7712a6c591d --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -0,0 +1,287 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/atomic_wait.h> + +#include <chrono> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <sys/time.h> +#endif + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + enum class __atomic_wait_status { no_timeout, timeout }; + + namespace __detail + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + using __platform_wait_clock_t = chrono::steady_clock; + + template<typename _Duration> + __atomic_wait_status + __platform_wait_until_impl(__platform_wait_t* __addr, + __platform_wait_t __val, + const chrono::time_point< + __platform_wait_clock_t, _Duration>& + __atime) noexcept + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + struct timespec __rt = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + auto __e = syscall (SYS_futex, __addr, + static_cast<int>(__futex_wait_flags:: + __wait_bitset_private), + __val, &__rt, nullptr, + static_cast<int>(__futex_wait_flags:: + __bitset_match_any)); + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) + std::terminate(); + return (__platform_wait_clock_t::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + template<typename _Clock, typename _Duration> + __atomic_wait_status + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, + const chrono::time_point<_Clock, _Duration>& + __atime) + { + if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) + { + return __detail::__platform_wait_until_impl(__addr, __val, __atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __platform_wait_clock_t::time_point __s_entry = + __platform_wait_clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (__detail::__platform_wait_until_impl(__addr, __val, __s_atime) + == __atomic_wait_status::no_timeout) + return __atomic_wait_status::no_timeout; + + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + } +#endif + +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<mutex>& __lock, + const chrono::time_point<chrono::steady_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(), + CLOCK_MONOTONIC, + &__ts); + return (chrono::steady_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } +#endif + + template<typename _Duration> + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<chrono::system_clock, _Duration>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(), + &__ts); + return (chrono::system_clock::now() < __atime) + ? __atomic_wait_status::no_timeout + : __atomic_wait_status::timeout; + } + + // return true if timeout + template<typename _Clock, typename _Duration> + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock<std::mutex>& __lock, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT + using __clock_t = chrono::steady_clock; +#else + using __clock_t = chrono::system_clock; +#endif + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t::time_point __s_entry = __clock_t::now(); + const auto __delta = __atime - __c_entry; + const auto __s_atime = __s_entry + __delta; + if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime)) + return __atomic_wait_status::no_timeout; + // 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. + if (_Clock::now() < __atime) + return __atomic_wait_status::no_timeout; + return __atomic_wait_status::timeout; + } + + struct __timed_waiters : __waiters + { + template<typename _Clock, typename _Duration> + __atomic_wait_status + _M_do_wait_until(__platform_wait_t __version, + const chrono::time_point<_Clock, _Duration>& __atime) + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + return __detail::__platform_wait_until(&_M_ver, __version, __atime); +#else + __platform_wait_t __cur = 0; + __waiters::__lock_t __l(_M_mtx); + while (__cur <= __version) + { + if (__detail::__cond_wait_until(&_M_cv, __l, __atime) + == __atomic_wait_status::timeout) + return __atomic_wait_status::timeout; + + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } + return __atomic_wait_status::no_timeout; +#endif + } + + static __timed_waiters& + _S_timed_for(void* __t) + { + static_assert(sizeof(__timed_waiters) == sizeof(__waiters)); + return static_cast<__timed_waiters&>(__waiters::_S_for(__t)); + } + }; + } // namespace __detail + + template<typename _Tp, typename _Pred, + typename _Clock, typename _Duration> + bool + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::time_point<_Clock, _Duration>& + __atime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + auto& __w = __timed_waiters::_S_timed_for((void*)__addr); + auto __version = __w._M_enter_wait(); + do + { + __atomic_wait_status __res; + if constexpr (__platform_wait_uses_type<_Tp>) + { + __res = __detail::__platform_wait_until((__platform_wait_t*)(void*) __addr, + __old, __atime); + } + else + { + __res = __w._M_do_wait_until(__version, __atime); + } + if (__res == __atomic_wait_status::timeout) + return false; + } + while (!__pred() && __atime < _Clock::now()); + __w._M_leave_wait(); + + // if timed out, return false + return (_Clock::now() < __atime); + } + + template<typename _Tp, typename _Pred, + typename _Rep, typename _Period> + bool + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred, + const chrono::duration<_Rep, _Period>& __rtime) noexcept + { + using namespace __detail; + + if (std::__atomic_spin(__pred)) + return true; + + if (!__rtime.count()) + return false; // no rtime supplied, and spin did not acquire + + using __dur = chrono::steady_clock::duration; + auto __reltime = chrono::duration_cast<__dur>(__rtime); + if (__reltime < __rtime) + ++__reltime; + + + return __atomic_wait_until(__addr, __old, std::move(__pred), + chrono::steady_clock::now() + __reltime); + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..2d08e5325fb --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,306 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#include <ext/numeric_traits.h> + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + constexpr auto __atomic_spin_count_1 = 16; + constexpr auto __atomic_spin_count_2 = 12; + + inline constexpr + auto __platform_wait_max_value = + __gnu_cxx::__int_traits<__platform_wait_t>::__max; + + template<typename _Tp> + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>; +#else + = false; +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum class __futex_wait_flags : int + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __private_flag = 128, +#else + __private_flag = 0, +#endif + __wait = 0, + __wake = 1, + __wait_bitset = 9, + __wake_bitset = 10, + __wait_private = __wait | __private_flag, + __wake_private = __wake | __private_flag, + __wait_bitset_private = __wait_bitset | __private_flag, + __wake_bitset_private = __wake_bitset | __private_flag, + __bitset_match_any = -1 + }; + + template<typename _Tp> + void + __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept + { + for(;;) + { + auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wait_private), + __val, nullptr); + if (!__e) + break; + else if (!(errno == EINTR || errno == EAGAIN)) + __throw_system_error(__e); + } + } + + template<typename _Tp> + void + __platform_notify(const _Tp* __addr, bool __all) noexcept + { + syscall (SYS_futex, static_cast<const void*>(__addr), + static_cast<int>(__futex_wait_flags::__wake_private), + __all ? INT_MAX : 1); + } +#endif + + struct __waiters + { + alignas(64) __platform_wait_t _M_ver = 0; + alignas(64) __platform_wait_t _M_wait = 0; + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +# ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +# else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +# endif +#endif + + __platform_wait_t + _M_enter_wait() noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(__platform_wait_t __version) noexcept + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_wait(&_M_ver, __version); +#else + __platform_wait_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + __throw_system_error(__e); + __platform_wait_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + if (__cur < __last) + break; // break the loop if version overflows + } +#endif + } + + bool + _M_waiting() const noexcept + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + __platform_notify(&_M_ver, __all); +#else + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); +#endif + } + + static __waiters& + _S_for(const void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + struct __waiter + { + __waiters& _M_w; + __platform_wait_t _M_version; + + template<typename _Tp> + __waiter(const _Tp* __addr) noexcept + : _M_w(__waiters::_S_for(static_cast<const void*>(__addr))) + , _M_version(_M_w._M_enter_wait()) + { } + + ~__waiter() + { _M_w._M_leave_wait(); } + + void _M_do_wait() noexcept + { _M_w._M_do_wait(_M_version); } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + } // namespace __detail + + template<typename _Pred> + bool + __atomic_spin(_Pred& __pred) noexcept + { + for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i) + { + if (__pred()) + return true; + + if (__i < __detail::__atomic_spin_count_2) + __detail::__thread_relax(); + else + __detail::__thread_yield(); + } + return false; + } + + template<typename _Tp, typename _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + using namespace __detail; + if (std::__atomic_spin(__pred)) + return; + + __waiter __w(__addr); + while (!__pred()) + { + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_wait(__addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(); + } + } + } + + template<typename _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + using namespace __detail; + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h new file mode 100644 index 00000000000..da6dc4b9185 --- /dev/null +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -0,0 +1,296 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/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 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/atomic_base.h> +#include <bits/atomic_timed_wait.h> + +#include <ext/numeric_traits.h> + +#if __has_include(<semaphore.h>) +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include <semaphore.h> +#endif + +#include <chrono> +#include <type_traits> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + struct __platform_semaphore + { + using __clock_t = chrono::system_clock; + static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX; + + 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 + { + for (;;) + { + auto __err = sem_wait(&_M_semaphore); + if (__err && (errno == EINTR)) + continue; + else if (__err) + std::terminate(); + else + break; + } + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(std::ptrdiff_t __update) noexcept + { + for(; __update != 0; --__update) + { + auto __err = sem_post(&_M_semaphore); + if (__err) + 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); + + struct timespec __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + for (;;) + { + if (auto __err = sem_timedwait(&_M_semaphore, &__ts)) + { + if (errno == EINTR) + continue; + else if (errno == ETIMEDOUT || errno == EINVAL) + return false; + else + std::terminate(); + } + else + break; + } + 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>) + { + return _M_try_acquire_until_impl(__atime); + } + else + { + const typename _Clock::time_point __c_entry = _Clock::now(); + const __clock_t __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; + + // 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 + + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static_assert(__gnu_cxx::__int_traits<_Tp>::__max + <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __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; + + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + std::__atomic_wait(&_M_counter, __old, __pred); + } + + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire); + auto const __pred = [this, __old] + { + if (__old == 0) + return false; + + auto __prev = __old; + return __atomic_impl::compare_exchange_weak(&this->_M_counter, + __prev, __prev - 1, + memory_order::acquire, + memory_order::release); + }; + return std::__atomic_spin(__pred); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, + _Duration>& __atime) noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + return __atomic_wait_until(&_M_counter, __old, __pred, __atime); + } + + template<typename _Rep, typename _Period> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) + noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + return __atomic_wait_for(&_M_counter, __old, __pred, __rtime); + } + + _GLIBCXX_ALWAYS_INLINE void + _M_release(ptrdiff_t __update) noexcept + { + if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release)) + return; + if (__update > 1) + __atomic_impl::notify_all(&_M_counter); + else + __atomic_impl::notify_one(&_M_counter); + } + + private: + alignas(__alignof__(_Tp)) _Tp _M_counter; + }; + +// Note: the _GLIBCXX_REQUIRE_POSIX_SEMAPHORE macro can be used to force the +// use of Posix semaphores (sem_t). Doing so however, alters the ABI. +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE + // Use futex if available and didn't force use of POSIX + using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>; +#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE + using __fast_semaphore = __platform_semaphore; +#else + using __fast_semaphore = __atomic_semaphore<ptrdiff_t>; +#endif + +template<ptrdiff_t __least_max_value> + using __semaphore_impl = conditional_t< + (__least_max_value > 1), + conditional_t< + (__least_max_value <= __fast_semaphore::_S_max), + __fast_semaphore, + __atomic_semaphore<ptrdiff_t>>, + __fast_semaphore>; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1a304261fe7..5afe33b41d9 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const + noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -363,6 +377,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } + +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { + std::__atomic_wait(&_M_i, __old, + [__m, this, __old] + { + const auto __v = this->load(__m); + // TODO make this ignore padding bits when we + // can do that + return __builtin_memcmp(&__old, &__v, + sizeof(_Tp)) != 0; + }); + } + + // TODO add const volatile overload + + void notify_one() const noexcept + { std::__atomic_notify(&_M_i, false); } + + void notify_all() const noexcept + { std::__atomic_notify(&_M_i, true); } +#endif + }; #undef _GLIBCXX20_INIT @@ -601,6 +639,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, + memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1353,6 +1404,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void + atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void + atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void + atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void + atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch new file mode 100644 index 00000000000..9d9c880a88b --- /dev/null +++ b/libstdc++-v3/include/std/latch @@ -0,0 +1,91 @@ +// <latch> -*- C++ -*- + +// Copyright (C) 2020 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 include/latch + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_LATCH +#define _GLIBCXX_LATCH + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_latch 201907L + +#include <bits/atomic_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + class latch + { + public: + static constexpr ptrdiff_t + max() noexcept + { return __gnu_cxx::__int_traits<ptrdiff_t>::__max; } + + constexpr explicit latch(ptrdiff_t __expected) noexcept + : _M_a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + _GLIBCXX_ALWAYS_INLINE void + count_down(ptrdiff_t __update = 1) + { + auto const __old = __atomic_impl::fetch_sub(&_M_a, + __update, memory_order::release); + if (__old == __update) + __atomic_impl::notify_all(&_M_a); + } + + _GLIBCXX_ALWAYS_INLINE bool + try_wait() const noexcept + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; } + + _GLIBCXX_ALWAYS_INLINE void + wait() const noexcept + { + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire); + std::__atomic_wait(&_M_a, __old, [this] { return this->try_wait(); }); + } + + _GLIBCXX_ALWAYS_INLINE void + arrive_and_wait(ptrdiff_t __update = 1) noexcept + { + count_down(__update); + wait(); + } + + private: + alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_LATCH diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..b4facde4ea1 --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,92 @@ +// <semaphore> -*- C++ -*- + +// Copyright (C) 2020 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 include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#pragma GCC system_header + +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L +#include <bits/semaphore_base.h> +#include <ext/numeric_traits.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<ptrdiff_t __least_max_value = + __gnu_cxx::__int_traits<ptrdiff_t>::__max> + class counting_semaphore + { + static_assert(__least_max_value >= 0); + + __semaphore_impl<__least_max_value> _M_sem; + + public: + explicit counting_semaphore(ptrdiff_t __desired) noexcept + : _M_sem(__desired) + { } + + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + static constexpr ptrdiff_t + max() noexcept + { return __least_max_value; } + + void + release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1))) + { _M_sem._M_release(__update); } + + void + acquire() noexcept(noexcept(_M_sem._M_acquire())) + { _M_sem._M_acquire(); } + + bool + try_acquire() noexcept(noexcept(_M_sem._M_try_acquire())) + { return _M_sem._M_try_acquire(); } + + template<typename _Rep, typename _Period> + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime) + { return _M_sem._M_try_acquire_for(__rtime); } + + template<typename _Clock, typename _Dur> + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime) + { return _M_sem._M_try_acquire_until(__atime); } + }; + + using binary_semaphore = std::counting_semaphore<1>; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // __cplusplus > 201703L +#endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index ebb50a04d24..dcfbbb9ee54 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -216,6 +216,7 @@ #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201911L #endif +#define __cpp_lib_latch 201907L #define __cpp_lib_list_remove_return_type 201806L #if __cpp_lib_concepts # define __cpp_lib_make_obj_using_allocator 201811L @@ -225,6 +226,7 @@ #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif +#define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..5f1e30a710f --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc new file mode 100644 index 00000000000..0249341055c --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -0,0 +1,31 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "atomic/wait_notify_util.h" + +int +main () +{ + struct S{ int i; }; + check<S> check_s{S{0},S{42}}; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc new file mode 100644 index 00000000000..4f026e1dc9c --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -0,0 +1,61 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +int +main() +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic_flag a; + std::atomic_flag b; + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + b.test_and_set(); + b.notify_one(); + }); + + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.test_and_set(); + a.notify_one(); + b.wait(false); + t.join(); + + VERIFY( a.test() ); + VERIFY( b.test() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc new file mode 100644 index 00000000000..b46eb603304 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "atomic/wait_notify_util.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc new file mode 100644 index 00000000000..6e9ee7dbf93 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc @@ -0,0 +1,65 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "atomic/wait_notify_util.h" + +void +test01() +{ + struct S{ int i; }; + std::atomic<S> s; + + s.wait(S{42}); +} + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc new file mode 100644 index 00000000000..aa203cdf525 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <latch> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <latch>" +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in <latch>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc new file mode 100644 index 00000000000..318a859ee21 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in <version>" +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc new file mode 100644 index 00000000000..5d08000f430 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -0,0 +1,69 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include <latch> +#include <atomic> +#include <thread> +#include <testsuite_hooks.h> + +void +test01() +{ + std::latch l(3); + + VERIFY( !l.try_wait() ); + + auto fn = [&] + { + l.count_down(); + }; + + std::thread t0(fn); + std::thread t1(fn); + + l.arrive_and_wait(); + t0.join(); + t1.join(); + + VERIFY( l.try_wait() ); +} + +void +test02() +{ + std::latch l(3); + std::thread t([&] + { + l.count_down(); + }); + + l.arrive_and_wait(2); + t.join(); + VERIFY( l.try_wait() ); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <semaphore> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <semaphore>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <semaphore>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..98743f5e27c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <version> + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in <version>" +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc new file mode 100644 index 00000000000..d74cfad53e9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -0,0 +1,30 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> + +int main() +{ + std::counting_semaphore<-1> sem(2); + return 0; +} +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..25280441d07 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <limits> +#include <cstddef> +#include <testsuite_hooks.h> + +void test01() +{ + std::counting_semaphore<10> s(3); + + s.acquire(); + VERIFY( s.try_acquire() ); + VERIFY( s.try_acquire() ); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + +void test02() +{ + std::binary_semaphore s(1); + + s.acquire(); + VERIFY( !s.try_acquire() ); + s.release(); + VERIFY( s.try_acquire() ); +} + + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..3f450e74661 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_for(dur) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_semaphore s(1); + std::atomic<int> a(0), b(0); + std::thread t([&] { + a.wait(0); + auto const dur = 250ms; + VERIFY( !s.try_acquire_for(dur) ); + b++; + b.notify_one(); + + a.wait(1); + VERIFY( s.try_acquire_for(dur) ); + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc new file mode 100644 index 00000000000..13bd7487d56 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#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; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..af7ab7bac39 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2020 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-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <semaphore> +#include <chrono> +#include <thread> +#include <atomic> +#include <testsuite_hooks.h> + +void test01() +{ + using namespace std::chrono_literals; + std::counting_semaphore<10> s(2); + s.acquire(); + + auto const dur = 250ms; + { + auto const at = std::chrono::system_clock::now() + dur; + auto const t0 = std::chrono::steady_clock::now(); + VERIFY( s.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.try_acquire_until(at) ); + auto const diff = std::chrono::steady_clock::now() - t0; + VERIFY( diff >= dur ); + } +} + +void test02() +{ + using namespace std::chrono_literals; + std::binary_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.try_acquire_until(at) ); + + b++; + b.notify_one(); + } + + a.wait(1); + { + auto const at = std::chrono::system_clock::now() + dur; + VERIFY( s.try_acquire_until(at) ); + } + b++; + b.notify_one(); + }); + t.detach(); + + s.acquire(); + a++; + a.notify_one(); + b.wait(0); + s.release(); + a++; + a.notify_one(); + + b.wait(1); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h b/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h new file mode 100644 index 00000000000..a319e8b60a6 --- /dev/null +++ b/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h @@ -0,0 +1,160 @@ +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <concepts> +#include <mutex> +#include <thread> + +#include <testsuite_hooks.h> + +#include <iostream> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) + requires std::equality_comparable<Tp> +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + auto v = a.load(); + // TODO this needs to zero padding bits when we can do that + if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check(Tp a = 0, Tp b = 42) + { + if constexpr (std::equality_comparable<Tp>) + { + VERIFY( check_wait_notify(a, b) == b); + VERIFY( check_atomic_wait_notify(a, b) == b); + } + else + { + { + // TODO this needs to zero padding bits when we can do that + auto v = check_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 ); + } + + { + // TODO this needs to zero padding bits when we can do that + auto v = check_atomic_wait_notify(a, b); + VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0); + } + } + } +}; -- 2.26.2 ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-10-26 21:48 ` [PATCH] libstdc++: Add C++2a synchronization support Thomas Rodgers @ 2020-10-27 10:23 ` Jonathan Wakely 2020-11-20 22:44 ` Thomas Rodgers 2020-11-21 15:16 ` Andreas Schwab 1 sibling, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-10-27 10:23 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 26/10/20 14:48 -0700, Thomas Rodgers wrote: >+#include <ext/numeric_traits.h> >+ >+#if __has_include(<semaphore.h>) >+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >+#include <semaphore.h> It occurs to me now that this check probably isn't robust enough. For any POSIX system it's probably safe to assume that <semaphore.h> means the POSIX header and so sem_t is available. But on non-POSIX systems there could be some other, unrelated header called <semaphore.h> in the include paths that the user is compiling this header with. It's not inconceivable that the user's own project or some third party lib could provide a file called semaphore.h, which wouldn't define sem_t, sem_init etc. It's OK for now, but we should revisit this and add an autoconf check for sem_init etc. to check at build time whether we've got POSIX semaphores available or not. Please add a "FIXME: replace this with an autoconf check" comment here. OK for trunk with that change, thanks. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-10-27 10:23 ` Jonathan Wakely @ 2020-11-20 22:44 ` Thomas Rodgers 2020-11-22 21:13 ` Stephan Bergmann 2020-11-22 21:41 ` Stephan Bergmann 0 siblings, 2 replies; 50+ messages in thread From: Thomas Rodgers @ 2020-11-20 22:44 UTC (permalink / raw) To: Jonathan Wakely; +Cc: gcc-patches List, libstdc++, trodgers Tested x86_64-pc-linux-gnu, committed. > On Oct 27, 2020, at 3:23 AM, Jonathan Wakely <jwakely@redhat.com> wrote: > > On 26/10/20 14:48 -0700, Thomas Rodgers wrote: >> +#include <ext/numeric_traits.h> >> + >> +#if __has_include(<semaphore.h>) >> +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >> +#include <semaphore.h> > > It occurs to me now that this check probably isn't robust enough. For > any POSIX system it's probably safe to assume that <semaphore.h> means > the POSIX header and so sem_t is available. > > But on non-POSIX systems there could be some other, unrelated header > called <semaphore.h> in the include paths that the user is compiling > this header with. It's not inconceivable that the user's own project > or some third party lib could provide a file called semaphore.h, which > wouldn't define sem_t, sem_init etc. > > It's OK for now, but we should revisit this and add an autoconf check > for sem_init etc. to check at build time whether we've got POSIX > semaphores available or not. > > Please add a "FIXME: replace this with an autoconf check" comment > here. > > OK for trunk with that change, thanks. > ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-20 22:44 ` Thomas Rodgers @ 2020-11-22 21:13 ` Stephan Bergmann 2020-11-23 18:33 ` Jonathan Wakely 2020-11-22 21:41 ` Stephan Bergmann 1 sibling, 1 reply; 50+ messages in thread From: Stephan Bergmann @ 2020-11-22 21:13 UTC (permalink / raw) To: Thomas Rodgers; +Cc: trodgers, libstdc++, gcc-patches List, Jonathan Wakely On 20/11/2020 23:44, Thomas Rodgers wrote: > Tested x86_64-pc-linux-gnu, committed. Clang complains: > $ cat test.cc > #include <semaphore> > > $ clang++ --gcc-toolchain=~/gcc/trunk/inst -std=c++20 -fsyntax-only test.cc > In file included from test.cc:1: > In file included from ~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/semaphore:36: > ~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/semaphore_base.h:145:22: error: no viable conversion from 'std::chrono::system_clock::time_point' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<1, 1000000000>>>') to 'const std::__platform_semaphore::__clock_t' (aka 'const std::chrono::system_clock') > const __clock_t __s_entry = __clock_t::now(); > ^ ~~~~~~~~~~~~~~~~ > ~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/chrono:1101:12: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::chrono::system_clock::time_point' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<1, 1000000000>>>') to 'const std::chrono::system_clock &' for 1st argument > struct system_clock > ^ > ~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/chrono:1101:12: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::chrono::system_clock::time_point' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<1, 1000000000>>>') to 'std::chrono::system_clock &&' for 1st argument > 1 error generated. which > diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h > index 78a0b6ba26e..f25c9fdb325 100644 > --- a/libstdc++-v3/include/bits/semaphore_base.h > +++ b/libstdc++-v3/include/bits/semaphore_base.h > @@ -142,7 +142,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > else > { > const typename _Clock::time_point __c_entry = _Clock::now(); > - const __clock_t __s_entry = __clock_t::now(); > + const __clock_t::time_point __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)) > ~ would fix. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 21:13 ` Stephan Bergmann @ 2020-11-23 18:33 ` Jonathan Wakely 0 siblings, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 18:33 UTC (permalink / raw) To: Stephan Bergmann; +Cc: Thomas Rodgers, trodgers, libstdc++, gcc-patches List [-- Attachment #1: Type: text/plain, Size: 2583 bytes --] On 22/11/20 22:13 +0100, Stephan Bergmann wrote: >On 20/11/2020 23:44, Thomas Rodgers wrote: >>Tested x86_64-pc-linux-gnu, committed. > >Clang complains: > >>$ cat test.cc >>#include <semaphore> >> >>$ clang++ --gcc-toolchain=~/gcc/trunk/inst -std=c++20 -fsyntax-only test.cc >>In file included from test.cc:1: >>In file included from ~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/semaphore:36: >>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/bits/semaphore_base.h:145:22: error: no viable conversion from 'std::chrono::system_clock::time_point' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<1, 1000000000>>>') to 'const std::__platform_semaphore::__clock_t' (aka 'const std::chrono::system_clock') >> const __clock_t __s_entry = __clock_t::now(); >> ^ ~~~~~~~~~~~~~~~~ >>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/chrono:1101:12: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::chrono::system_clock::time_point' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<1, 1000000000>>>') to 'const std::chrono::system_clock &' for 1st argument >> struct system_clock >> ^ >>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/11.0.0/../../../../include/c++/11.0.0/chrono:1101:12: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::chrono::system_clock::time_point' (aka 'time_point<std::chrono::system_clock, duration<long, ratio<1, 1000000000>>>') to 'std::chrono::system_clock &&' for 1st argument >>1 error generated. > >which > >>diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h >>index 78a0b6ba26e..f25c9fdb325 100644 >>--- a/libstdc++-v3/include/bits/semaphore_base.h >>+++ b/libstdc++-v3/include/bits/semaphore_base.h >>@@ -142,7 +142,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> else >> { >> const typename _Clock::time_point __c_entry = _Clock::now(); >>- const __clock_t __s_entry = __clock_t::now(); >>+ const __clock_t::time_point __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)) >>~ > >would fix. I just used 'auto'` instead. Committed, thanks. The fact this didn't error with GCC suggests we're missing some tests. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 972 bytes --] commit 1ccee0fbfa8e528b3671dfbf4dad5b6f67755e4c Author: Jonathan Wakely <jwakely@redhat.com> Date: Mon Nov 23 18:16:44 2020 libstdc++: Fix variable declared with wrong type libstdc++-v3/ChangeLog: * include/bits/semaphore_base.h (__platform_semaphore::_M_try_acquire_until): Fix type of variable. diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index 0692f95f24f2..56333bbbfef7 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -141,7 +141,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION else { const typename _Clock::time_point __c_entry = _Clock::now(); - const __clock_t __s_entry = __clock_t::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)) ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-20 22:44 ` Thomas Rodgers 2020-11-22 21:13 ` Stephan Bergmann @ 2020-11-22 21:41 ` Stephan Bergmann 2020-11-23 18:32 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Stephan Bergmann @ 2020-11-22 21:41 UTC (permalink / raw) To: Thomas Rodgers; +Cc: trodgers, libstdc++, gcc-patches List, Jonathan Wakely On 20/11/2020 23:44, Thomas Rodgers wrote: > Tested x86_64-pc-linux-gnu, committed. ...and there are multiple definition complaints from the linker because of two missing "include": > index 7b2682a577e..23ab2018ca8 100644 > --- a/libstdc++-v3/include/bits/atomic_wait.h > +++ b/libstdc++-v3/include/bits/atomic_wait.h > @@ -223,7 +223,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { _M_w._M_do_wait(_M_version); } > }; > > - void > + inline void > __thread_relax() noexcept > { > #if defined __i386__ || defined __x86_64__ > @@ -233,7 +233,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif > } > > - void > + inline void > __thread_yield() noexcept > { > #if defined _GLIBCXX_USE_SCHED_YIELD ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 21:41 ` Stephan Bergmann @ 2020-11-23 18:32 ` Jonathan Wakely 0 siblings, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 18:32 UTC (permalink / raw) To: Stephan Bergmann; +Cc: Thomas Rodgers, trodgers, libstdc++, gcc-patches List [-- Attachment #1: Type: text/plain, Size: 809 bytes --] On 22/11/20 22:41 +0100, Stephan Bergmann wrote: >On 20/11/2020 23:44, Thomas Rodgers wrote: >>Tested x86_64-pc-linux-gnu, committed. > >...and there are multiple definition complaints from the linker >because of two missing "include": > >>index 7b2682a577e..23ab2018ca8 100644 >>--- a/libstdc++-v3/include/bits/atomic_wait.h >>+++ b/libstdc++-v3/include/bits/atomic_wait.h >>@@ -223,7 +223,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> { _M_w._M_do_wait(_M_version); } >> }; >>- void >>+ inline void >> __thread_relax() noexcept >> { >> #if defined __i386__ || defined __x86_64__ >>@@ -233,7 +233,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> #endif >> } >>- void >>+ inline void >> __thread_yield() noexcept >> { >> #if defined _GLIBCXX_USE_SCHED_YIELD Committed, thanks. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 986 bytes --] commit 0986d3bc621b12c3d0367bf7bd25927c7fbfc552 Author: Stephan Bergmann <sbergman@redhat.com> Date: Mon Nov 23 18:14:44 2020 libstdc++: Fix linker errors due to missing 'inline' keywords libstdc++-v3/ChangeLog: * include/bits/atomic_wait.h (__thread_relax, __thread_yield): Add 'inline'. diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index a40cff124d7d..cd756f68de6d 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -224,7 +224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { _M_w._M_do_wait(_M_version); } }; - void + inline void __thread_relax() noexcept { #if defined __i386__ || defined __x86_64__ @@ -234,9 +234,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } - void + inline void __thread_yield() noexcept - { + { #if defined _GLIBCXX_USE_SCHED_YIELD __gthread_yield(); #endif ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-10-26 21:48 ` [PATCH] libstdc++: Add C++2a synchronization support Thomas Rodgers 2020-10-27 10:23 ` Jonathan Wakely @ 2020-11-21 15:16 ` Andreas Schwab 2020-11-21 17:04 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Andreas Schwab @ 2020-11-21 15:16 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') Andreas. -- Andreas Schwab, schwab@linux-m68k.org GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1 "And now for something completely different." ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-21 15:16 ` Andreas Schwab @ 2020-11-21 17:04 ` Jonathan Wakely 2020-11-21 17:39 ` Jonathan Wakely 0 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-11-21 17:04 UTC (permalink / raw) To: Andreas Schwab; +Cc: Thomas Rodgers, trodgers, libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 2902 bytes --] On 21/11/20 16:16 +0100, Andreas Schwab wrote: >In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, > from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, > from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, > from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, > from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') I'm testing this. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 6587 bytes --] commit 613ac97bed57eb0edb1803b66d5ce3510e665b3d Author: Jonathan Wakely <jwakely@redhat.com> Date: Sat Nov 21 16:52:22 2020 libstdc++: Fix atomic waiting for non-linux targets This fixes some UNRESOLVED tests on (at least) Solaris and Darwin, and disables some tests that hang forever on Solaris. A proper fix is still needed. libstdc++-v3/ChangeLog: * include/bits/atomic_base.h (atomic_flag::wait): Use correct type for __atomic_wait call. * include/bits/atomic_timed_wait.h (__atomic_wait_until): Check _GLIBCXX_HAVE_LINUX_FUTEX. * include/bits/atomic_wait.h (__atomic_notify): Likewise. * include/bits/semaphore_base.h (_GLIBCXX_HAVE_POSIX_SEMAPHORE): Only define if SEM_VALUE_MAX or _POSIX_SEM_VALUE_MAX is defined. * testsuite/29_atomics/atomic/wait_notify/bool.cc: Disable on non-linux targes. * testsuite/29_atomics/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise. * testsuite/29_atomics/atomic_float/wait_notify.cc: Likewise. diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index dd4db926592e..7de02f169977 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -234,7 +234,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept { - std::__atomic_wait(&_M_i, __old, + std::__atomic_wait(&_M_i, static_cast<__atomic_flag_data_type>(__old), [__m, this, __old]() { return this->test(__m) != __old; }); } diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h index 7712a6c591dc..405f7e93ca85 100644 --- a/libstdc++-v3/include/bits/atomic_timed_wait.h +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -240,12 +240,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION do { __atomic_wait_status __res; +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX if constexpr (__platform_wait_uses_type<_Tp>) { __res = __detail::__platform_wait_until((__platform_wait_t*)(void*) __addr, __old, __atime); } else +#endif { __res = __w._M_do_wait_until(__version, __atime); } diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 2d08e5325fb2..7b2682a577ef 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -292,11 +292,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (!__w._M_waiting()) return; +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX if constexpr (__platform_wait_uses_type<_Tp>) { __platform_notify((__platform_wait_t*)(void*) __addr, __all); } else +#endif { __w._M_notify(__all); } diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index da6dc4b91858..78a0b6ba26e6 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -39,8 +39,10 @@ #include <ext/numeric_traits.h> #if __has_include(<semaphore.h>) -#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 -#include <semaphore.h> +# include <semaphore.h> +# if defined SEM_VALUE_MAX || _POSIX_SEM_VALUE_MAX +# define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +# endif #endif #include <chrono> @@ -54,7 +56,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __platform_semaphore { 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 { diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc index 5f1e30a710fe..29781c6e1357 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc index 8531bb2e7880..f54961f893d4 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } +// { dg-additional-options "-pthread" { target pthread } } // { dg-require-gthreads "" } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc index 4f026e1dc9c1..763d3e77159c 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc index 640a84e0342e..27d9b601c2f4 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-21 17:04 ` Jonathan Wakely @ 2020-11-21 17:39 ` Jonathan Wakely 2020-11-22 0:36 ` H.J. Lu 2020-11-23 16:08 ` Jonathan Wakely 0 siblings, 2 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-21 17:39 UTC (permalink / raw) To: Andreas Schwab; +Cc: Thomas Rodgers, trodgers, libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 3281 bytes --] On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >On 21/11/20 16:16 +0100, Andreas Schwab wrote: >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') > >I'm testing this. I'm committing this instead, it's the same but also disables 29_atomics/atomic/wait_notify/generic.cc on non-linux targets. Tested sparc-solaris2.11 and powerpc64le-linux. There are still some timeouts on linux: FAIL: 30_threads/latch/3.cc execution test FAIL: 30_threads/semaphore/try_acquire_for.cc execution test [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 7354 bytes --] commit 6f5387b7c9047baa5ee1385c8f5148d2c351bd20 Author: Jonathan Wakely <jwakely@redhat.com> Date: Sat Nov 21 16:52:22 2020 libstdc++: Fix atomic waiting for non-linux targets This fixes some UNRESOLVED tests on (at least) Solaris and Darwin, and disables some tests that hang forever on Solaris. A proper fix is still needed. libstdc++-v3/ChangeLog: * include/bits/atomic_base.h (atomic_flag::wait): Use correct type for __atomic_wait call. * include/bits/atomic_timed_wait.h (__atomic_wait_until): Check _GLIBCXX_HAVE_LINUX_FUTEX. * include/bits/atomic_wait.h (__atomic_notify): Likewise. * include/bits/semaphore_base.h (_GLIBCXX_HAVE_POSIX_SEMAPHORE): Only define if SEM_VALUE_MAX or _POSIX_SEM_VALUE_MAX is defined. * testsuite/29_atomics/atomic/wait_notify/bool.cc: Disable on non-linux targes. * testsuite/29_atomics/atomic/wait_notify/generic.cc: Likewise. * testsuite/29_atomics/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise. * testsuite/29_atomics/atomic_float/wait_notify.cc: Likewise. diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index dd4db926592e..7de02f169977 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -234,7 +234,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept { - std::__atomic_wait(&_M_i, __old, + std::__atomic_wait(&_M_i, static_cast<__atomic_flag_data_type>(__old), [__m, this, __old]() { return this->test(__m) != __old; }); } diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h index 7712a6c591dc..405f7e93ca85 100644 --- a/libstdc++-v3/include/bits/atomic_timed_wait.h +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -240,12 +240,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION do { __atomic_wait_status __res; +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX if constexpr (__platform_wait_uses_type<_Tp>) { __res = __detail::__platform_wait_until((__platform_wait_t*)(void*) __addr, __old, __atime); } else +#endif { __res = __w._M_do_wait_until(__version, __atime); } diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 2d08e5325fb2..7b2682a577ef 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -292,11 +292,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (!__w._M_waiting()) return; +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX if constexpr (__platform_wait_uses_type<_Tp>) { __platform_notify((__platform_wait_t*)(void*) __addr, __all); } else +#endif { __w._M_notify(__all); } diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index da6dc4b91858..78a0b6ba26e6 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -39,8 +39,10 @@ #include <ext/numeric_traits.h> #if __has_include(<semaphore.h>) -#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 -#include <semaphore.h> +# include <semaphore.h> +# if defined SEM_VALUE_MAX || _POSIX_SEM_VALUE_MAX +# define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +# endif #endif #include <chrono> @@ -54,7 +56,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __platform_semaphore { 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 { diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc index 5f1e30a710fe..29781c6e1357 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc index 0249341055cc..629556a9d2d0 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc index 8531bb2e7880..f54961f893d4 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } +// { dg-additional-options "-pthread" { target pthread } } // { dg-require-gthreads "" } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc index 4f026e1dc9c1..763d3e77159c 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc index 640a84e0342e..27d9b601c2f4 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc @@ -1,7 +1,8 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-21 17:39 ` Jonathan Wakely @ 2020-11-22 0:36 ` H.J. Lu 2020-11-23 14:50 ` Jonathan Wakely 2020-11-24 23:45 ` Jonathan Wakely 2020-11-23 16:08 ` Jonathan Wakely 1 sibling, 2 replies; 50+ messages in thread From: H.J. Lu @ 2020-11-22 0:36 UTC (permalink / raw) To: Jonathan Wakely; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ On Sat, Nov 21, 2020 at 9:40 AM Jonathan Wakely via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > On 21/11/20 17:04 +0000, Jonathan Wakely wrote: > >On 21/11/20 16:16 +0100, Andreas Schwab wrote: > >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, > >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: > >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': > >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' > >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, > >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: > >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' > >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: > >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, > >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, > >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: > >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') > > > >I'm testing this. > > I'm committing this instead, it's the same but also disables > 29_atomics/atomic/wait_notify/generic.cc on non-linux targets. > > Tested sparc-solaris2.11 and powerpc64le-linux. > > There are still some timeouts on linux: > > FAIL: 30_threads/latch/3.cc execution test > FAIL: 30_threads/semaphore/try_acquire_for.cc execution test > I opened: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97936 -- H.J. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 0:36 ` H.J. Lu @ 2020-11-23 14:50 ` Jonathan Wakely 2020-11-24 23:45 ` Jonathan Wakely 1 sibling, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 14:50 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ On 21/11/20 16:36 -0800, H.J. Lu wrote: >On Sat, Nov 21, 2020 at 9:40 AM Jonathan Wakely via Gcc-patches ><gcc-patches@gcc.gnu.org> wrote: >> >> On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >> >On 21/11/20 16:16 +0100, Andreas Schwab wrote: >> >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >> >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >> >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') >> > >> >I'm testing this. >> >> I'm committing this instead, it's the same but also disables >> 29_atomics/atomic/wait_notify/generic.cc on non-linux targets. >> >> Tested sparc-solaris2.11 and powerpc64le-linux. >> >> There are still some timeouts on linux: >> >> FAIL: 30_threads/latch/3.cc execution test >> FAIL: 30_threads/semaphore/try_acquire_for.cc execution test >> > >I opened: > >https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97936 Thanks. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 0:36 ` H.J. Lu 2020-11-23 14:50 ` Jonathan Wakely @ 2020-11-24 23:45 ` Jonathan Wakely 2020-11-25 1:07 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-11-24 23:45 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 3718 bytes --] On 21/11/20 16:36 -0800, H.J. Lu wrote: >On Sat, Nov 21, 2020 at 9:40 AM Jonathan Wakely via Gcc-patches ><gcc-patches@gcc.gnu.org> wrote: >> >> On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >> >On 21/11/20 16:16 +0100, Andreas Schwab wrote: >> >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >> >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >> >>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >> >> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >> >> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >> >>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') >> > >> >I'm testing this. >> >> I'm committing this instead, it's the same but also disables >> 29_atomics/atomic/wait_notify/generic.cc on non-linux targets. >> >> Tested sparc-solaris2.11 and powerpc64le-linux. >> >> There are still some timeouts on linux: >> >> FAIL: 30_threads/latch/3.cc execution test >> FAIL: 30_threads/semaphore/try_acquire_for.cc execution test >> > >I opened: > >https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97936 I've disabled the failing tests for now. They can be re-enabled after the problem is found and fixed. Committed to trunk. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 3503 bytes --] commit a3313a2214a6253672ab4fa37a2dcf57fd0f8dce Author: Jonathan Wakely <jwakely@redhat.com> Date: Tue Nov 24 23:22:01 2020 libstdc++: Disable failing tests [PR 97936] These tests are unstable and causing failures due to timeouts. Disable them until the cause can be found, so that testing doesn't have to wait for them to timeout. libstdc++-v3/ChangeLog: PR libstdc++/97936 PR libstdc++/97944 * testsuite/29_atomics/atomic_integral/wait_notify.cc: Disable. Do not require pthreads, but add -pthread when appropriate. * testsuite/30_threads/jthread/95989.cc: Likewise. * testsuite/30_threads/latch/3.cc: Likewise. * testsuite/30_threads/semaphore/try_acquire_until.cc: Likewise. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc index abf2bfdbee96..762583cf8c76 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc @@ -1,8 +1,9 @@ -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } // { dg-add-options libatomic } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { *-*-* } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/30_threads/jthread/95989.cc b/libstdc++-v3/testsuite/30_threads/jthread/95989.cc index c7a9430eee90..a179eab38198 100644 --- a/libstdc++-v3/testsuite/30_threads/jthread/95989.cc +++ b/libstdc++-v3/testsuite/30_threads/jthread/95989.cc @@ -20,6 +20,7 @@ // { dg-require-gthreads {} } // { dg-additional-options "-pthread" { target pthread } } // { dg-additional-options "-static" { target static } } +// { dg-skip-if "broken" { *-*-* } } #include <thread> diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc index 5d08000f4302..6304135a877c 100644 --- a/libstdc++-v3/testsuite/30_threads/latch/3.cc +++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc @@ -15,11 +15,12 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } -// +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { *-*-* } } + #include <latch> #include <atomic> #include <thread> diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc index af7ab7bac395..5e1141425f72 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -15,10 +15,11 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -// { dg-options "-std=gnu++2a -pthread" } +// { dg-options "-std=gnu++2a" } // { dg-do run { target c++2a } } -// { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-skip-if "broken" { *-*-* } } #include <semaphore> #include <chrono> ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-24 23:45 ` Jonathan Wakely @ 2020-11-25 1:07 ` Jonathan Wakely 2020-11-25 10:35 ` Jonathan Wakely 0 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-11-25 1:07 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ On 24/11/20 23:45 +0000, Jonathan Wakely wrote: >On 21/11/20 16:36 -0800, H.J. Lu wrote: >>On Sat, Nov 21, 2020 at 9:40 AM Jonathan Wakely via Gcc-patches >><gcc-patches@gcc.gnu.org> wrote: >>> >>>On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >>>>On 21/11/20 16:16 +0100, Andreas Schwab wrote: >>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') >>>> >>>>I'm testing this. >>> >>>I'm committing this instead, it's the same but also disables >>>29_atomics/atomic/wait_notify/generic.cc on non-linux targets. >>> >>>Tested sparc-solaris2.11 and powerpc64le-linux. >>> >>>There are still some timeouts on linux: >>> >>>FAIL: 30_threads/latch/3.cc execution test >>>FAIL: 30_threads/semaphore/try_acquire_for.cc execution test >>> >> >>I opened: >> >>https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97936 > >I've disabled the failing tests for now. They can be re-enabled after >the problem is found and fixed. I was finally able to reproduce the hangs, and I think this is the fix: --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -100,9 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), static_cast<int>(__futex_wait_flags::__wait_private), __val, nullptr); - if (!__e) + if (!__e || errno == EAGAIN) break; - else if (!(errno == EINTR || errno == EAGAIN)) + else if (errno != EINTR) __throw_system_error(__e); } } The problem is that we're going into a busy loop when SYS_futex returns EAGAIN, but that means the current value doesn't match the expected value, so we should return not keep waiting for the value to change. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-25 1:07 ` Jonathan Wakely @ 2020-11-25 10:35 ` Jonathan Wakely 2020-11-25 12:32 ` Jonathan Wakely 2020-11-25 18:39 ` Jonathan Wakely 0 siblings, 2 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-25 10:35 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ On 25/11/20 01:07 +0000, Jonathan Wakely wrote: >On 24/11/20 23:45 +0000, Jonathan Wakely wrote: >>On 21/11/20 16:36 -0800, H.J. Lu wrote: >>>On Sat, Nov 21, 2020 at 9:40 AM Jonathan Wakely via Gcc-patches >>><gcc-patches@gcc.gnu.org> wrote: >>>> >>>>On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >>>>>On 21/11/20 16:16 +0100, Andreas Schwab wrote: >>>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >>>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >>>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') >>>>> >>>>>I'm testing this. >>>> >>>>I'm committing this instead, it's the same but also disables >>>>29_atomics/atomic/wait_notify/generic.cc on non-linux targets. >>>> >>>>Tested sparc-solaris2.11 and powerpc64le-linux. >>>> >>>>There are still some timeouts on linux: >>>> >>>>FAIL: 30_threads/latch/3.cc execution test >>>>FAIL: 30_threads/semaphore/try_acquire_for.cc execution test >>>> >>> >>>I opened: >>> >>>https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97936 >> >>I've disabled the failing tests for now. They can be re-enabled after >>the problem is found and fixed. > >I was finally able to reproduce the hangs, and I think this is the >fix: > >--- a/libstdc++-v3/include/bits/atomic_wait.h >+++ b/libstdc++-v3/include/bits/atomic_wait.h >@@ -100,9 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), > static_cast<int>(__futex_wait_flags::__wait_private), > __val, nullptr); >- if (!__e) >+ if (!__e || errno == EAGAIN) > break; >- else if (!(errno == EINTR || errno == EAGAIN)) >+ else if (errno != EINTR) > __throw_system_error(__e); > } > } > >The problem is that we're going into a busy loop when SYS_futex >returns EAGAIN, but that means the current value doesn't match the >expected value, so we should return not keep waiting for the value to >change. I've pushed that as ad9cbcee543ecccd79fa49dafcd925532d2ce210 but there are still other FAILs to be fixed. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-25 10:35 ` Jonathan Wakely @ 2020-11-25 12:32 ` Jonathan Wakely 2020-11-25 18:39 ` Jonathan Wakely 1 sibling, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-25 12:32 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 5109 bytes --] On 25/11/20 10:35 +0000, Jonathan Wakely wrote: >On 25/11/20 01:07 +0000, Jonathan Wakely wrote: >>On 24/11/20 23:45 +0000, Jonathan Wakely wrote: >>>On 21/11/20 16:36 -0800, H.J. Lu wrote: >>>>On Sat, Nov 21, 2020 at 9:40 AM Jonathan Wakely via Gcc-patches >>>><gcc-patches@gcc.gnu.org> wrote: >>>>> >>>>>On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >>>>>>On 21/11/20 16:16 +0100, Andreas Schwab wrote: >>>>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >>>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >>>>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >>>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >>>>>>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>>>>>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>>>>>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>>>>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') >>>>>> >>>>>>I'm testing this. >>>>> >>>>>I'm committing this instead, it's the same but also disables >>>>>29_atomics/atomic/wait_notify/generic.cc on non-linux targets. >>>>> >>>>>Tested sparc-solaris2.11 and powerpc64le-linux. >>>>> >>>>>There are still some timeouts on linux: >>>>> >>>>>FAIL: 30_threads/latch/3.cc execution test >>>>>FAIL: 30_threads/semaphore/try_acquire_for.cc execution test >>>>> >>>> >>>>I opened: >>>> >>>>https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97936 >>> >>>I've disabled the failing tests for now. They can be re-enabled after >>>the problem is found and fixed. >> >>I was finally able to reproduce the hangs, and I think this is the >>fix: >> >>--- a/libstdc++-v3/include/bits/atomic_wait.h >>+++ b/libstdc++-v3/include/bits/atomic_wait.h >>@@ -100,9 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), >> static_cast<int>(__futex_wait_flags::__wait_private), >> __val, nullptr); >>- if (!__e) >>+ if (!__e || errno == EAGAIN) >> break; >>- else if (!(errno == EINTR || errno == EAGAIN)) >>+ else if (errno != EINTR) >> __throw_system_error(__e); >> } >> } >> >>The problem is that we're going into a busy loop when SYS_futex >>returns EAGAIN, but that means the current value doesn't match the >>expected value, so we should return not keep waiting for the value to >>change. > >I've pushed that as ad9cbcee543ecccd79fa49dafcd925532d2ce210 but there >are still other FAILs to be fixed. Except that what I pushed was not what I wrote above, as noticed by Jakub. I need to get more sleep. Fixed with this patch, pushed to trunk. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 1235 bytes --] commit a5ccfd04605d940daded7e95474389f1c7dfad61 Author: Jonathan Wakely <jwakely@redhat.com> Date: Wed Nov 25 12:16:07 2020 libstdc++: Fix silly typos [PR 97936] libstdc++-v3/ChangeLog: PR libstdc++/97936 * include/bits/atomic_wait.h (__platform_wait): Check errno, not just the value of EAGAIN. (__waiters::__waiters()): Fix name of data member. diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index fdf7c4586f22..5af9367ca2e9 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -100,7 +100,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __e = syscall (SYS_futex, static_cast<const void*>(__addr), static_cast<int>(__futex_wait_flags::__wait_private), __val, nullptr); - if (!__e || EAGAIN) + if (!__e || errno == EAGAIN) break; else if (errno != EINTR) __throw_system_error(__e); @@ -133,7 +133,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION mutable __gthread_cond_t _M_cv; __waiters() noexcept { - __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + __GTHREAD_COND_INIT_FUNCTION(&_M_cv); } # endif #endif ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-25 10:35 ` Jonathan Wakely 2020-11-25 12:32 ` Jonathan Wakely @ 2020-11-25 18:39 ` Jonathan Wakely 2020-11-26 16:26 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-11-25 18:39 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 418 bytes --] On 25/11/20 10:35 +0000, Jonathan Wakely wrote: >I've pushed that as ad9cbcee543ecccd79fa49dafcd925532d2ce210 but there >are still other FAILs to be fixed. I think the other FAILs are due to a race condition in the tests, fixed by this patch. Tested x86_64-linux, powerpc64le-linux, sparc-solaris2.11 and powerpc-aix. Committed to trunk. I'll keep an eye on the testresults to see if this really fixes it or not. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 7093 bytes --] commit f76cad692a62d44ed32d010200bad74f36c73092 Author: Jonathan Wakely <jwakely@redhat.com> Date: Wed Nov 25 14:39:54 2020 libstdc++: Fix testsuite helper functions [PR 97936] This fixes a race condition in the util/atomic/wait_notify_util.h header used by several tests, which should make the tests work properly. libstdc++-v3/ChangeLog: PR libstdc++/97936 * testsuite/29_atomics/atomic/wait_notify/bool.cc: Re-eneable test. * testsuite/29_atomics/atomic/wait_notify/generic.cc: Likewise. * testsuite/29_atomics/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise. * testsuite/29_atomics/atomic_float/wait_notify.cc: Likewise. * testsuite/29_atomics/atomic_integral/wait_notify.cc: Likewise. * testsuite/util/atomic/wait_notify_util.h: Fix missed notifications by making the new thread wait until the parent thread is waiting on the condition variable. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc index 29781c6e1357..c14a2391d68b 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -2,7 +2,6 @@ // { dg-do run { target c++2a } } // { dg-require-gthreads "" } // { dg-additional-options "-pthread" { target pthread } } -// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc index 629556a9d2d0..988fe7b334f3 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc @@ -2,7 +2,6 @@ // { dg-do run { target c++2a } } // { dg-require-gthreads "" } // { dg-additional-options "-pthread" { target pthread } } -// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc index f54961f893d4..87830236e0ee 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -2,7 +2,6 @@ // { dg-do run { target c++2a } } // { dg-additional-options "-pthread" { target pthread } } // { dg-require-gthreads "" } -// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc index 763d3e77159c..991713fbcdee 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -2,7 +2,6 @@ // { dg-do run { target c++2a } } // { dg-require-gthreads "" } // { dg-additional-options "-pthread" { target pthread } } -// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc index 8f9e4a39a21f..134eff39e1b1 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc @@ -3,7 +3,6 @@ // { dg-require-gthreads "" } // { dg-additional-options "-pthread" { target pthread } } // { dg-add-options libatomic } -// { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc index 762583cf8c76..c65379cba619 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc @@ -3,7 +3,6 @@ // { dg-require-gthreads "" } // { dg-add-options libatomic } // { dg-additional-options "-pthread" { target pthread } } -// { dg-skip-if "broken" { *-*-* } } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h b/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h index a319e8b60a69..f5fff4af4e49 100644 --- a/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h +++ b/libstdc++-v3/testsuite/util/atomic/wait_notify_util.h @@ -34,16 +34,20 @@ Tp check_wait_notify(Tp val1, Tp val2) std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); std::atomic<Tp> a(val1); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); a.wait(val1); if (a.load() != val2) a = val1; }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(val2); @@ -59,10 +63,15 @@ Tp check_wait_notify(Tp val1, Tp val2) std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); std::atomic<Tp> a(val1); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); a.wait(val1); auto v = a.load(); @@ -70,7 +79,6 @@ Tp check_wait_notify(Tp val1, Tp val2) if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) a = val1; }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(val2); @@ -87,16 +95,20 @@ Tp check_atomic_wait_notify(Tp val1, Tp val2) std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); std::atomic<Tp> a(val1); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); std::atomic_wait(&a, val1); if (a.load() != val2) a = val1; }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(val2); @@ -112,10 +124,15 @@ Tp check_atomic_wait_notify(Tp val1, Tp val2) std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); std::atomic<Tp> a(val1); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); std::atomic_wait(&a, val1); auto v = a.load(); @@ -123,7 +140,6 @@ Tp check_atomic_wait_notify(Tp val1, Tp val2) if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0) a = val1; }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(val2); ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-25 18:39 ` Jonathan Wakely @ 2020-11-26 16:26 ` Jonathan Wakely 0 siblings, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-26 16:26 UTC (permalink / raw) To: H.J. Lu; +Cc: Andreas Schwab, trodgers, GCC Patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 579 bytes --] On 25/11/20 18:39 +0000, Jonathan Wakely wrote: >On 25/11/20 10:35 +0000, Jonathan Wakely wrote: >>I've pushed that as ad9cbcee543ecccd79fa49dafcd925532d2ce210 but there >>are still other FAILs to be fixed. > >I think the other FAILs are due to a race condition in the tests, >fixed by this patch. Tested x86_64-linux, powerpc64le-linux, >sparc-solaris2.11 and powerpc-aix. Committed to trunk. > >I'll keep an eye on the testresults to see if this really fixes it or >not. This fixes some more races of the same kind, in different tiles. Tested as above. Committed to trunk. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 4212 bytes --] commit 10522ed1089277e2aa6cd708205aa5c730179cf0 Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Nov 26 12:55:47 2020 libstdc++: Fix some more deadlocks in tests [PR 97936] The missed notifications fixed in r11-5383 also happen in some other tests which have similar code. libstdc++-v3/ChangeLog: PR libstdc++/97936 * testsuite/29_atomics/atomic/wait_notify/bool.cc: Fix missed notifications by making the new thread wait until the parent thread is waiting on the condition variable. * testsuite/29_atomics/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise. * testsuite/29_atomics/atomic_ref/wait_notify.cc: Likewise. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc index c14a2391d68b..1fc014911737 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -36,11 +36,16 @@ main () std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); std::atomic<bool> a(false); std::atomic<bool> b(false); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); a.wait(false); if (a.load()) @@ -48,7 +53,6 @@ main () b.store(true); } }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(true); diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc index 87830236e0ee..3b699e9133b2 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -36,6 +36,7 @@ main () std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); long aa; long bb; @@ -43,12 +44,15 @@ main () std::atomic<long*> a(nullptr); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); a.wait(nullptr); if (a.load() == &aa) a.store(&bb); }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(&aa); diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc index 991713fbcdee..5d5e06dde31c 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc @@ -36,18 +36,22 @@ main() std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); std::atomic_flag a; std::atomic_flag b; std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); a.wait(false); b.test_and_set(); b.notify_one(); }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.test_and_set(); diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc index b38fc206d468..bc5a7d0d8bf9 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc @@ -37,17 +37,21 @@ Tp check_wait_notify(Tp val1, Tp val2) std::mutex m; std::condition_variable cv; + std::unique_lock<std::mutex> l(m); Tp aa = val1; std::atomic_ref<Tp> a(aa); std::thread t([&] { + { + // This ensures we block until cv.wait(l) starts. + std::lock_guard<std::mutex> ll(m); + } cv.notify_one(); a.wait(val1); if (a.load() != val2) a = val1; }); - std::unique_lock<std::mutex> l(m); cv.wait(l); std::this_thread::sleep_for(100ms); a.store(val2); ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-21 17:39 ` Jonathan Wakely 2020-11-22 0:36 ` H.J. Lu @ 2020-11-23 16:08 ` Jonathan Wakely 1 sibling, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 16:08 UTC (permalink / raw) To: Andreas Schwab; +Cc: Thomas Rodgers, trodgers, libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 3424 bytes --] On 21/11/20 17:39 +0000, Jonathan Wakely wrote: >On 21/11/20 17:04 +0000, Jonathan Wakely wrote: >>On 21/11/20 16:16 +0100, Andreas Schwab wrote: >>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h: In member function 'void std::atomic_flag::wait(bool, std::memory_order) const': >>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: error: no matching function for call to '__atomic_wait(const __atomic_flag_data_type*, bool&, std::atomic_flag::wait(bool, std::memory_order) const::<lambda()>)' >>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:41, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: candidate: 'template<class _Tp, class _Pred> void std::__atomic_wait(const _Tp*, _Tp, _Pred)' >>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_wait.h:265: note: template argument deduction/substitution failed: >>>In file included from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:33, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/memory:78, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/stdc++.h:82, >>> from /daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/m68k-linux/bits/extc++.h:32, >>> from /daten/aranym/gcc/gcc-20201121/libstdc++-v3/testsuite/17_intro/headers/c++2020/all_attributes.cc:37: >>>/daten/aranym/gcc/gcc-20201121/Build/m68k-linux/libstdc++-v3/include/bits/atomic_base.h:239: note: deduced conflicting types for parameter '_Tp' ('unsigned char' and 'bool') >> >>I'm testing this. > >I'm committing this instead, it's the same but also disables >29_atomics/atomic/wait_notify/generic.cc on non-linux targets. > >Tested sparc-solaris2.11 and powerpc64le-linux. > >There are still some timeouts on linux: > >FAIL: 30_threads/latch/3.cc execution test >FAIL: 30_threads/semaphore/try_acquire_for.cc execution test Here's another small fix. Tested powerpc64le-linux, pushed to trunk. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 2174 bytes --] commit fd62daea40e09c1e6d599a6171db6b298d6c362e Author: Jonathan Wakely <jwakely@redhat.com> Date: Mon Nov 23 15:46:24 2020 libstdc++: Link tests to libatomic as required [PR 97948] libstdc++-v3/ChangeLog: PR libstdc++/97948 * testsuite/29_atomics/atomic_float/wait_notify.cc: Add options for libatomic. * testsuite/29_atomics/atomic_integral/wait_notify.cc: Likewise. * testsuite/29_atomics/atomic_ref/wait_notify.cc: Likewise. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc index 27d9b601c2f4..8f9e4a39a21f 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_float/wait_notify.cc @@ -2,6 +2,7 @@ // { dg-do run { target c++2a } } // { dg-require-gthreads "" } // { dg-additional-options "-pthread" { target pthread } } +// { dg-add-options libatomic } // { dg-skip-if "broken" { ! *-*-*linux } } // Copyright (C) 2020 Free Software Foundation, Inc. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc index 6e9ee7dbf93f..abf2bfdbee96 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc @@ -2,6 +2,7 @@ // { dg-do run { target c++2a } } // { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-add-options libatomic } // Copyright (C) 2020 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc index dc5ae7a21eac..b38fc206d468 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc @@ -2,6 +2,7 @@ // { dg-do run { target c++2a } } // { dg-require-effective-target pthread } // { dg-require-gthreads "" } +// { dg-add-options libatomic } // Copyright (C) 2020 Free Software Foundation, Inc. // ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers 2020-09-28 13:25 ` Jonathan Wakely @ 2020-09-28 13:30 ` Jonathan Wakely 2020-09-28 13:36 ` Jonathan Wakely 2 siblings, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-09-28 13:30 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 11/09/20 16:58 -0700, Thomas Rodgers wrote: >From: Thomas Rodgers <trodgers@redhat.com> > >This patch supercedes both the Add C++2a synchronization support patch >being replied to *and* the patch adding wait/notify_* to atomic_flag. > >Add support for - > * atomic_flag::wait/notify_one/notify_all > * atomic::wait/notify_one/notify_all > * counting_semaphore > * binary_semaphore > * latch > >libstdc++-v3/ChangeLog: > > * include/Makefile.am (bits_headers): Add new header. > * include/Makefile.in: Regenerate. > * include/bits/atomic_base.h (__atomic_flag::wait): Define. > (__atomic_flag::notify_one): Likewise. > (__atomic_flag::notify_all): Likewise. > (__atomic_base<_Itp>::wait): Likewise. > (__atomic_base<_Itp>::notify_one): Likewise. > (__atomic_base<_Itp>::notify_all): Likewise. > (__atomic_base<_Ptp*>::wait): Likewise. > (__atomic_base<_Ptp*>::notify_one): Likewise. > (__atomic_base<_Ptp*>::notify_all): Likewise. > (__atomic_impl::wait): Likewise. > (__atomic_impl::notify_one): Likewise. > (__atomic_impl::notify_all): Likewise. > (__atomic_float<_Fp>::wait): Likewise. > (__atomic_float<_Fp>::notify_one): Likewise. > (__atomic_float<_Fp>::notify_all): Likewise. > (__atomic_ref<_Tp>::wait): Likewise. > (__atomic_ref<_Tp>::notify_one): Likewise. > (__atomic_ref<_Tp>::notify_all): Likewise. > (atomic_wait<_Tp>): Likewise. > (atomic_wait_explicit<_Tp>): Likewise. > (atomic_notify_one<_Tp>): Likewise. > (atomic_notify_all<_Tp>): Likewise. > * include/bits/atomic_wait.h: New file. > * include/bits/atomic_timed_wait.h: New file. > * include/bits/semaphore_base.h: New file. > * include/std/atomic (atomic<bool>::wait): Define. > (atomic<bool>::wait_one): Likewise. > (atomic<bool>::wait_all): Likewise. > (atomic<_Tp>::wait): Likewise. > (atomic<_Tp>::wait_one): Likewise. > (atomic<_Tp>::wait_all): Likewise. > (atomic<_Tp*>::wait): Likewise. > (atomic<_Tp*>::wait_one): Likewise. > (atomic<_Tp*>::wait_all): Likewise. > * include/std/latch: New file. > * include/std/semaphore: New file. > * include/std/version: Add __cpp_lib_semaphore and > __cpp_lib_latch defines. > * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. > * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. > * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. > * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. > * testsuite/30_thread/semaphore/1.cc: New test. > * testsuite/30_thread/semaphore/2.cc: Likewise. > * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. > * testsuite/30_thread/latch/1.cc: New test. > * testsuite/30_thread/latch/2.cc: New test. > * testsuite/30_thread/latch/3.cc: New test. >--- > libstdc++-v3/include/Makefile.am | 5 + > libstdc++-v3/include/Makefile.in | 5 + > libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- > libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ > libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ > libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ > libstdc++-v3/include/std/atomic | 73 +++++ > libstdc++-v3/include/std/latch | 90 ++++++ > libstdc++-v3/include/std/semaphore | 92 ++++++ > libstdc++-v3/include/std/version | 2 + > .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ > .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ > .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ > .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ > .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ > .../atomic/wait_notify/integrals.cc | 65 ++++ > .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ > .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ > libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ > libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ > libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ > .../testsuite/30_threads/semaphore/1.cc | 27 ++ > .../testsuite/30_threads/semaphore/2.cc | 27 ++ > .../semaphore/least_max_value_neg.cc | 30 ++ > .../30_threads/semaphore/try_acquire.cc | 55 ++++ > .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ > .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ > .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ > 28 files changed, 2471 insertions(+), 1 deletion(-) > create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h > create mode 100644 libstdc++-v3/include/bits/atomic_wait.h > create mode 100644 libstdc++-v3/include/bits/semaphore_base.h > create mode 100644 libstdc++-v3/include/std/latch > create mode 100644 libstdc++-v3/include/std/semaphore > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc > >diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am >index c9df9a9d6c6..9b5b6ed0005 100644 >--- a/libstdc++-v3/include/Makefile.am >+++ b/libstdc++-v3/include/Makefile.am >@@ -52,6 +52,7 @@ std_headers = \ > ${std_srcdir}/iostream \ > ${std_srcdir}/istream \ > ${std_srcdir}/iterator \ >+ ${std_srcdir}/latch \ > ${std_srcdir}/limits \ > ${std_srcdir}/list \ > ${std_srcdir}/locale \ >@@ -69,6 +70,7 @@ std_headers = \ > ${std_srcdir}/ratio \ > ${std_srcdir}/regex \ > ${std_srcdir}/scoped_allocator \ >+ ${std_srcdir}/semaphore \ > ${std_srcdir}/set \ > ${std_srcdir}/shared_mutex \ > ${std_srcdir}/span \ >@@ -101,6 +103,8 @@ bits_headers = \ > ${bits_srcdir}/allocated_ptr.h \ > ${bits_srcdir}/allocator.h \ > ${bits_srcdir}/atomic_base.h \ >+ ${bits_srcdir}/atomic_wait.h \ >+ ${bits_srcdir}/atomic_timed_wait.h \ > ${bits_srcdir}/atomic_futex.h \ These should be kept in alphabetical order. > ${bits_srcdir}/basic_ios.h \ > ${bits_srcdir}/basic_ios.tcc \ ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers 2020-09-28 13:25 ` Jonathan Wakely 2020-09-28 13:30 ` Jonathan Wakely @ 2020-09-28 13:36 ` Jonathan Wakely 2020-09-28 21:29 ` Thomas Rodgers 2 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-09-28 13:36 UTC (permalink / raw) To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers On 11/09/20 16:58 -0700, Thomas Rodgers wrote: >From: Thomas Rodgers <trodgers@redhat.com> > >This patch supercedes both the Add C++2a synchronization support patch >being replied to *and* the patch adding wait/notify_* to atomic_flag. > >Add support for - > * atomic_flag::wait/notify_one/notify_all > * atomic::wait/notify_one/notify_all > * counting_semaphore > * binary_semaphore > * latch > >libstdc++-v3/ChangeLog: > > * include/Makefile.am (bits_headers): Add new header. > * include/Makefile.in: Regenerate. > * include/bits/atomic_base.h (__atomic_flag::wait): Define. > (__atomic_flag::notify_one): Likewise. > (__atomic_flag::notify_all): Likewise. > (__atomic_base<_Itp>::wait): Likewise. > (__atomic_base<_Itp>::notify_one): Likewise. > (__atomic_base<_Itp>::notify_all): Likewise. > (__atomic_base<_Ptp*>::wait): Likewise. > (__atomic_base<_Ptp*>::notify_one): Likewise. > (__atomic_base<_Ptp*>::notify_all): Likewise. > (__atomic_impl::wait): Likewise. > (__atomic_impl::notify_one): Likewise. > (__atomic_impl::notify_all): Likewise. > (__atomic_float<_Fp>::wait): Likewise. > (__atomic_float<_Fp>::notify_one): Likewise. > (__atomic_float<_Fp>::notify_all): Likewise. > (__atomic_ref<_Tp>::wait): Likewise. > (__atomic_ref<_Tp>::notify_one): Likewise. > (__atomic_ref<_Tp>::notify_all): Likewise. > (atomic_wait<_Tp>): Likewise. > (atomic_wait_explicit<_Tp>): Likewise. > (atomic_notify_one<_Tp>): Likewise. > (atomic_notify_all<_Tp>): Likewise. > * include/bits/atomic_wait.h: New file. > * include/bits/atomic_timed_wait.h: New file. > * include/bits/semaphore_base.h: New file. > * include/std/atomic (atomic<bool>::wait): Define. > (atomic<bool>::wait_one): Likewise. > (atomic<bool>::wait_all): Likewise. > (atomic<_Tp>::wait): Likewise. > (atomic<_Tp>::wait_one): Likewise. > (atomic<_Tp>::wait_all): Likewise. > (atomic<_Tp*>::wait): Likewise. > (atomic<_Tp*>::wait_one): Likewise. > (atomic<_Tp*>::wait_all): Likewise. > * include/std/latch: New file. > * include/std/semaphore: New file. > * include/std/version: Add __cpp_lib_semaphore and > __cpp_lib_latch defines. > * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. > * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. > * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. > * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. > * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. > * testsuite/30_thread/semaphore/1.cc: New test. > * testsuite/30_thread/semaphore/2.cc: Likewise. > * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. > * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. > * testsuite/30_thread/latch/1.cc: New test. > * testsuite/30_thread/latch/2.cc: New test. > * testsuite/30_thread/latch/3.cc: New test. >--- > libstdc++-v3/include/Makefile.am | 5 + > libstdc++-v3/include/Makefile.in | 5 + > libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- > libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ > libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ > libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ > libstdc++-v3/include/std/atomic | 73 +++++ > libstdc++-v3/include/std/latch | 90 ++++++ > libstdc++-v3/include/std/semaphore | 92 ++++++ > libstdc++-v3/include/std/version | 2 + > .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ > .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ > .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ > .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ > .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ > .../atomic/wait_notify/integrals.cc | 65 ++++ > .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ > .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ > libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ > libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ > libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ > .../testsuite/30_threads/semaphore/1.cc | 27 ++ > .../testsuite/30_threads/semaphore/2.cc | 27 ++ > .../semaphore/least_max_value_neg.cc | 30 ++ > .../30_threads/semaphore/try_acquire.cc | 55 ++++ > .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ > .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ > .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ > 28 files changed, 2471 insertions(+), 1 deletion(-) > create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h > create mode 100644 libstdc++-v3/include/bits/atomic_wait.h > create mode 100644 libstdc++-v3/include/bits/semaphore_base.h > create mode 100644 libstdc++-v3/include/std/latch > create mode 100644 libstdc++-v3/include/std/semaphore > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc > create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc > create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc > >diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am >index c9df9a9d6c6..9b5b6ed0005 100644 >--- a/libstdc++-v3/include/Makefile.am >+++ b/libstdc++-v3/include/Makefile.am >@@ -52,6 +52,7 @@ std_headers = \ > ${std_srcdir}/iostream \ > ${std_srcdir}/istream \ > ${std_srcdir}/iterator \ >+ ${std_srcdir}/latch \ > ${std_srcdir}/limits \ > ${std_srcdir}/list \ > ${std_srcdir}/locale \ >@@ -69,6 +70,7 @@ std_headers = \ > ${std_srcdir}/ratio \ > ${std_srcdir}/regex \ > ${std_srcdir}/scoped_allocator \ >+ ${std_srcdir}/semaphore \ > ${std_srcdir}/set \ > ${std_srcdir}/shared_mutex \ > ${std_srcdir}/span \ >@@ -101,6 +103,8 @@ bits_headers = \ > ${bits_srcdir}/allocated_ptr.h \ > ${bits_srcdir}/allocator.h \ > ${bits_srcdir}/atomic_base.h \ >+ ${bits_srcdir}/atomic_wait.h \ >+ ${bits_srcdir}/atomic_timed_wait.h \ > ${bits_srcdir}/atomic_futex.h \ > ${bits_srcdir}/basic_ios.h \ > ${bits_srcdir}/basic_ios.tcc \ >@@ -175,6 +179,7 @@ bits_headers = \ > ${bits_srcdir}/regex_compiler.tcc \ > ${bits_srcdir}/regex_executor.h \ > ${bits_srcdir}/regex_executor.tcc \ >+ ${bits_srcdir}/semaphore_base.h \ > ${bits_srcdir}/shared_ptr.h \ > ${bits_srcdir}/shared_ptr_atomic.h \ > ${bits_srcdir}/shared_ptr_base.h \ >diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >index 2cdd2bd6cae..dd4db926592 100644 >--- a/libstdc++-v3/include/bits/atomic_base.h >+++ b/libstdc++-v3/include/bits/atomic_base.h >@@ -37,6 +37,10 @@ > #include <bits/atomic_lockfree_defines.h> > #include <bits/move.h> > >+#if __cplusplus > 201703L >+#include <bits/atomic_wait.h> >+#endif >+ > #ifndef _GLIBCXX_ALWAYS_INLINE > #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) > #endif >@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return __ret; > } > >- > // Base types for atomics. > template<typename _IntTp> > struct __atomic_base; >@@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __atomic_load(&_M_i, &__v, int(__m)); > return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; > } >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(bool __old, >+ memory_order __m = memory_order_seq_cst) const noexcept >+ { >+ std::__atomic_wait(&_M_i, __old, >+ [__m, this, __old]() >+ { return this->test(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { std::__atomic_notify(&_M_i, false); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { std::__atomic_notify(&_M_i, true); } >+ >+ // TODO add const volatile overload > #endif // C++20 > > _GLIBCXX_ALWAYS_INLINE void >@@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__m)); > } > >+#if __cplusplus > 201703L >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(__int_type __old, >+ memory_order __m = memory_order_seq_cst) const noexcept >+ { >+ std::__atomic_wait(&_M_i, __old, >+ [__m, this, __old] >+ { return this->load(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { std::__atomic_notify(&_M_i, false); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { std::__atomic_notify(&_M_i, true); } >+ >+ // TODO add const volatile overload >+#endif // C++2a >+ > _GLIBCXX_ALWAYS_INLINE __int_type > fetch_add(__int_type __i, > memory_order __m = memory_order_seq_cst) noexcept >@@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > int(__m1), int(__m2)); > } > >+#if __cplusplus > 201703L >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(__pointer_type __old, >+ memory_order __m = memory_order_seq_cst) noexcept >+ { >+ std::__atomic_wait(&_M_p, __old, >+ [__m, this, __old]() >+ { return this->load(__m) != __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { std::__atomic_notify(&_M_p, false); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { std::__atomic_notify(&_M_p, true); } >+ >+ // TODO add const volatile overload >+#endif // C++2a >+ > _GLIBCXX_ALWAYS_INLINE __pointer_type > fetch_add(ptrdiff_t __d, > memory_order __m = memory_order_seq_cst) noexcept >@@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > int(__success), int(__failure)); > } > >+#if __cplusplus > 201703L >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(const _Tp* __ptr, _Val<_Tp> __old, >+ memory_order __m = memory_order_seq_cst) noexcept >+ { >+ std::__atomic_wait(__ptr, __old, >+ [=]() { return load(__ptr, __m) == __old; }); >+ } >+ >+ // TODO add const volatile overload >+ >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one(const _Tp* __ptr) noexcept >+ { std::__atomic_notify(__ptr, false); } >+ >+ // TODO add const volatile overload >+ >+ template<typename _Tp> >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all(const _Tp* __ptr) noexcept >+ { std::__atomic_notify(__ptr, true); } >+ >+ // TODO add const volatile overload >+#endif // C++2a >+ > template<typename _Tp> > _GLIBCXX_ALWAYS_INLINE _Tp > fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept >@@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(&_M_fp, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(&_M_fp); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(&_M_fp); } >+ >+ // TODO add const volatile overload >+ > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) noexcept >@@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > private: > _Tp* _M_ptr; > }; >@@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) const noexcept >@@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > value_type > fetch_add(value_type __i, > memory_order __m = memory_order_seq_cst) const noexcept >@@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __cmpexch_failure_order(__order)); > } > >+ _GLIBCXX_ALWAYS_INLINE void >+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >+ { __atomic_impl::wait(_M_ptr, __old, __m); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_one() const noexcept >+ { __atomic_impl::notify_one(_M_ptr); } >+ >+ // TODO add const volatile overload >+ >+ _GLIBCXX_ALWAYS_INLINE void >+ notify_all() const noexcept >+ { __atomic_impl::notify_all(_M_ptr); } >+ >+ // TODO add const volatile overload >+ > _GLIBCXX_ALWAYS_INLINE value_type > fetch_add(difference_type __d, > memory_order __m = memory_order_seq_cst) const noexcept >diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >new file mode 100644 >index 00000000000..2f57356b366 >--- /dev/null >+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >@@ -0,0 +1,281 @@ >+// -*- C++ -*- header. >+ >+// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >+ >+#pragma GCC system_header >+ >+#include <bits/c++config.h> >+#include <bits/functional_hash.h> >+#include <bits/atomic_wait.h> >+ >+#include <chrono> >+ >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+#include <sys/time.h> >+#endif >+ >+namespace std _GLIBCXX_VISIBILITY(default) >+{ >+_GLIBCXX_BEGIN_NAMESPACE_VERSION >+ >+ enum class __atomic_wait_status { no_timeout, timeout }; >+ >+ namespace __detail >+ { >+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >+ using __platform_wait_clock_t = chrono::steady_clock; >+ >+ template<typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until_impl(__platform_wait_t* __addr, >+ __platform_wait_t __val, >+ const chrono::time_point<__platform_wait_clock_t, >+ _Duration>& __atime) noexcept >+ { >+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >+ >+ struct timespec __rt = >+ { >+ static_cast<std::time_t>(__s.time_since_epoch().count()), >+ static_cast<long>(__ns.count()) >+ }; >+ >+ auto __e = syscall (SYS_futex, __addr, >+ static_cast<int>(__futex_wait_flags::__wait_bitset_private), >+ __val, &__rt, nullptr, >+ static_cast<int>(__futex_wait_flags::__bitset_match_any)); >+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >+ std::terminate(); >+ return (__platform_wait_clock_t::now() < __atime) >+ ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; >+ } >+ >+ template<typename _Clock, typename _Duration> >+ __atomic_wait_status >+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >+ const chrono::time_point<_Clock, _Duration>& __atime) >+ { >+ if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) This case is impossible, since the other overload would be selected if the clock is the __platform_wait_clock_t (unless the caller says __platform_wait_until<__platform_wait_until> to explicitly call this overload, but users can't call this function, and we won't do that). >+ { >+ return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); >+ } ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-09-28 13:36 ` Jonathan Wakely @ 2020-09-28 21:29 ` Thomas Rodgers 2020-09-29 9:44 ` Jonathan Wakely 0 siblings, 1 reply; 50+ messages in thread From: Thomas Rodgers @ 2020-09-28 21:29 UTC (permalink / raw) To: Jonathan Wakely; +Cc: Thomas Rodgers, gcc-patches, libstdc++ Jonathan Wakely writes: > On 11/09/20 16:58 -0700, Thomas Rodgers wrote: >>From: Thomas Rodgers <trodgers@redhat.com> >> >>This patch supercedes both the Add C++2a synchronization support patch >>being replied to *and* the patch adding wait/notify_* to atomic_flag. >> >>Add support for - >> * atomic_flag::wait/notify_one/notify_all >> * atomic::wait/notify_one/notify_all >> * counting_semaphore >> * binary_semaphore >> * latch >> >>libstdc++-v3/ChangeLog: >> >> * include/Makefile.am (bits_headers): Add new header. >> * include/Makefile.in: Regenerate. >> * include/bits/atomic_base.h (__atomic_flag::wait): Define. >> (__atomic_flag::notify_one): Likewise. >> (__atomic_flag::notify_all): Likewise. >> (__atomic_base<_Itp>::wait): Likewise. >> (__atomic_base<_Itp>::notify_one): Likewise. >> (__atomic_base<_Itp>::notify_all): Likewise. >> (__atomic_base<_Ptp*>::wait): Likewise. >> (__atomic_base<_Ptp*>::notify_one): Likewise. >> (__atomic_base<_Ptp*>::notify_all): Likewise. >> (__atomic_impl::wait): Likewise. >> (__atomic_impl::notify_one): Likewise. >> (__atomic_impl::notify_all): Likewise. >> (__atomic_float<_Fp>::wait): Likewise. >> (__atomic_float<_Fp>::notify_one): Likewise. >> (__atomic_float<_Fp>::notify_all): Likewise. >> (__atomic_ref<_Tp>::wait): Likewise. >> (__atomic_ref<_Tp>::notify_one): Likewise. >> (__atomic_ref<_Tp>::notify_all): Likewise. >> (atomic_wait<_Tp>): Likewise. >> (atomic_wait_explicit<_Tp>): Likewise. >> (atomic_notify_one<_Tp>): Likewise. >> (atomic_notify_all<_Tp>): Likewise. >> * include/bits/atomic_wait.h: New file. >> * include/bits/atomic_timed_wait.h: New file. >> * include/bits/semaphore_base.h: New file. >> * include/std/atomic (atomic<bool>::wait): Define. >> (atomic<bool>::wait_one): Likewise. >> (atomic<bool>::wait_all): Likewise. >> (atomic<_Tp>::wait): Likewise. >> (atomic<_Tp>::wait_one): Likewise. >> (atomic<_Tp>::wait_all): Likewise. >> (atomic<_Tp*>::wait): Likewise. >> (atomic<_Tp*>::wait_one): Likewise. >> (atomic<_Tp*>::wait_all): Likewise. >> * include/std/latch: New file. >> * include/std/semaphore: New file. >> * include/std/version: Add __cpp_lib_semaphore and >> __cpp_lib_latch defines. >> * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. >> * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/generic.cc: Liekwise. >> * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. >> * testsuite/29_atomics/atomic_flag/wait_notify/1.cc: New test. >> * testsuite/30_thread/semaphore/1.cc: New test. >> * testsuite/30_thread/semaphore/2.cc: Likewise. >> * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise. >> * testsuite/30_thread/semaphore/try_acquire.cc: Likewise. >> * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise. >> * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise. >> * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise. >> * testsuite/30_thread/latch/1.cc: New test. >> * testsuite/30_thread/latch/2.cc: New test. >> * testsuite/30_thread/latch/3.cc: New test. >>--- >> libstdc++-v3/include/Makefile.am | 5 + >> libstdc++-v3/include/Makefile.in | 5 + >> libstdc++-v3/include/bits/atomic_base.h | 195 +++++++++++- >> libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++ >> libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++ >> libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++ >> libstdc++-v3/include/std/atomic | 73 +++++ >> libstdc++-v3/include/std/latch | 90 ++++++ >> libstdc++-v3/include/std/semaphore | 92 ++++++ >> libstdc++-v3/include/std/version | 2 + >> .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ >> .../29_atomics/atomic/wait_notify/bool.cc | 59 ++++ >> .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ >> .../29_atomics/atomic/wait_notify/generic.cc | 31 ++ >> .../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++ >> .../atomic/wait_notify/integrals.cc | 65 ++++ >> .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ >> .../29_atomics/atomic_flag/wait_notify/1.cc | 61 ++++ >> libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++ >> libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++ >> libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++ >> .../testsuite/30_threads/semaphore/1.cc | 27 ++ >> .../testsuite/30_threads/semaphore/2.cc | 27 ++ >> .../semaphore/least_max_value_neg.cc | 30 ++ >> .../30_threads/semaphore/try_acquire.cc | 55 ++++ >> .../30_threads/semaphore/try_acquire_for.cc | 85 +++++ >> .../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++ >> .../30_threads/semaphore/try_acquire_until.cc | 94 ++++++ >> 28 files changed, 2471 insertions(+), 1 deletion(-) >> create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h >> create mode 100644 libstdc++-v3/include/bits/atomic_wait.h >> create mode 100644 libstdc++-v3/include/bits/semaphore_base.h >> create mode 100644 libstdc++-v3/include/std/latch >> create mode 100644 libstdc++-v3/include/std/semaphore >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_flag/wait_notify/1.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc >> create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc >> >>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am >>index c9df9a9d6c6..9b5b6ed0005 100644 >>--- a/libstdc++-v3/include/Makefile.am >>+++ b/libstdc++-v3/include/Makefile.am >>@@ -52,6 +52,7 @@ std_headers = \ >> ${std_srcdir}/iostream \ >> ${std_srcdir}/istream \ >> ${std_srcdir}/iterator \ >>+ ${std_srcdir}/latch \ >> ${std_srcdir}/limits \ >> ${std_srcdir}/list \ >> ${std_srcdir}/locale \ >>@@ -69,6 +70,7 @@ std_headers = \ >> ${std_srcdir}/ratio \ >> ${std_srcdir}/regex \ >> ${std_srcdir}/scoped_allocator \ >>+ ${std_srcdir}/semaphore \ >> ${std_srcdir}/set \ >> ${std_srcdir}/shared_mutex \ >> ${std_srcdir}/span \ >>@@ -101,6 +103,8 @@ bits_headers = \ >> ${bits_srcdir}/allocated_ptr.h \ >> ${bits_srcdir}/allocator.h \ >> ${bits_srcdir}/atomic_base.h \ >>+ ${bits_srcdir}/atomic_wait.h \ >>+ ${bits_srcdir}/atomic_timed_wait.h \ >> ${bits_srcdir}/atomic_futex.h \ >> ${bits_srcdir}/basic_ios.h \ >> ${bits_srcdir}/basic_ios.tcc \ >>@@ -175,6 +179,7 @@ bits_headers = \ >> ${bits_srcdir}/regex_compiler.tcc \ >> ${bits_srcdir}/regex_executor.h \ >> ${bits_srcdir}/regex_executor.tcc \ >>+ ${bits_srcdir}/semaphore_base.h \ >> ${bits_srcdir}/shared_ptr.h \ >> ${bits_srcdir}/shared_ptr_atomic.h \ >> ${bits_srcdir}/shared_ptr_base.h \ >>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h >>index 2cdd2bd6cae..dd4db926592 100644 >>--- a/libstdc++-v3/include/bits/atomic_base.h >>+++ b/libstdc++-v3/include/bits/atomic_base.h >>@@ -37,6 +37,10 @@ >> #include <bits/atomic_lockfree_defines.h> >> #include <bits/move.h> >> >>+#if __cplusplus > 201703L >>+#include <bits/atomic_wait.h> >>+#endif >>+ >> #ifndef _GLIBCXX_ALWAYS_INLINE >> #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) >> #endif >>@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> return __ret; >> } >> >>- >> // Base types for atomics. >> template<typename _IntTp> >> struct __atomic_base; >>@@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __atomic_load(&_M_i, &__v, int(__m)); >> return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; >> } >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(bool __old, >>+ memory_order __m = memory_order_seq_cst) const noexcept >>+ { >>+ std::__atomic_wait(&_M_i, __old, >>+ [__m, this, __old]() >>+ { return this->test(__m) != __old; }); >>+ } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { std::__atomic_notify(&_M_i, false); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { std::__atomic_notify(&_M_i, true); } >>+ >>+ // TODO add const volatile overload >> #endif // C++20 >> >> _GLIBCXX_ALWAYS_INLINE void >>@@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__m)); >> } >> >>+#if __cplusplus > 201703L >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(__int_type __old, >>+ memory_order __m = memory_order_seq_cst) const noexcept >>+ { >>+ std::__atomic_wait(&_M_i, __old, >>+ [__m, this, __old] >>+ { return this->load(__m) != __old; }); >>+ } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { std::__atomic_notify(&_M_i, false); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { std::__atomic_notify(&_M_i, true); } >>+ >>+ // TODO add const volatile overload >>+#endif // C++2a >>+ >> _GLIBCXX_ALWAYS_INLINE __int_type >> fetch_add(__int_type __i, >> memory_order __m = memory_order_seq_cst) noexcept >>@@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> int(__m1), int(__m2)); >> } >> >>+#if __cplusplus > 201703L >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(__pointer_type __old, >>+ memory_order __m = memory_order_seq_cst) noexcept >>+ { >>+ std::__atomic_wait(&_M_p, __old, >>+ [__m, this, __old]() >>+ { return this->load(__m) != __old; }); >>+ } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { std::__atomic_notify(&_M_p, false); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { std::__atomic_notify(&_M_p, true); } >>+ >>+ // TODO add const volatile overload >>+#endif // C++2a >>+ >> _GLIBCXX_ALWAYS_INLINE __pointer_type >> fetch_add(ptrdiff_t __d, >> memory_order __m = memory_order_seq_cst) noexcept >>@@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> int(__success), int(__failure)); >> } >> >>+#if __cplusplus > 201703L >>+ template<typename _Tp> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(const _Tp* __ptr, _Val<_Tp> __old, >>+ memory_order __m = memory_order_seq_cst) noexcept >>+ { >>+ std::__atomic_wait(__ptr, __old, >>+ [=]() { return load(__ptr, __m) == __old; }); >>+ } >>+ >>+ // TODO add const volatile overload >>+ >>+ template<typename _Tp> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one(const _Tp* __ptr) noexcept >>+ { std::__atomic_notify(__ptr, false); } >>+ >>+ // TODO add const volatile overload >>+ >>+ template<typename _Tp> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all(const _Tp* __ptr) noexcept >>+ { std::__atomic_notify(__ptr, true); } >>+ >>+ // TODO add const volatile overload >>+#endif // C++2a >>+ >> template<typename _Tp> >> _GLIBCXX_ALWAYS_INLINE _Tp >> fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept >>@@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >>+ { __atomic_impl::wait(&_M_fp, __old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { __atomic_impl::notify_one(&_M_fp); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { __atomic_impl::notify_all(&_M_fp); } >>+ >>+ // TODO add const volatile overload >>+ >> value_type >> fetch_add(value_type __i, >> memory_order __m = memory_order_seq_cst) noexcept >>@@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >>+ { __atomic_impl::wait(_M_ptr, __old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { __atomic_impl::notify_one(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { __atomic_impl::notify_all(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >> private: >> _Tp* _M_ptr; >> }; >>@@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >>+ { __atomic_impl::wait(_M_ptr, __old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { __atomic_impl::notify_one(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { __atomic_impl::notify_all(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >> value_type >> fetch_add(value_type __i, >> memory_order __m = memory_order_seq_cst) const noexcept >>@@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept >>+ { __atomic_impl::wait(_M_ptr, __old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { __atomic_impl::notify_one(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { __atomic_impl::notify_all(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >> value_type >> fetch_add(value_type __i, >> memory_order __m = memory_order_seq_cst) const noexcept >>@@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >>+ _GLIBCXX_ALWAYS_INLINE void >>+ wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept >>+ { __atomic_impl::wait(_M_ptr, __old, __m); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_one() const noexcept >>+ { __atomic_impl::notify_one(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >>+ _GLIBCXX_ALWAYS_INLINE void >>+ notify_all() const noexcept >>+ { __atomic_impl::notify_all(_M_ptr); } >>+ >>+ // TODO add const volatile overload >>+ >> _GLIBCXX_ALWAYS_INLINE value_type >> fetch_add(difference_type __d, >> memory_order __m = memory_order_seq_cst) const noexcept >>diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h >>new file mode 100644 >>index 00000000000..2f57356b366 >>--- /dev/null >>+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h >>@@ -0,0 +1,281 @@ >>+// -*- C++ -*- header. >>+ >>+// Copyright (C) 2020 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_timed_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_TIMED_WAIT_H >>+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1 >>+ >>+#pragma GCC system_header >>+ >>+#include <bits/c++config.h> >>+#include <bits/functional_hash.h> >>+#include <bits/atomic_wait.h> >>+ >>+#include <chrono> >>+ >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+#include <sys/time.h> >>+#endif >>+ >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ >>+ enum class __atomic_wait_status { no_timeout, timeout }; >>+ >>+ namespace __detail >>+ { >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ using __platform_wait_clock_t = chrono::steady_clock; >>+ >>+ template<typename _Duration> >>+ __atomic_wait_status >>+ __platform_wait_until_impl(__platform_wait_t* __addr, >>+ __platform_wait_t __val, >>+ const chrono::time_point<__platform_wait_clock_t, >>+ _Duration>& __atime) noexcept >>+ { >>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>+ >>+ struct timespec __rt = >>+ { >>+ static_cast<std::time_t>(__s.time_since_epoch().count()), >>+ static_cast<long>(__ns.count()) >>+ }; >>+ >>+ auto __e = syscall (SYS_futex, __addr, >>+ static_cast<int>(__futex_wait_flags::__wait_bitset_private), >>+ __val, &__rt, nullptr, >>+ static_cast<int>(__futex_wait_flags::__bitset_match_any)); >>+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >>+ std::terminate(); >>+ return (__platform_wait_clock_t::now() < __atime) >>+ ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; >>+ } >>+ >>+ template<typename _Clock, typename _Duration> >>+ __atomic_wait_status >>+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >>+ const chrono::time_point<_Clock, _Duration>& __atime) >>+ { >>+ if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) > > This case is impossible, since the other overload would be selected > if the clock is the __platform_wait_clock_t (unless the caller says > __platform_wait_until<__platform_wait_until> to explicitly call this > overload, but users can't call this function, and we won't do that). > Which overload? >>+ { >>+ return std::__detail::__platform_wait_until_impl(__addr, __val, __atime); >>+ } ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-09-28 21:29 ` Thomas Rodgers @ 2020-09-29 9:44 ` Jonathan Wakely 0 siblings, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-09-29 9:44 UTC (permalink / raw) To: Thomas Rodgers; +Cc: Thomas Rodgers, gcc-patches, libstdc++ On 28/09/20 14:29 -0700, Thomas Rodgers wrote: >>>+ template<typename _Duration> >>>+ __atomic_wait_status >>>+ __platform_wait_until_impl(__platform_wait_t* __addr, >>>+ __platform_wait_t __val, >>>+ const chrono::time_point<__platform_wait_clock_t, >>>+ _Duration>& __atime) noexcept >>>+ { >>>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime); >>>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); >>>+ >>>+ struct timespec __rt = >>>+ { >>>+ static_cast<std::time_t>(__s.time_since_epoch().count()), >>>+ static_cast<long>(__ns.count()) >>>+ }; >>>+ >>>+ auto __e = syscall (SYS_futex, __addr, >>>+ static_cast<int>(__futex_wait_flags::__wait_bitset_private), >>>+ __val, &__rt, nullptr, >>>+ static_cast<int>(__futex_wait_flags::__bitset_match_any)); >>>+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT)) >>>+ std::terminate(); >>>+ return (__platform_wait_clock_t::now() < __atime) >>>+ ? __atomic_wait_status::no_timeout : __atomic_wait_status::timeout; >>>+ } >>>+ >>>+ template<typename _Clock, typename _Duration> >>>+ __atomic_wait_status >>>+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val, >>>+ const chrono::time_point<_Clock, _Duration>& __atime) >>>+ { >>>+ if constexpr (is_same_v<__platform_wait_clock_t, _Clock>) >> >> This case is impossible, since the other overload would be selected >> if the clock is the __platform_wait_clock_t (unless the caller says >> __platform_wait_until<__platform_wait_until> to explicitly call this >> overload, but users can't call this function, and we won't do that). >> > >Which overload? I must have misread __platform_wait_until_impl above as __platform_wait_until. Ignore this comment, sorry! ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support @ 2020-11-21 23:54 David Edelsohn 2020-11-22 1:27 ` Jonathan Wakely 0 siblings, 1 reply; 50+ messages in thread From: David Edelsohn @ 2020-11-21 23:54 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, GCC Patches, Thomas Rogers I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's fixes. And a few c++ failures with similar symptoms. I'm not certain that it is due to this patch, but it's the likely suspect. FAIL: 17_intro/headers/c++2020/all_attributes.cc (test for excess errors) FAIL: 17_intro/headers/c++2020/all_no_exceptions.cc (test for excess errors) FAIL: 17_intro/headers/c++2020/all_no_rtti.cc (test for excess errors) FAIL: 17_intro/headers/c++2020/all_pedantic_errors.cc (test for excess errors) FAIL: 17_intro/headers/c++2020/operator_names.cc (test for excess errors) FAIL: 17_intro/headers/c++2020/stdc++.cc (test for excess errors) FAIL: 17_intro/headers/c++2020/stdc++_multiple_inclusion.cc (test for excess errors) FAIL: 20_util/allocator/rebind_c++20.cc (test for excess errors) FAIL: 20_util/allocator/requirements/constexpr.cc (test for excess errors) FAIL: 20_util/allocator/requirements/typedefs_c++20.cc (test for excess errors) FAIL: 20_util/allocator_traits/header.cc (test for excess errors) FAIL: 20_util/allocator_traits/members/92878_92947.cc (test for excess errors) UNRESOLVED: 20_util/allocator_traits/members/92878_92947.cc compilation failed to produce executable FAIL: 20_util/assume_aligned/1.cc (test for excess errors) UNRESOLVED: 20_util/assume_aligned/1.cc compilation failed to produce executable FAIL: 20_util/assume_aligned/2_neg.cc (test for excess errors) FAIL: 20_util/assume_aligned/3.cc (test for excess errors) UNRESOLVED: 20_util/assume_aligned/3.cc scan-assembler-not undefined FAIL: 20_util/assume_aligned/97132.cc (test for excess errors) FAIL: 20_util/function_objects/bind_front/2.cc (test for excess errors) UNRESOLVED: 20_util/function_objects/bind_front/2.cc compilation failed to produce executable FAIL: 20_util/pair/comparison_operators/constexpr_c++20.cc (test for excess errors) FAIL: 20_util/pair/cons/92878_92947.cc (test for excess errors) UNRESOLVED: 20_util/pair/cons/92878_92947.cc compilation failed to produce executable etc. The errors all are of the form: /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:125: error: 'mutex' is not a member of 'std' /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:125: error: template argument 1 is invalid /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:125: error: '<expression error>' in namespace 'std' does not name a type /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:126: error: '__lock_t' does not name a type; did you mean 'clock_t'? /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:132: error: '__gthread_cond_t' does not name a type; did you mean 'pthread_cond_t'? /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:135: error: '_M_cond' was not declared in this scope /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:135: error: '__GTHREAD_COND_INIT_FUNCTION' was not declared in this scope /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:164: error: '__lock_t' is not a member of 'std::__detail::__waiters' /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:165: error: '_M_cv' was not declared in this scope /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:165: error: '__l' was not declared in this scope; did you mean '__lg'? /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:165: error: '__gthread_cond_wait' was not declared in this scope; did you mean 'pthread_cond_t'? /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:191: error: '_M_cv' was not declared in this scope /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++-v3/include/bits/atomic_wait.h:191: error: '__gthread_cond_broadcast' was not declared in this scope /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++v3/include/bits/atomic_timed_wait.h:144: error: '__gthread_cond_t' was not declared in this scope; did you mean 'pthread_cond_t'? /tmp/GCC/powerpc-ibm-aix7.2.3.0/libstdc++v3/include/bits/atomic_timed_wait.h:144: error: '__cv' was not declared in this scope ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-21 23:54 David Edelsohn @ 2020-11-22 1:27 ` Jonathan Wakely 2020-11-22 12:29 ` Iain Sandoe 2020-11-23 18:30 ` Jonathan Wakely 0 siblings, 2 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-22 1:27 UTC (permalink / raw) To: David Edelsohn; +Cc: Jonathan Wakely, libstdc++, GCC Patches [-- Attachment #1: Type: text/plain, Size: 424 bytes --] On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ <libstdc++@gcc.gnu.org> wrote: > > I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's > fixes. And a few c++ failures with similar symptoms. I'm not certain > that it is due to this patch, but it's the likely suspect. Yes, it's that patch. This should fix most of those errors, but I haven't finished testing it, and can't commit it now anyway. [-- Attachment #2: patch.txt --] [-- Type: text/plain, Size: 16676 bytes --] commit c304e59c2fd8c500232c4c88b632e051e481991f Author: Jonathan Wakely <jwakely@redhat.com> Date: Sun Nov 22 01:00:46 2020 libstdc++: make atomic waiting depend on gthreads or futexes libstdc++-v3/ChangeLog: * include/bits/atomic_wait.h: Do not define anything unless gthreads or futexes are available. * include/bits/atomic_timed_wait.h: Likewise. * include/bits/semaphore_base.h: Likewise. * include/std/semaphore: Likewise. * include/bits/atomic_base.h (atomic_flag::wait) (atomic_flag::notify_one, atomic_flag::notify_all) (__atomic_base<I>::wait, __atomic_base<I>::notify_one) (__atomic_base<I>::notify_all, __atomic_base<P*>::wait) (__atomic_base<P*>::notify_one, __atomic_base<P*>::notify_all) (__atomic_impl::wait, __atomic_impl::notify_one) (__atomic_impl::notify_all, __atomic_float::wait) (__atomic_float::notify_one, __atomic_float::notify_all) (__atomic_ref::wait, __atomic_ref::notify_one) (__atomic_ref::notify_all): Only define if gthreads or futexes are available. * include/std/atomic (atomic::wait, atomic::notify_one) (atomic::notify_all): Likewise. diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 7de02f169977..d7db8612889e 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -230,6 +230,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept @@ -252,6 +253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(&_M_i, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -603,6 +605,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept @@ -625,6 +628,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(&_M_i, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a _GLIBCXX_ALWAYS_INLINE __int_type @@ -897,6 +901,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept @@ -919,6 +924,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(&_M_p, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a _GLIBCXX_ALWAYS_INLINE __pointer_type @@ -1010,6 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX template<typename _Tp> _GLIBCXX_ALWAYS_INLINE void wait(const _Tp* __ptr, _Val<_Tp> __old, @@ -1034,6 +1041,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(__ptr, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a template<typename _Tp> @@ -1289,6 +1297,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(&_M_fp, __old, __m); } @@ -1306,6 +1315,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(&_M_fp); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX value_type fetch_add(value_type __i, @@ -1444,6 +1454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1461,6 +1472,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX private: _Tp* _M_ptr; @@ -1557,6 +1569,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1574,6 +1587,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX value_type fetch_add(value_type __i, @@ -1730,6 +1744,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1747,6 +1762,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX value_type fetch_add(value_type __i, @@ -1857,6 +1873,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1874,6 +1891,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h index 405f7e93ca85..b13f8aa12861 100644 --- a/libstdc++-v3/include/bits/atomic_timed_wait.h +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -33,6 +33,7 @@ #pragma GCC system_header #include <bits/c++config.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <bits/functional_hash.h> #include <bits/atomic_wait.h> @@ -286,4 +287,5 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // _GLIBCXX_ATOMIC_TIMED_WAIT_H diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 7b2682a577ef..a40cff124d7d 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -33,6 +33,7 @@ #pragma GCC system_header #include <bits/c++config.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <bits/functional_hash.h> #include <bits/gthr.h> #include <bits/std_mutex.h> @@ -106,7 +107,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } - template<typename _Tp> + template<typename _Tp> void __platform_notify(const _Tp* __addr, bool __all) noexcept { @@ -175,11 +176,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool _M_waiting() const noexcept - { - __platform_wait_t __res; - __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); - return __res; - } + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } void _M_notify(bool __all) noexcept @@ -305,4 +306,5 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // _GLIBCXX_ATOMIC_WAIT_H diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index 78a0b6ba26e6..5e29d3783fe1 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -33,6 +33,7 @@ #pragma GCC system_header #include <bits/c++config.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <bits/atomic_base.h> #include <bits/atomic_timed_wait.h> @@ -161,30 +162,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept { return _M_try_acquire_until(__clock_t::now() + __rtime); } - private: - sem_t _M_semaphore; - }; + private: + sem_t _M_semaphore; + }; #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE - template<typename _Tp> - struct __atomic_semaphore + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static_assert(__gnu_cxx::__int_traits<_Tp>::__max + <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_counter(__count) { - static_assert(std::is_integral_v<_Tp>); - static_assert(__gnu_cxx::__int_traits<_Tp>::__max - <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); - static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + __glibcxx_assert(__count >= 0 && __count <= _S_max); + } - explicit __atomic_semaphore(_Tp __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; - __atomic_semaphore(const __atomic_semaphore&) = delete; - __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + std::__atomic_wait(&_M_counter, __old, __pred); + } - _GLIBCXX_ALWAYS_INLINE void - _M_acquire() noexcept + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire); + auto const __pred = [this, __old] + { + if (__old == 0) + return false; + + auto __prev = __old; + return __atomic_impl::compare_exchange_weak(&this->_M_counter, + __prev, __prev - 1, + memory_order::acquire, + memory_order::release); + }; + return std::__atomic_spin(__pred); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, + _Duration>& __atime) noexcept { auto const __pred = [this] { @@ -193,51 +232,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__old == 0) return false; return __atomic_impl::compare_exchange_strong(&this->_M_counter, - __old, __old - 1, - memory_order::acquire, - memory_order::release); + __old, __old - 1, + memory_order::acquire, + memory_order::release); }; + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); - std::__atomic_wait(&_M_counter, __old, __pred); - } - - bool - _M_try_acquire() noexcept - { - auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire); - auto const __pred = [this, __old] - { - if (__old == 0) - return false; - - auto __prev = __old; - return __atomic_impl::compare_exchange_weak(&this->_M_counter, - __prev, __prev - 1, - memory_order::acquire, - memory_order::release); - }; - return std::__atomic_spin(__pred); - } - - template<typename _Clock, typename _Duration> - _GLIBCXX_ALWAYS_INLINE bool - _M_try_acquire_until(const chrono::time_point<_Clock, - _Duration>& __atime) noexcept - { - auto const __pred = [this] - { - auto __old = __atomic_impl::load(&this->_M_counter, - memory_order::acquire); - if (__old == 0) - return false; - return __atomic_impl::compare_exchange_strong(&this->_M_counter, - __old, __old - 1, - memory_order::acquire, - memory_order::release); - }; - - auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); - return __atomic_wait_until(&_M_counter, __old, __pred, __atime); + return __atomic_wait_until(&_M_counter, __old, __pred, __atime); } template<typename _Rep, typename _Period> @@ -299,4 +300,5 @@ template<ptrdiff_t __least_max_value> _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // _GLIBCXX_SEMAPHORE_BASE_H diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 5afe33b41d9b..fe4de244f858 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -165,6 +165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_base.compare_exchange_strong(__i1, __i2, __m); } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept { _M_base.wait(__old, __m); } @@ -176,6 +177,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() const noexcept { _M_base.notify_all(); } +#endif // GTHREADS || LINUX_FUTEX #endif }; @@ -379,6 +381,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { std::__atomic_wait(&_M_i, __old, @@ -399,7 +402,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() const noexcept { std::__atomic_notify(&_M_i, true); } -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // C++20 }; #undef _GLIBCXX20_INIT @@ -640,6 +644,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept { _M_b.wait(__old, __m); } @@ -651,6 +656,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() const noexcept { _M_b.notify_all(); } +#endif // GTHREADS || LINUX_FUTEX #endif __pointer_type fetch_add(ptrdiff_t __d, @@ -1406,6 +1412,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX template<typename _Tp> inline void atomic_wait(const atomic<_Tp>* __a, @@ -1429,6 +1436,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION atomic_notify_all(atomic<_Tp>* __a) noexcept { __a->notify_all(); } +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a // Function templates for atomic_integral and atomic_pointer operations only. diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore index b4facde4ea1f..f4b83ab6ae3f 100644 --- a/libstdc++-v3/include/std/semaphore +++ b/libstdc++-v3/include/std/semaphore @@ -32,14 +32,16 @@ #pragma GCC system_header #if __cplusplus > 201703L -#define __cpp_lib_semaphore 201907L #include <bits/semaphore_base.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <ext/numeric_traits.h> namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#define __cpp_lib_semaphore 201907L + template<ptrdiff_t __least_max_value = __gnu_cxx::__int_traits<ptrdiff_t>::__max> class counting_semaphore @@ -88,5 +90,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using binary_semaphore = std::counting_semaphore<1>; _GLIBCXX_END_NAMESPACE_VERSION } // namespace -#endif // __cplusplus > 201703L +#endif // GTHREADS || LINUX_FUTEX +#endif // C++20 #endif // _GLIBCXX_SEMAPHORE ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 1:27 ` Jonathan Wakely @ 2020-11-22 12:29 ` Iain Sandoe 2020-11-22 13:37 ` Jonathan Wakely 2020-11-23 18:30 ` Jonathan Wakely 1 sibling, 1 reply; 50+ messages in thread From: Iain Sandoe @ 2020-11-22 12:29 UTC (permalink / raw) To: Jonathan Wakely; +Cc: David Edelsohn, Jonathan Wakely, libstdc++, GCC Patches thanks for looking at this over the weekend. Jonathan Wakely via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ > <libstdc++@gcc.gnu.org> wrote: >> I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's >> fixes. And a few c++ failures with similar symptoms. I'm not certain >> that it is due to this patch, but it's the likely suspect. > Yes, it's that patch. > > This should fix most of those errors, but I haven't finished testing > it, and can't commit it now anyway. > <patch.txt> with r11-5235 + this patch there are still quite a few fails on Darwin - but all seem to be the same ( so maybe only problem ;) ): “sem_timedwait was not declared in this scope”. It looks like the semaphore header is optional in SUSv3 (AFAIK that’s still the claimed edition for Darwin) - and although Darwin has the semaphore header, it doesn’t seem to have an impl. of sem_timedwait. just: int sem_trywait(sem_t *); int sem_wait(sem_t *) ; thanks Iain ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 12:29 ` Iain Sandoe @ 2020-11-22 13:37 ` Jonathan Wakely 2020-11-23 18:31 ` Jonathan Wakely 0 siblings, 1 reply; 50+ messages in thread From: Jonathan Wakely @ 2020-11-22 13:37 UTC (permalink / raw) To: Iain Sandoe; +Cc: David Edelsohn, Jonathan Wakely, libstdc++, GCC Patches On Sun, 22 Nov 2020, 12:29 Iain Sandoe, <idsandoe@googlemail.com> wrote: > thanks for looking at this over the weekend. > > Jonathan Wakely via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > > On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ > > <libstdc++@gcc.gnu.org> wrote: > >> I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's > >> fixes. And a few c++ failures with similar symptoms. I'm not certain > >> that it is due to this patch, but it's the likely suspect. > > Yes, it's that patch. > > > > This should fix most of those errors, but I haven't finished testing > > it, and can't commit it now anyway. > > <patch.txt> > > with r11-5235 + this patch there are still quite a few fails on Darwin - > but > all seem to be the same ( so maybe only problem ;) ): > “sem_timedwait was not declared in this scope”. > > It looks like the semaphore header is optional in SUSv3 (AFAIK that’s still > the claimed edition for Darwin) - and although Darwin has the semaphore > header, it doesn’t seem to have an impl. of sem_timedwait. > > just: > int sem_trywait(sem_t *); > int sem_wait(sem_t *) ; > It probably depends on the _POSIX_TIMEOUTS option which MacOS doesn't support (optional in POSIX 2001, but not 2008). > > ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 13:37 ` Jonathan Wakely @ 2020-11-23 18:31 ` Jonathan Wakely 2020-11-23 18:31 ` Jonathan Wakely 2020-11-24 1:48 ` David Edelsohn 0 siblings, 2 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 18:31 UTC (permalink / raw) To: Jonathan Wakely; +Cc: Iain Sandoe, libstdc++, GCC Patches, David Edelsohn On 22/11/20 13:37 +0000, Jonathan Wakely via Libstdc++ wrote: >On Sun, 22 Nov 2020, 12:29 Iain Sandoe, <idsandoe@googlemail.com> wrote: > >> thanks for looking at this over the weekend. >> >> Jonathan Wakely via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: >> >> > On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ >> > <libstdc++@gcc.gnu.org> wrote: >> >> I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's >> >> fixes. And a few c++ failures with similar symptoms. I'm not certain >> >> that it is due to this patch, but it's the likely suspect. >> > Yes, it's that patch. >> > >> > This should fix most of those errors, but I haven't finished testing >> > it, and can't commit it now anyway. >> > <patch.txt> >> >> with r11-5235 + this patch there are still quite a few fails on Darwin - >> but >> all seem to be the same ( so maybe only problem ;) ): >> “sem_timedwait was not declared in this scope”. >> >> It looks like the semaphore header is optional in SUSv3 (AFAIK that’s still >> the claimed edition for Darwin) - and although Darwin has the semaphore >> header, it doesn’t seem to have an impl. of sem_timedwait. >> >> just: >> int sem_trywait(sem_t *); >> int sem_wait(sem_t *) ; >> > > >It probably depends on the _POSIX_TIMEOUTS option which MacOS doesn't >support (optional in POSIX 2001, but not 2008). Hopefully this fixes it, but I haven't tested it on darwin, only linux, aix and solaris. Committed. ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-23 18:31 ` Jonathan Wakely @ 2020-11-23 18:31 ` Jonathan Wakely 2020-11-24 1:48 ` David Edelsohn 1 sibling, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 18:31 UTC (permalink / raw) To: Jonathan Wakely; +Cc: Iain Sandoe, libstdc++, GCC Patches, David Edelsohn [-- Attachment #1: Type: text/plain, Size: 1558 bytes --] On 23/11/20 18:31 +0000, Jonathan Wakely wrote: >On 22/11/20 13:37 +0000, Jonathan Wakely via Libstdc++ wrote: >>On Sun, 22 Nov 2020, 12:29 Iain Sandoe, <idsandoe@googlemail.com> wrote: >> >>>thanks for looking at this over the weekend. >>> >>>Jonathan Wakely via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: >>> >>>> On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ >>>> <libstdc++@gcc.gnu.org> wrote: >>>>> I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's >>>>> fixes. And a few c++ failures with similar symptoms. I'm not certain >>>>> that it is due to this patch, but it's the likely suspect. >>>> Yes, it's that patch. >>>> >>>> This should fix most of those errors, but I haven't finished testing >>>> it, and can't commit it now anyway. >>>> <patch.txt> >>> >>>with r11-5235 + this patch there are still quite a few fails on Darwin - >>>but >>>all seem to be the same ( so maybe only problem ;) ): >>> “sem_timedwait was not declared in this scope”. >>> >>>It looks like the semaphore header is optional in SUSv3 (AFAIK that’s still >>>the claimed edition for Darwin) - and although Darwin has the semaphore >>>header, it doesn’t seem to have an impl. of sem_timedwait. >>> >>>just: >>>int sem_trywait(sem_t *); >>>int sem_wait(sem_t *) ; >>> >> >> >>It probably depends on the _POSIX_TIMEOUTS option which MacOS doesn't >>support (optional in POSIX 2001, but not 2008). > >Hopefully this fixes it, but I haven't tested it on darwin, only >linux, aix and solaris. > >Committed. With the patch this time ... [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 2736 bytes --] commit 92b47a321e14f98c524f6e67e7ecabad5afa7886 Author: Jonathan Wakely <jwakely@redhat.com> Date: Mon Nov 23 17:17:09 2020 libstdc++: Add configure checks for semaphores This moves the checks for POSIX semaphores to configure time. As well as requiring <semaphore.h> and SEM_VALUE_MAX, we also require the sem_timedwait function. That was only optional in POSIX 2001 (and is absent on Darwin). libstdc++-v3/ChangeLog: * acinclude.m4 (GLIBCXX_CHECK_GTHREADS): Check for * config.h.in: Regenerate. * configure: Regenerate. * include/bits/semaphore_base.h (_GLIBCXX_HAVE_POSIX_SEMAPHORE): Check autoconf macro instead of defining it here. diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 486347b34d94..a4a0bb840181 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -4089,6 +4089,43 @@ AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [ fi fi + AC_CHECK_HEADER(semaphore.h, [ + AC_MSG_CHECKING([for POSIX Semaphores and sem_timedwait]) + AC_TRY_COMPILE([ + #include <unistd.h> + #include <semaphore.h> + #include <limits.h> + ], + [ + #if !defined _POSIX_TIMEOUTS || _POSIX_TIMEOUTS <= 0 + # error "POSIX Timeouts option not supported" + #elif !defined _POSIX_SEMAPHORES || _POSIX_SEMAPHORES <= 0 + # error "POSIX Semaphores option not supported" + #else + #if defined SEM_VALUE_MAX + constexpr int sem_value_max = SEM_VALUE_MAX; + #elif defined _POSIX_SEM_VALUE_MAX + constexpr int sem_value_max = _POSIX_SEM_VALUE_MAX; + #else + # error "SEM_VALUE_MAX not available" + #endif + sem_t sem; + sem_init(&sem, 0, sem_value_max); + struct timespec ts = { 0 }; + sem_timedwait(&sem, &ts); + #endif + ], + [ac_have_posix_semaphore=yes], + [ac_have_posix_semaphore=no])], + [ac_have_posix_semaphore=no]) + + if test $ac_have_posix_semaphore = yes ; then + AC_DEFINE(_GLIBCXX_HAVE_POSIX_SEMAPHORE, + 1, + [Define to 1 if POSIX Semaphores with sem_timedwait are available in <semaphore.h>.]) + fi + AC_MSG_RESULT([$ac_have_posix_semaphore]) + CXXFLAGS="$ac_save_CXXFLAGS" AC_LANG_RESTORE ]) diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index 5e29d3783fe1..0692f95f24f2 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -39,11 +39,9 @@ #include <ext/numeric_traits.h> -#if __has_include(<semaphore.h>) +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE +# include <limits.h> # include <semaphore.h> -# if defined SEM_VALUE_MAX || _POSIX_SEM_VALUE_MAX -# define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 -# endif #endif #include <chrono> ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-23 18:31 ` Jonathan Wakely 2020-11-23 18:31 ` Jonathan Wakely @ 2020-11-24 1:48 ` David Edelsohn 1 sibling, 0 replies; 50+ messages in thread From: David Edelsohn @ 2020-11-24 1:48 UTC (permalink / raw) To: Jonathan Wakely; +Cc: Jonathan Wakely, Iain Sandoe, libstdc++, GCC Patches On Mon, Nov 23, 2020 at 1:31 PM Jonathan Wakely <jwakely@redhat.com> wrote: > > On 22/11/20 13:37 +0000, Jonathan Wakely via Libstdc++ wrote: > >On Sun, 22 Nov 2020, 12:29 Iain Sandoe, <idsandoe@googlemail.com> wrote: > > > >> thanks for looking at this over the weekend. > >> > >> Jonathan Wakely via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > >> > >> > On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ > >> > <libstdc++@gcc.gnu.org> wrote: > >> >> I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's > >> >> fixes. And a few c++ failures with similar symptoms. I'm not certain > >> >> that it is due to this patch, but it's the likely suspect. > >> > Yes, it's that patch. > >> > > >> > This should fix most of those errors, but I haven't finished testing > >> > it, and can't commit it now anyway. > >> > <patch.txt> > >> > >> with r11-5235 + this patch there are still quite a few fails on Darwin - > >> but > >> all seem to be the same ( so maybe only problem ;) ): > >> “sem_timedwait was not declared in this scope”. > >> > >> It looks like the semaphore header is optional in SUSv3 (AFAIK that’s still > >> the claimed edition for Darwin) - and although Darwin has the semaphore > >> header, it doesn’t seem to have an impl. of sem_timedwait. > >> > >> just: > >> int sem_trywait(sem_t *); > >> int sem_wait(sem_t *) ; > >> > > > > > >It probably depends on the _POSIX_TIMEOUTS option which MacOS doesn't > >support (optional in POSIX 2001, but not 2008). > > Hopefully this fixes it, but I haven't tested it on darwin, only > linux, aix and solaris. This patch fixed the problems that I saw on AIX. Thanks for addressing it! Thanks, David ^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] libstdc++: Add C++2a synchronization support 2020-11-22 1:27 ` Jonathan Wakely 2020-11-22 12:29 ` Iain Sandoe @ 2020-11-23 18:30 ` Jonathan Wakely 1 sibling, 0 replies; 50+ messages in thread From: Jonathan Wakely @ 2020-11-23 18:30 UTC (permalink / raw) To: Jonathan Wakely; +Cc: David Edelsohn, libstdc++, GCC Patches [-- Attachment #1: Type: text/plain, Size: 598 bytes --] On 22/11/20 01:27 +0000, Jonathan Wakely via Libstdc++ wrote: >On Sat, 21 Nov 2020 at 23:55, David Edelsohn via Libstdc++ ><libstdc++@gcc.gnu.org> wrote: >> >> I am seeing 93 new libstdc++ failures on AIX, even after Jonathan's >> fixes. And a few c++ failures with similar symptoms. I'm not certain >> that it is due to this patch, but it's the likely suspect. >Yes, it's that patch. > >This should fix most of those errors, but I haven't finished testing >it, and can't commit it now anyway. I've committed this now. Tested powerpc64le-linux and (lightly) on powerpc-aix and sparc-solaris. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 17290 bytes --] commit 183ae52b226898cc34aa51d4153cf0c006212a8a Author: Jonathan Wakely <jwakely@redhat.com> Date: Sun Nov 22 01:00:46 2020 libstdc++: make atomic waiting depend on gthreads or futexes libstdc++-v3/ChangeLog: * include/bits/atomic_wait.h: Do not define anything unless gthreads or futexes are available. * include/bits/atomic_timed_wait.h: Likewise. * include/bits/semaphore_base.h: Likewise. * include/std/semaphore: Likewise. * include/bits/atomic_base.h (atomic_flag::wait) (atomic_flag::notify_one, atomic_flag::notify_all) (__atomic_base<I>::wait, __atomic_base<I>::notify_one) (__atomic_base<I>::notify_all, __atomic_base<P*>::wait) (__atomic_base<P*>::notify_one, __atomic_base<P*>::notify_all) (__atomic_impl::wait, __atomic_impl::notify_one) (__atomic_impl::notify_all, __atomic_float::wait) (__atomic_float::notify_one, __atomic_float::notify_all) (__atomic_ref::wait, __atomic_ref::notify_one) (__atomic_ref::notify_all): Only define if gthreads or futexes are available. * include/std/atomic (atomic::wait, atomic::notify_one) (atomic::notify_all): Likewise. * include/std/version (__cpp_lib_semaphore): Define conditionally. diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 7de02f169977..d7db8612889e 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -230,6 +230,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL; } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept @@ -252,6 +253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(&_M_i, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++20 _GLIBCXX_ALWAYS_INLINE void @@ -603,6 +605,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept @@ -625,6 +628,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(&_M_i, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a _GLIBCXX_ALWAYS_INLINE __int_type @@ -897,6 +901,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept @@ -919,6 +924,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(&_M_p, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a _GLIBCXX_ALWAYS_INLINE __pointer_type @@ -1010,6 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX template<typename _Tp> _GLIBCXX_ALWAYS_INLINE void wait(const _Tp* __ptr, _Val<_Tp> __old, @@ -1034,6 +1041,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::__atomic_notify(__ptr, true); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a template<typename _Tp> @@ -1289,6 +1297,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(&_M_fp, __old, __m); } @@ -1306,6 +1315,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(&_M_fp); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX value_type fetch_add(value_type __i, @@ -1444,6 +1454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1461,6 +1472,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX private: _Tp* _M_ptr; @@ -1557,6 +1569,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1574,6 +1587,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX value_type fetch_add(value_type __i, @@ -1730,6 +1744,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1747,6 +1762,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX value_type fetch_add(value_type __i, @@ -1857,6 +1873,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { __atomic_impl::wait(_M_ptr, __old, __m); } @@ -1874,6 +1891,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { __atomic_impl::notify_all(_M_ptr); } // TODO add const volatile overload +#endif // GTHREADS || LINUX_FUTEX _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h index 405f7e93ca85..b13f8aa12861 100644 --- a/libstdc++-v3/include/bits/atomic_timed_wait.h +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h @@ -33,6 +33,7 @@ #pragma GCC system_header #include <bits/c++config.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <bits/functional_hash.h> #include <bits/atomic_wait.h> @@ -286,4 +287,5 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // _GLIBCXX_ATOMIC_TIMED_WAIT_H diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index 7b2682a577ef..a40cff124d7d 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -33,6 +33,7 @@ #pragma GCC system_header #include <bits/c++config.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <bits/functional_hash.h> #include <bits/gthr.h> #include <bits/std_mutex.h> @@ -106,7 +107,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } - template<typename _Tp> + template<typename _Tp> void __platform_notify(const _Tp* __addr, bool __all) noexcept { @@ -175,11 +176,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool _M_waiting() const noexcept - { - __platform_wait_t __res; - __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); - return __res; - } + { + __platform_wait_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } void _M_notify(bool __all) noexcept @@ -305,4 +306,5 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // _GLIBCXX_ATOMIC_WAIT_H diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index 78a0b6ba26e6..5e29d3783fe1 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -33,6 +33,7 @@ #pragma GCC system_header #include <bits/c++config.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <bits/atomic_base.h> #include <bits/atomic_timed_wait.h> @@ -161,30 +162,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept { return _M_try_acquire_until(__clock_t::now() + __rtime); } - private: - sem_t _M_semaphore; - }; + private: + sem_t _M_semaphore; + }; #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE - template<typename _Tp> - struct __atomic_semaphore + template<typename _Tp> + struct __atomic_semaphore + { + static_assert(std::is_integral_v<_Tp>); + static_assert(__gnu_cxx::__int_traits<_Tp>::__max + <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); + static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + + explicit __atomic_semaphore(_Tp __count) noexcept + : _M_counter(__count) { - static_assert(std::is_integral_v<_Tp>); - static_assert(__gnu_cxx::__int_traits<_Tp>::__max - <= __gnu_cxx::__int_traits<ptrdiff_t>::__max); - static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max; + __glibcxx_assert(__count >= 0 && __count <= _S_max); + } - explicit __atomic_semaphore(_Tp __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; - __atomic_semaphore(const __atomic_semaphore&) = delete; - __atomic_semaphore& operator=(const __atomic_semaphore&) = delete; + _GLIBCXX_ALWAYS_INLINE void + _M_acquire() noexcept + { + auto const __pred = [this] + { + auto __old = __atomic_impl::load(&this->_M_counter, + memory_order::acquire); + if (__old == 0) + return false; + return __atomic_impl::compare_exchange_strong(&this->_M_counter, + __old, __old - 1, + memory_order::acquire, + memory_order::release); + }; + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); + std::__atomic_wait(&_M_counter, __old, __pred); + } - _GLIBCXX_ALWAYS_INLINE void - _M_acquire() noexcept + bool + _M_try_acquire() noexcept + { + auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire); + auto const __pred = [this, __old] + { + if (__old == 0) + return false; + + auto __prev = __old; + return __atomic_impl::compare_exchange_weak(&this->_M_counter, + __prev, __prev - 1, + memory_order::acquire, + memory_order::release); + }; + return std::__atomic_spin(__pred); + } + + template<typename _Clock, typename _Duration> + _GLIBCXX_ALWAYS_INLINE bool + _M_try_acquire_until(const chrono::time_point<_Clock, + _Duration>& __atime) noexcept { auto const __pred = [this] { @@ -193,51 +232,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__old == 0) return false; return __atomic_impl::compare_exchange_strong(&this->_M_counter, - __old, __old - 1, - memory_order::acquire, - memory_order::release); + __old, __old - 1, + memory_order::acquire, + memory_order::release); }; + auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); - std::__atomic_wait(&_M_counter, __old, __pred); - } - - bool - _M_try_acquire() noexcept - { - auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire); - auto const __pred = [this, __old] - { - if (__old == 0) - return false; - - auto __prev = __old; - return __atomic_impl::compare_exchange_weak(&this->_M_counter, - __prev, __prev - 1, - memory_order::acquire, - memory_order::release); - }; - return std::__atomic_spin(__pred); - } - - template<typename _Clock, typename _Duration> - _GLIBCXX_ALWAYS_INLINE bool - _M_try_acquire_until(const chrono::time_point<_Clock, - _Duration>& __atime) noexcept - { - auto const __pred = [this] - { - auto __old = __atomic_impl::load(&this->_M_counter, - memory_order::acquire); - if (__old == 0) - return false; - return __atomic_impl::compare_exchange_strong(&this->_M_counter, - __old, __old - 1, - memory_order::acquire, - memory_order::release); - }; - - auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed); - return __atomic_wait_until(&_M_counter, __old, __pred, __atime); + return __atomic_wait_until(&_M_counter, __old, __pred, __atime); } template<typename _Rep, typename _Period> @@ -299,4 +300,5 @@ template<ptrdiff_t __least_max_value> _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // _GLIBCXX_SEMAPHORE_BASE_H diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 5afe33b41d9b..fe4de244f858 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -165,6 +165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_base.compare_exchange_strong(__i1, __i2, __m); } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept { _M_base.wait(__old, __m); } @@ -176,6 +177,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() const noexcept { _M_base.notify_all(); } +#endif // GTHREADS || LINUX_FUTEX #endif }; @@ -379,6 +381,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept { std::__atomic_wait(&_M_i, __old, @@ -399,7 +402,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() const noexcept { std::__atomic_notify(&_M_i, true); } -#endif +#endif // GTHREADS || LINUX_FUTEX +#endif // C++20 }; #undef _GLIBCXX20_INIT @@ -640,6 +644,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept { _M_b.wait(__old, __m); } @@ -651,6 +656,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() const noexcept { _M_b.notify_all(); } +#endif // GTHREADS || LINUX_FUTEX #endif __pointer_type fetch_add(ptrdiff_t __d, @@ -1406,6 +1412,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus > 201703L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX template<typename _Tp> inline void atomic_wait(const atomic<_Tp>* __a, @@ -1429,6 +1436,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION atomic_notify_all(atomic<_Tp>* __a) noexcept { __a->notify_all(); } +#endif // GTHREADS || LINUX_FUTEX #endif // C++2a // Function templates for atomic_integral and atomic_pointer operations only. diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore index b4facde4ea1f..f4b83ab6ae3f 100644 --- a/libstdc++-v3/include/std/semaphore +++ b/libstdc++-v3/include/std/semaphore @@ -32,14 +32,16 @@ #pragma GCC system_header #if __cplusplus > 201703L -#define __cpp_lib_semaphore 201907L #include <bits/semaphore_base.h> +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX #include <ext/numeric_traits.h> namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#define __cpp_lib_semaphore 201907L + template<ptrdiff_t __least_max_value = __gnu_cxx::__int_traits<ptrdiff_t>::__max> class counting_semaphore @@ -88,5 +90,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using binary_semaphore = std::counting_semaphore<1>; _GLIBCXX_END_NAMESPACE_VERSION } // namespace -#endif // __cplusplus > 201703L +#endif // GTHREADS || LINUX_FUTEX +#endif // C++20 #endif // _GLIBCXX_SEMAPHORE diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 42ed7cb74d38..cf0454dbaf84 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -226,7 +226,9 @@ #if __cpp_lib_concepts # define __cpp_lib_ranges 201911L #endif -#define __cpp_lib_semaphore 201907L +#if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX +# define __cpp_lib_semaphore 201907L +#endif #define __cpp_lib_shift 201806L #if __cpp_lib_concepts # define __cpp_lib_span 202002L ^ permalink raw reply [flat|nested] 50+ messages in thread
end of thread, other threads:[~2020-11-26 16:26 UTC | newest] Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-05-10 0:01 [PATCH] Add C++2a synchronization support Thomas Rodgers 2020-05-11 14:05 ` Jonathan Wakely 2020-05-11 15:43 ` Thomas Rodgers 2020-05-11 17:05 ` Jonathan Wakely 2020-05-11 20:59 ` Thomas Rodgers 2020-05-23 22:52 ` Thomas Rodgers 2020-05-24 17:41 ` Thomas Rodgers 2020-06-06 0:29 ` Thomas Rodgers 2020-07-08 16:43 ` Jonathan Wakely 2020-08-03 14:09 ` Jonathan Wakely 2020-08-03 20:19 ` Jonathan Wakely 2020-09-03 0:47 ` Thomas Rodgers 2020-09-03 0:54 ` Thomas Rodgers 2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers 2020-09-28 13:25 ` Jonathan Wakely 2020-10-01 23:37 ` Thomas Rodgers 2020-10-02 15:40 ` Thomas Rodgers 2020-10-05 22:54 ` [PATCH] t/trodgers/c2a_synchronization Thomas Rodgers 2020-10-23 10:28 ` Jonathan Wakely 2020-10-26 21:48 ` [PATCH] libstdc++: Add C++2a synchronization support Thomas Rodgers 2020-10-27 10:23 ` Jonathan Wakely 2020-11-20 22:44 ` Thomas Rodgers 2020-11-22 21:13 ` Stephan Bergmann 2020-11-23 18:33 ` Jonathan Wakely 2020-11-22 21:41 ` Stephan Bergmann 2020-11-23 18:32 ` Jonathan Wakely 2020-11-21 15:16 ` Andreas Schwab 2020-11-21 17:04 ` Jonathan Wakely 2020-11-21 17:39 ` Jonathan Wakely 2020-11-22 0:36 ` H.J. Lu 2020-11-23 14:50 ` Jonathan Wakely 2020-11-24 23:45 ` Jonathan Wakely 2020-11-25 1:07 ` Jonathan Wakely 2020-11-25 10:35 ` Jonathan Wakely 2020-11-25 12:32 ` Jonathan Wakely 2020-11-25 18:39 ` Jonathan Wakely 2020-11-26 16:26 ` Jonathan Wakely 2020-11-23 16:08 ` Jonathan Wakely 2020-09-28 13:30 ` Jonathan Wakely 2020-09-28 13:36 ` Jonathan Wakely 2020-09-28 21:29 ` Thomas Rodgers 2020-09-29 9:44 ` Jonathan Wakely 2020-11-21 23:54 David Edelsohn 2020-11-22 1:27 ` Jonathan Wakely 2020-11-22 12:29 ` Iain Sandoe 2020-11-22 13:37 ` Jonathan Wakely 2020-11-23 18:31 ` Jonathan Wakely 2020-11-23 18:31 ` Jonathan Wakely 2020-11-24 1:48 ` David Edelsohn 2020-11-23 18:30 ` Jonathan Wakely
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).