From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) by sourceware.org (Postfix) with ESMTP id EF564386F465 for ; Mon, 11 May 2020 20:59:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org EF564386F465 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-399-y_2wW5yrP6C8pOAA9zsrRQ-1; Mon, 11 May 2020 16:59:10 -0400 X-MC-Unique: y_2wW5yrP6C8pOAA9zsrRQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6BFA62273; Mon, 11 May 2020 20:59:09 +0000 (UTC) Received: from trodgers.remote.redhat.com (ovpn-115-101.phx2.redhat.com [10.3.115.101]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D18CC78B20; Mon, 11 May 2020 20:59:06 +0000 (UTC) References: <20200511140504.GL2678@redhat.com> User-agent: mu4e 1.3.3; emacs 26.3 From: Thomas Rodgers To: Jonathan Wakely Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: Re: [PATCH] Add C++2a synchronization support In-reply-to: <20200511140504.GL2678@redhat.com> Date: Mon, 11 May 2020 13:59:05 -0700 Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Status: No, score=-20.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 11 May 2020 20:59:32 -0000 --=-=-= Content-Type: text/plain I *think* I have addressed everything in the attached patch. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Add-C-2a-synchronization-support.patch Content-Description: updated patch commit 24a989d2bf2158bdbe2511310d0583d0c6226f71 Author: Thomas Rodgers 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::wait): Define. (atomic::wait_one): Likewise. (atomic::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 #include +#if __cplusplus > 201703L +#include +#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 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 + _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 + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, false); } + + // TODO add const volatile overload + + template + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { std::__atomic_notify(__ptr, true); } + + // TODO add const volatile overload +#endif // C++2a + template _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 +// . + +/** @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 +#include +#include + +#include + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include +#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 + __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(__atime); + auto __ns = chrono::duration_cast(__atime - __s); + + struct timespec __rt = + { + static_cast(__s.time_since_epoch().count()), + static_cast(__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 + __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 + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock& __lock, + const chrono::time_point& __atime) + { + auto __s = chrono::time_point_cast(__atime); + auto __ns = chrono::duration_cast(__atime - __s); + + __gthread_time_t __ts = + { + static_cast(__s.time_since_epoch().count()), + static_cast(__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 + __atomic_wait_status + __cond_wait_until_impl(__gthread_cond_t* __cv, + unique_lock& __lock, + const chrono::time_point& __atime) + { + auto __s = chrono::time_point_cast(__atime); + auto __ns = chrono::duration_cast(__atime - __s); + + __gthread_time_t __ts = + { + static_cast(__s.time_since_epoch().count()), + static_cast(__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 + __atomic_wait_status + __cond_wait_until(__gthread_cond_t* __cv, + unique_lock& __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 + __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 + 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 + 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 +// . + +/** @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 +#include +#include +#include +#include +#include + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include +#include +#include +#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 + inline constexpr bool __platform_wait_uses_type +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + = is_same_v, __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; + 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 + __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 + 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 + 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 + 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 +// . + +/** @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 +#include +#include + +#if defined _POSIX_SEMAPHORES && __has_include() +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 +#include +#endif + +#include +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE + template + 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 + bool + __try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime) noexcept + { + auto __s = chrono::time_point_cast(__atime); + auto __ns = chrono::duration_cast(__atime - __s); + + struct timespec __ts = + { + static_cast(__s.time_since_epoch().count()), + static_cast(__ns.count()) + }; + + auto __err = sem_timedwait(&_M_semaphore, &__ts); + if (__err && (errno == ETIMEDOUT)) + return false; + else if (__err) + std::terminate(); + return true; + } + + template + 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 + _GLIBCXX_ALWAYS_INLINE bool + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept + { return try_acquire_until(__clock_t::now() + __rtime); } + + template + _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 + 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 + _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 + _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 + using __semaphore_base = __platform_semaphore<__least_max_value>; +#else +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX + template + 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>; + +// __platform_semaphore +# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE + template + using __semaphore_base = conditional_t<( + __least_max_value >= 0 + && __least_max_value <= SEM_VALUE_MAX), + __platform_semaphore<__least_max_value>, + __atomic_semaphore>; +# else + template + using __semaphore_base = __atomic_semaphore; +# 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 + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template + 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 + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template + 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 @@ +// -*- 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 +// . + +/** @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 +#include + +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::__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 @@ +// -*- 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 +// . + +/** @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 +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template::__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 + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return _M_sem.try_acquire_for(__rel_time); } + + 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 #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 +// . + +#include +#include +#include +#include +#include +#include + +#include + +template +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 a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template + || std::is_floating_point_v> +struct check; + +template +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template +struct check +{ + 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(); + check(); + check({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 +// . + +#include +#include +#include +#include +#include +#include + +#include + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic a(false); + std::atomic b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + { + b.store(true); + } + }); + std::unique_lock 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 +// . + +#include "generic.h" + +int +main () +{ + check f; + check d; + check 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 +// . + +#include +#include +#include +#include +#include + +#include + +template +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock 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 +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 +// . + +#include "generic.h" + +int +main () +{ + // check bb; + check ch; + check sch; + check uch; + check s; + check us; + check i; + check ui; + check l; + check ul; + check ll; + check ull; + + check wch; + check ch8; + check ch16; + check ch32; + + check i8; + check i16; + check i32; + check i64; + + check u8; + check u16; + check u32; + check 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 +// . + +#include +#include +#include +#include +#include +#include + +#include + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in " +#elif __cpp_lib_latch!= 201907L +# error "Feature-test macro for latch has wrong value in " +#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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_latch +# error "Feature-test macro for latch missing in " +#elif __cpp_lib_latch != 201907L +# error "Feature-test macro for latch has wrong value in " +#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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// +#include +#include +#include +#include + +int main() +{ + std::atomic 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in " +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in " +#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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in " +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in " +#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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include + +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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include +#include + +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 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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include + +#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::max(); + std::counting_semaphore 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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include +#include + +#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 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 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 +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include +#include + +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 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(); +} --=-=-= Content-Type: text/plain 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 >> #include >> >>+#if __cplusplus > 201703L >>+#include >>+#include > > 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 >>+ _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 >>+// . >>+ >>+/** @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 >>+#include >>+#include >>+ >>+#include >>+ >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+#include >>+#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 >>+ __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(__atime); >>+ auto __ns = chrono::duration_cast(__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(__s.time_since_epoch().count()), >>+ static_cast(__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 >>+ __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 >>+ __atomic_wait_status >>+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock& __lock, >>+ const chrono::time_point& __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(__atime); >>+ auto __ns = chrono::duration_cast(__atime - __s); >>+ >>+ __gthread_time_t __ts = >>+ { >>+ static_cast(__s.time_since_epoch().count()), >>+ static_cast(__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 >>+ __atomic_wait_status >>+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock& __lock, >>+ const chrono::time_point& __atime) >>+ { >>+ auto __s = chrono::time_point_cast(__atime); >>+ auto __ns = chrono::duration_cast(__atime - __s); >>+ >>+ __gthread_time_t __ts = >>+ { >>+ static_cast(__s.time_since_epoch().count()), >>+ static_cast(__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 >>+ __atomic_wait_status >>+ __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock& __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 >>+ __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 _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 _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 >>+// . >>+ >>+/** @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 >>+#include >>+#include >>+#include >>+#include >>+ >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+#include >>+#include >>+#include >>+#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 > > This should be typename not class. > >>+ struct __platform_wait_uses_type >>+ { >>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ enum { __value = std::is_same::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 > struct __platform_wait_uses_type > #ifdef _GLIBCXX_HAVE_LINUX_FUTEX > : is_same, __platform_wait_t> > #else > : false_type > #endif > { }; > > Or better yet, just use a variable template: > > template > inline constexpr bool __platform_wait_uses_type > #ifdef _GLIBCXX_HAVE_LINUX_FUTEX > = is_same_v, __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; >>+ 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 > , 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 >>+ __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 > > 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 > > 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 > > 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 >>+// . >>+ >>+/** @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 >>+#include >>+#include >>+ >>+#if defined _POSIX_SEMAPHORES && __has_include() >>+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1 >>+#include >>+#endif >>+ >>+#include >>+#include >>+#include > > is much smaller than and should be > used for limits of integer types. (I recently added > 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 > > __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 >>+ _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(__atime); >>+ auto __ns = chrono::duration_cast(__atime - __s); >>+ >>+ struct timespec __ts = >>+ { >>+ static_cast(__s.time_since_epoch().count()), >>+ static_cast(__ns.count()) >>+ }; >>+ >>+ auto __err = sem_timedwait(&_M_semaphore, &__ts); >>+ if (__err && (errno == ETIMEDOUT)) >>+ return false; >>+ else if (__err) >>+ std::terminate(); >>+ return true; >>+ } >>+ >>+ template >>+ _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 >>+ _GLIBCXX_ALWAYS_INLINE bool >>+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept >>+ { return try_acquire_until(__clock_t::now() + __rtime); } >>+ >>+ template >>+ _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 >>+ 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 >>+ _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 >>+ _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 > > Rename __least_max_t here too. > >>+ using __semaphore_base = __platform_semaphore<__least_max_t>; >>+#else >>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX >>+ template >>+ 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>::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 >>+ using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX), >>+ __platform_semaphore<__least_max_t>, >>+ __atomic_semaphore>::type; >>+# else >>+ template >>+ using __semaphore_base = __atomic_semaphore; >>+# 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 >>+ inline void atomic_wait(const atomic<_Tp>* __a, >>+ typename std::atomic<_Tp>::value_type __old) noexcept >>+ { __a->wait(__old); } >>+ >>+ template >>+ 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 >>+ inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >>+ { __a->notify_one(); } >>+ >>+ template >>+ 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 @@ >>+// -*- C++ -*- > > A space before . > >>+ >>+// Copyright (C) 2020 Free Software Foundation, Inc. >>+// >>+// This file is part of the GNU ISO C++ Library. This library is free >>+// software; you can redistribute it and/or modify it under the >>+// terms of the GNU General Public License as published by the >>+// Free Software Foundation; either version 3, or (at your option) >>+// any later version. >>+ >>+// This library is distributed in the hope that it will be useful, >>+// but WITHOUT ANY WARRANTY; without even the implied warranty of >>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>+// GNU General Public License for more details. >>+ >>+// Under Section 7 of GPL version 3, you are granted additional >>+// permissions described in the GCC Runtime Library Exception, version >>+// 3.1, as published by the Free Software Foundation. >>+ >>+// You should have received a copy of the GNU General Public License and >>+// a copy of the GCC Runtime Library Exception along with this program; >>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see >>+// . >>+ >>+/** @file 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 >>+#include > > Use 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::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 @@ >>+// -*- C++ -*- > > A space before . > >>+ >>+// Copyright (C) 2020 Free Software Foundation, Inc. >>+// >>+// This file is part of the GNU ISO C++ Library. This library is free >>+// software; you can redistribute it and/or modify it under the >>+// terms of the GNU General Public License as published by the >>+// Free Software Foundation; either version 3, or (at your option) >>+// any later version. >>+ >>+// This library is distributed in the hope that it will be useful, >>+// but WITHOUT ANY WARRANTY; without even the implied warranty of >>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>+// GNU General Public License for more details. >>+ >>+// Under Section 7 of GPL version 3, you are granted additional >>+// permissions described in the GCC Runtime Library Exception, version >>+// 3.1, as published by the Free Software Foundation. >>+ >>+// You should have received a copy of the GNU General Public License and >>+// a copy of the GCC Runtime Library Exception along with this program; >>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see >>+// . >>+ >>+/** @file 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 >>+ >>+namespace std _GLIBCXX_VISIBILITY(default) >>+{ >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION >>+ >>+ template::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 > > s/class/template/ > >>+ bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) >>+ { return _M_sem.try_acquire_for(__rel_time); } >>+ >>+ template > > 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... --=-=-=--