[PATCH] Add C++2a synchronization support
Thomas Rodgers
rodgert@appliantology.com
Sun May 24 17:41:36 GMT 2020
The previous patch had a Makefile.in included, should be a property filtered patch attached now.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Add-C-2a-synchronization-support_f.patch
Type: application/octet-stream
Size: 82762 bytes
Desc: not available
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20200524/d1d212dc/attachment-0001.obj>
-------------- next part --------------
> On May 23, 2020, at 3:52 PM, Thomas Rodgers <rodgert@appliantology.com> wrote:
>
> Updated patch.
>
> <0001-Add-C-2a-synchronization-support.patch>
>
>> On May 11, 2020, at 1:59 PM, Thomas Rodgers via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
>>
>> I *think* I have addressed everything in the attached patch.
>> <0001-Add-C-2a-synchronization-support.patch>
>> Jonathan Wakely writes:
>>
>>> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote:
>>>> * Note, this patch supersedes my previous atomic wait and semaphore
>>>> patches.
>>>>
>>>> Add support for -
>>>> atomic wait/notify_one/notify_all
>>>> counting_semaphore
>>>> binary_semaphore
>>>> latch
>>>>
>>>> * include/Makefile.am (bits_headers): Add new header.
>>>> * include/Makefile.in: Regenerate.
>>>> * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define.
>>>
>>> Should be two colons before wait.
>>>
>>>> diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>>>> index eb437ad8d8d..e73ff8b3e64 100644
>>>> --- a/libstdc++-v3/include/Makefile.in
>>>> +++ b/libstdc++-v3/include/Makefile.in
>>>
>>> Generated files don't need to be in the patch.
>>>
>>>> diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>>>> index 87fe0bd6000..b2cec0f1722 100644
>>>> --- a/libstdc++-v3/include/bits/atomic_base.h
>>>> +++ b/libstdc++-v3/include/bits/atomic_base.h
>>>> @@ -37,6 +37,11 @@
>>>> #include <bits/atomic_lockfree_defines.h>
>>>> #include <bits/move.h>
>>>>
>>>> +#if __cplusplus > 201703L
>>>> +#include <bits/atomic_wait.h>
>>>> +#include <iostream>
>>>
>>> <iostream> shouldn't be here (it adds runtime cost, as well as
>>> compile-time).
>>>
>>>> @@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> __cmpexch_failure_order(__m));
>>>> }
>>>>
>>>> +#if __cplusplus > 201703L
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept
>>>
>>> Please format everything to <= 80 columns (ideally < 80).
>>>
>>>> + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept
>>>> + {
>>>> + __atomic_wait(&_M_p, __old,
>>>
>>> This should be qualified to prevent ADL.
>>>
>>>> + [__m, this, __old]()
>>>> + { return this->load(__m) != __old; });
>>>> + }
>>>> +
>>>> + // TODO add const volatile overload
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + notify_one() const noexcept
>>>> + { __atomic_notify(&_M_p, false); }
>>>
>>> Qualify to prevent ADL here too, and all similar calls.
>>>
>>>> +#if __cplusplus > 201703L
>>>> + template<typename _Tp>
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept
>>>> + {
>>>> + __atomic_wait(__ptr, *std::__addressof(__old),
>>>
>>> Can't this just be __old instead of *std::__addressof(__old) ?
>>>
>>>> + [=]()
>>>> + { return load(__ptr, __m) == *std::__addressof(__old); });
>>>
>>> Same here?
>>>
>>>> diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h
>>>> new file mode 100644
>>>> index 00000000000..10f0fe50ed9
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
>>>> @@ -0,0 +1,270 @@
>>>> +// -*- C++ -*- header.
>>>> +
>>>> +// Copyright (C) 2020 Free Software Foundation, Inc.
>>>> +//
>>>> +// This file is part of the GNU ISO C++ Library. This library is free
>>>> +// software; you can redistribute it and/or modify it under the
>>>> +// terms of the GNU General Public License as published by the
>>>> +// Free Software Foundation; either version 3, or (at your option)
>>>> +// any later version.
>>>> +
>>>> +// This library is distributed in the hope that it will be useful,
>>>> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +// GNU General Public License for more details.
>>>> +
>>>> +// Under Section 7 of GPL version 3, you are granted additional
>>>> +// permissions described in the GCC Runtime Library Exception, version
>>>> +// 3.1, as published by the Free Software Foundation.
>>>> +
>>>> +// You should have received a copy of the GNU General Public License and
>>>> +// a copy of the GCC Runtime Library Exception along with this program;
>>>> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>>>> +// <http://www.gnu.org/licenses/>.
>>>> +
>>>> +/** @file bits/atomic_timed_wait.h
>>>> + * This is an internal header file, included by other library headers.
>>>> + * Do not attempt to use it directly. @headername{atomic}
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_ATOMIC_TIMED_WAIT_H
>>>> +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#include <bits/c++config.h>
>>>> +#include <bits/functional_hash.h>
>>>> +#include <bits/atomic_wait.h>
>>>> +
>>>> +#include <chrono>
>>>> +
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> +#include <sys/time.h>
>>>> +#endif
>>>> +
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> + _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> + enum class __atomic_wait_status { __no_timeout, __timeout };
>>>
>>> Blank line before and after this enum definition please.
>>>
>>>> + namespace __detail
>>>> + {
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> + enum
>>>> + {
>>>> + __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag,
>>>> + __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag,
>>>> + __futex_bitset_match_any = 0xffffffff
>>>> + };
>>>> +
>>>> + using __platform_wait_clock_t = chrono::steady_clock;
>>>
>>> Blank line after this using-decl please.
>>>
>>>> + template<typename _Duration>
>>>> + __atomic_wait_status
>>>> + __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val,
>>>> + const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept
>>>> + {
>>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>
>>> Eventually we'll want to move the rest of this function (which doesn't
>>> depend on the template argument) into the compiled library, but it's
>>> better to be header-only for now.
>>>
>>>> + struct timespec __rt =
>>>> + {
>>>> + static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> + static_cast<long>(__ns.count())
>>>> + };
>>>> +
>>>> + auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt,
>>>> + nullptr, __futex_bitset_match_any);
>>>> + if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT))
>>>> + std::terminate();
>>>> + return (__platform_wait_clock_t::now() < __atime)
>>>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>>>> + }
>>>> +
>>>> + template<typename _Clock, typename _Duration>
>>>> + __atomic_wait_status
>>>> + __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val,
>>>> + const chrono::time_point<_Clock, _Duration>& __atime)
>>>> + {
>>>> + if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value)
>>>
>>> This is C++20 so you can use is_same_v here, which uses the intrinsic
>>> directly and avoids instantiating the is_same class template.
>>>
>>>> + {
>>>> + return __platform_wait_until_impl(__addr, __val, __atime);
>>>> + }
>>>> + else
>>>> + {
>>>> + const typename _Clock::time_point __c_entry = _Clock::now();
>>>> + const __platform_wait_clock_t::time_point __s_entry =
>>>> + __platform_wait_clock_t::now();
>>>> + const auto __delta = __atime - __c_entry;
>>>> + const auto __s_atime = __s_entry + __delta;
>>>> + if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout)
>>>> + return __atomic_wait_status::__no_timeout;
>>>> +
>>>> + // We got a timeout when measured against __clock_t but
>>>> + // we need to check against the caller-supplied clock
>>>> + // to tell whether we should return a timeout.
>>>> + if (_Clock::now() < __atime)
>>>> + return __atomic_wait_status::__no_timeout;
>>>> + return __atomic_wait_status::__timeout;
>>>> + }
>>>> + }
>>>> +#endif
>>>> +
>>>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
>>>> + template<typename _Duration>
>>>> + __atomic_wait_status
>>>> + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>>>> + const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime)
>>>
>>> The std:: qualification here isn't needed (and doesn't help with
>>> keeping the line below 80 cols).
>>>
>>>> + {
>>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>> + __gthread_time_t __ts =
>>>> + {
>>>> + static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> + static_cast<long>(__ns.count())
>>>> + };
>>>> +
>>>> + pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(),
>>>> + CLOCK_MONOTONIC,
>>>> + &__ts);
>>>> + return (chrono::steady_clock::now() < __atime)
>>>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>>>> + }
>>>> +#endif
>>>> +
>>>> + template<typename _Duration>
>>>> + __atomic_wait_status
>>>> + __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>>>> + const chrono::time_point<chrono::system_clock, _Duration>& __atime)
>>>> + {
>>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>> + __gthread_time_t __ts =
>>>> + {
>>>> + static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> + static_cast<long>(__ns.count())
>>>> + };
>>>> +
>>>> + __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(),
>>>> + &__ts);
>>>> + return (chrono::system_clock::now() < __atime)
>>>> + ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>>>> + }
>>>> +
>>>> + // return true if timeout
>>>> + template<typename _Clock, typename _Duration>
>>>> + __atomic_wait_status
>>>> + __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>>>> + const chrono::time_point<_Clock, _Duration>& __atime)
>>>> + {
>>>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
>>>> + using __clock_t = chrono::steady_clock;
>>>> +#else
>>>> + using __clock_t = chrono::system_clock;
>>>> +#endif
>>>> + const typename _Clock::time_point __c_entry = _Clock::now();
>>>> + const __clock_t::time_point __s_entry = __clock_t::now();
>>>> + const auto __delta = __atime - __c_entry;
>>>> + const auto __s_atime = __s_entry + __delta;
>>>> + if (__cond_wait_until_impl(__cv, __lock, __s_atime))
>>>> + return __atomic_wait_status::__no_timeout;
>>>> + // We got a timeout when measured against __clock_t but
>>>> + // we need to check against the caller-supplied clock
>>>> + // to tell whether we should return a timeout.
>>>> + if (_Clock::now() < __atime)
>>>> + return __atomic_wait_status::__no_timeout;
>>>> + return __atomic_wait_status::__timeout;
>>>> + }
>>>> +
>>>> + struct __timed_waiters : __waiters
>>>> + {
>>>> + template<typename _Clock, typename _Duration>
>>>> + __atomic_wait_status
>>>> + _M_do_wait_until(int32_t __version,
>>>> + const chrono::time_point<_Clock, _Duration>& __atime)
>>>> + {
>>>> + int32_t __cur = 0;
>>>> + __waiters::__lock_t __l(_M_mtx);
>>>> + while (__cur <= __version)
>>>> + {
>>>> + if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout)
>>>> + return __atomic_wait_status::__timeout;
>>>> +
>>>> + int32_t __last = __cur;
>>>> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
>>>> + if (__cur < __last)
>>>> + break; // break the loop if version overflows
>>>> + }
>>>> + return __atomic_wait_status::__no_timeout;
>>>> + }
>>>> +
>>>> + static __timed_waiters&
>>>> + _S_timed_for(void* __t)
>>>> + {
>>>> + static_assert(sizeof(__timed_waiters) == sizeof(__waiters));
>>>> + return (__timed_waiters&) __waiters::_S_for(__t);
>>>> + }
>>>> + };
>>>> + } // namespace __detail
>>>> +
>>>> + template<typename _Tp, typename _Pred,
>>>> + typename _Clock, typename _Duration>
>>>> + bool
>>>> + __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred,
>>>> + const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>>>> + {
>>>> + using namespace __detail;
>>>> +
>>>> + if (__atomic_spin(__pred))
>>>
>>> Qualify to prevent ADL.
>>>
>>>> + return true;
>>>> +
>>>> + auto& __w = __timed_waiters::_S_timed_for((void*)__addr);
>>>> + auto __version = __w._M_enter_wait();
>>>> + do
>>>> + {
>>>> + __atomic_wait_status __res;
>>>> + if constexpr (__platform_wait_uses_type<_Tp>::__value)
>>>> + {
>>>> + __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old,
>>>> + __atime);
>>>> + }
>>>> + else
>>>> + {
>>>> + __res = __w._M_do_wait_until(__version, __atime);
>>>> + }
>>>> + if (__res == __atomic_wait_status::__timeout)
>>>> + return false;
>>>> + }
>>>> + while (!__pred() && __atime < _Clock::now());
>>>> + __w._M_leave_wait();
>>>> +
>>>> + // if timed out, return false
>>>> + return (_Clock::now() < __atime);
>>>> + }
>>>> +
>>>> + template<typename _Tp, typename _Pred,
>>>> + typename _Rep, typename _Period>
>>>> + bool
>>>> + __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred,
>>>> + const chrono::duration<_Rep, _Period>& __rtime) noexcept
>>>> + {
>>>> + using namespace __detail;
>>>> +
>>>> + if (__atomic_spin(__pred))
>>>> + return true;
>>>> +
>>>> + if (!__rtime.count())
>>>> + return false; // no rtime supplied, and spin did not acquire
>>>> +
>>>> + using __dur = chrono::steady_clock::duration;
>>>> + auto __reltime = chrono::duration_cast<__dur>(__rtime);
>>>> + if (__reltime < __rtime)
>>>> + ++__reltime;
>>>> +
>>>> +
>>>> + return __atomic_wait_until(__addr, __old, std::move(__pred),
>>>> + chrono::steady_clock::now() + __reltime);
>>>> + }
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace std
>>>> +#endif
>>>> diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h
>>>> new file mode 100644
>>>> index 00000000000..32070a54f40
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/bits/atomic_wait.h
>>>> @@ -0,0 +1,280 @@
>>>> +// -*- C++ -*- header.
>>>> +
>>>> +// Copyright (C) 2020 Free Software Foundation, Inc.
>>>> +//
>>>> +// This file is part of the GNU ISO C++ Library. This library is free
>>>> +// software; you can redistribute it and/or modify it under the
>>>> +// terms of the GNU General Public License as published by the
>>>> +// Free Software Foundation; either version 3, or (at your option)
>>>> +// any later version.
>>>> +
>>>> +// This library is distributed in the hope that it will be useful,
>>>> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +// GNU General Public License for more details.
>>>> +
>>>> +// Under Section 7 of GPL version 3, you are granted additional
>>>> +// permissions described in the GCC Runtime Library Exception, version
>>>> +// 3.1, as published by the Free Software Foundation.
>>>> +
>>>> +// You should have received a copy of the GNU General Public License and
>>>> +// a copy of the GCC Runtime Library Exception along with this program;
>>>> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>>>> +// <http://www.gnu.org/licenses/>.
>>>> +
>>>> +/** @file bits/atomic_wait.h
>>>> + * This is an internal header file, included by other library headers.
>>>> + * Do not attempt to use it directly. @headername{atomic}
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_ATOMIC_WAIT_H
>>>> +#define _GLIBCXX_ATOMIC_WAIT_H 1
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#include <bits/c++config.h>
>>>> +#include <bits/functional_hash.h>
>>>> +#include <bits/gthr.h>
>>>> +#include <bits/std_mutex.h>
>>>> +#include <bits/unique_lock.h>
>>>> +
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> +#include <climits>
>>>> +#include <unistd.h>
>>>> +#include <syscall.h>
>>>> +#endif
>>>> +
>>>> +#define _GLIBCXX_SPIN_COUNT_1 16
>>>> +#define _GLIBCXX_SPIN_COUNT_2 12
>>>> +
>>>> +// TODO get this from Autoconf
>>>> +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1
>>>> +
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> + namespace __detail
>>>> + {
>>>> + using __platform_wait_t = int;
>>>> +
>>>> + template<class _Tp>
>>>
>>> This should be typename not class.
>>>
>>>> + struct __platform_wait_uses_type
>>>> + {
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> + enum { __value = std::is_same<typename std::remove_cv<_Tp>::type,
>>>
>>> This should be remove_cv_t.
>>>
>>>> + __platform_wait_t>::value };
>>>> +#else
>>>> + enum { __value = std::false_type::value };
>>>> +#endif
>>>
>>> There's no need to use the C++03 enum hack here, it should just derive
>>> from true_type or false_type.
>>>
>>> template<typename _Tp>
>>> struct __platform_wait_uses_type
>>> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>> : is_same<std::remove_cv_t<_Tp>, __platform_wait_t>
>>> #else
>>> : false_type
>>> #endif
>>> { };
>>>
>>> Or better yet, just use a variable template:
>>>
>>> template<typename _Tp>
>>> inline constexpr bool __platform_wait_uses_type
>>> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>> = is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>;
>>> #else
>>> = false;
>>> #endif
>>>
>>>
>>>
>>>> + };
>>>> +
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> + enum
>>>> + {
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
>>>> + __futex_private_flag = 128,
>>>> +#else
>>>> + __futex_private_flag = 0,
>>>> +#endif
>>>> + __futex_wait = 0,
>>>> + __futex_wake = 1,
>>>> + __futex_wait_bitset = 9,
>>>> + __futex_wake_bitset = 10,
>>>> + __futex_wait_private = __futex_wait | __futex_private_flag,
>>>> + __futex_wake_private = __futex_wake | __futex_private_flag
>>>> + };
>>>> +
>>>> + void
>>>> + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept
>>>> + {
>>>> + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr);
>>>> + if (__e && !(errno == EINTR || errno == EAGAIN))
>>>> + std::terminate();
>>>> + }
>>>> +
>>>> + void
>>>> + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept
>>>> + {
>>>> + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1);
>>>> + }
>>>> +#endif
>>>> +
>>>> + struct alignas(64) __waiters
>>>
>>> Isn't alignas(64) already implied by the first data member?
>>>
>>>> + {
>>>> + int32_t alignas(64) _M_ver = 0;
>>>> + int32_t alignas(64) _M_wait = 0;
>>>> +
>>>> + // TODO make this used only where we don't have futexes
>>>
>>> Don't we always need these even with futexes, for the types that don't
>>> use a futex?
>>>
>>>> + using __lock_t = std::unique_lock<std::mutex>;
>>>> + mutable __lock_t::mutex_type _M_mtx;
>>>> +
>>>> +#ifdef __GTHREAD_COND_INIT
>>>> + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT;
>>>> + __waiters() noexcept = default;
>>>
>>> If we moved std::condition_variable into its own header (or
>>> <bits/std_mutex.h>, could we reuse that here instead of using
>>> __gthread_cond_t directly?
>>>
>>>> +#else
>>>> + mutable __gthread_cond_t _M_cv;
>>>> + __waiters() noexcept
>>>> + {
>>>> + __GTHREAD_COND_INIT_FUNCTION(&_M_cond);
>>>> + }
>>>> +#endif
>>>> +
>>>> + int32_t
>>>> + _M_enter_wait() noexcept
>>>> + {
>>>> + int32_t __res;
>>>> + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE);
>>>> + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL);
>>>> + return __res;
>>>> + }
>>>> +
>>>> + void
>>>> + _M_leave_wait() noexcept
>>>> + {
>>>> + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL);
>>>> + }
>>>> +
>>>> + void
>>>> + _M_do_wait(int32_t __version) const noexcept
>>>> + {
>>>> + int32_t __cur = 0;
>>>> + while (__cur <= __version)
>>>> + {
>>>> + __waiters::__lock_t __l(_M_mtx);
>>>> + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle());
>>>> + if (__e)
>>>> + std::terminate();
>>>> + int32_t __last = __cur;
>>>> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
>>>> + if (__cur < __last)
>>>> + break; // break the loop if version overflows
>>>> + }
>>>> + }
>>>> +
>>>> + int32_t
>>>> + _M_waiting() const noexcept
>>>> + {
>>>> + int32_t __res;
>>>> + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE);
>>>> + return __res;
>>>> + }
>>>> +
>>>> + void
>>>> + _M_notify(bool __all) noexcept
>>>> + {
>>>> + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL);
>>>> + auto __e = __gthread_cond_broadcast(&_M_cv);
>>>> + if (__e)
>>>> + __throw_system_error(__e);
>>>> + }
>>>> +
>>>> + static __waiters&
>>>> + _S_for(void* __t)
>>>> + {
>>>> + const unsigned char __mask = 0xf;
>>>> + static __waiters __w[__mask + 1];
>>>> +
>>>> + auto __key = _Hash_impl::hash(__t) & __mask;
>>>> + return __w[__key];
>>>> + }
>>>> + };
>>>> +
>>>> + struct __waiter
>>>> + {
>>>> + __waiters& _M_w;
>>>> + int32_t _M_version;
>>>> +
>>>> + template<typename _Tp>
>>>> + __waiter(const _Tp* __addr) noexcept
>>>> + : _M_w(__waiters::_S_for((void*) __addr))
>>>> + , _M_version(_M_w._M_enter_wait())
>>>> + { }
>>>> +
>>>> + ~__waiter()
>>>> + { _M_w._M_leave_wait(); }
>>>> +
>>>> + void _M_do_wait() noexcept
>>>> + { _M_w._M_do_wait(_M_version); }
>>>> + };
>>>> +
>>>> + void
>>>> + __thread_relax() noexcept
>>>> + {
>>>> +#if defined __i386__ || defined __x86_64__
>>>> + __builtin_ia32_pause();
>>>> +#elif defined _GLIBCXX_USE_SCHED_YIELD
>>>> + __gthread_yield();
>>>> +#endif
>>>> + }
>>>> +
>>>> + void
>>>> + __thread_yield() noexcept
>>>> + {
>>>> +#if defined _GLIBCXX_USE_SCHED_YIELD
>>>> + __gthread_yield();
>>>> +#endif
>>>> + }
>>>> +
>>>> + } // namespace __detail
>>>> +
>>>> + template<class _Pred>
>>>
>>> s/class/template/
>>>
>>>> + bool
>>>> + __atomic_spin(_Pred __pred) noexcept
>>>> + {
>>>> + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i)
>>>> + {
>>>> + if (__pred())
>>>> + return true;
>>>> +
>>>> + if (__i < _GLIBCXX_SPIN_COUNT_2)
>>>> + __detail::__thread_relax();
>>>> + else
>>>> + __detail::__thread_yield();
>>>> + }
>>>> + return false;
>>>> + }
>>>> +
>>>> + template<class _Tp, class _Pred>
>>>
>>> s/class/template/
>>>
>>>> + void
>>>> + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept
>>>> + {
>>>> + using namespace __detail;
>>>> + if (__atomic_spin(__pred))
>>>> + return;
>>>> +
>>>> + __waiter __w(__addr);
>>>> + while (!__pred())
>>>> + {
>>>> + if constexpr (__platform_wait_uses_type<_Tp>::__value)
>>>> + {
>>>> + __platform_wait((__platform_wait_t*)(void*) __addr, __old);
>>>> + }
>>>> + else
>>>> + {
>>>> + // TODO support timed backoff when this can be moved into the lib
>>>> + __w._M_do_wait();
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + template<class _Tp>
>>>
>>> s/class/template/
>>>
>>>> + void
>>>> + __atomic_notify(const _Tp* __addr, bool __all) noexcept
>>>> + {
>>>> + using namespace __detail;
>>>> + auto& __w = __waiters::_S_for((void*)__addr);
>>>> + if (!__w._M_waiting())
>>>
>>> When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting()
>>> ever be true? Won't this always return before notifying?
>>>
>>> Is there meant to be a __waiter constructed here?
>>>
>>>> + return;
>>>> +
>>>> + if constexpr (__platform_wait_uses_type<_Tp>::__value)
>>>> + {
>>>> + __platform_notify((__platform_wait_t*)(void*) __addr, __all);
>>>> + }
>>>> + else
>>>> + {
>>>> + __w._M_notify(__all);
>>>> + }
>>>> + }
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace std
>>>> +#endif
>>>> diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h
>>>> new file mode 100644
>>>> index 00000000000..b3c83bbc70b
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/bits/semaphore_base.h
>>>> @@ -0,0 +1,270 @@
>>>> +// -*- C++ -*- header.
>>>> +
>>>> +// Copyright (C) 2020 Free Software Foundation, Inc.
>>>> +//
>>>> +// This file is part of the GNU ISO C++ Library. This library is free
>>>> +// software; you can redistribute it and/or modify it under the
>>>> +// terms of the GNU General Public License as published by the
>>>> +// Free Software Foundation; either version 3, or (at your option)
>>>> +// any later version.
>>>> +
>>>> +// This library is distributed in the hope that it will be useful,
>>>> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +// GNU General Public License for more details.
>>>> +
>>>> +// Under Section 7 of GPL version 3, you are granted additional
>>>> +// permissions described in the GCC Runtime Library Exception, version
>>>> +// 3.1, as published by the Free Software Foundation.
>>>> +
>>>> +// You should have received a copy of the GNU General Public License and
>>>> +// a copy of the GCC Runtime Library Exception along with this program;
>>>> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>>>> +// <http://www.gnu.org/licenses/>.
>>>> +
>>>> +/** @file bits/semaphore.h
>>>
>>> Should be bits/semaphore_base.h
>>>
>>>> + * This is an internal header file, included by other library headers.
>>>> + * Do not attempt to use it directly. @headername{atomic}
>>>
>>> Should be @headername{semaphore}
>>>
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_SEMAPHORE_BASE_H
>>>> +#define _GLIBCXX_SEMAPHORE_BASE_H 1
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#include <bits/c++config.h>
>>>> +#include <bits/atomic_base.h>
>>>> +#include <bits/atomic_timed_wait.h>
>>>> +
>>>> +#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>)
>>>> +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1
>>>> +#include <semaphore.h>
>>>> +#endif
>>>> +
>>>> +#include <chrono>
>>>> +#include <type_traits>
>>>> +#include <limits>
>>>
>>> <ext/numeric_traits.h> is much smaller than <limits> and should be
>>> used for limits of integer types. (I recently added
>>> <bits/int_limits.h> too but that was a mistake that I need to fix).
>>>
>>>
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +
>>>> +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>>> + template<ptrdiff_t __least_max_t>
>>>
>>> __least_max_t isn't a type so shouldn't have the _t suffix.
>>>
>>>> + struct __platform_semaphore
>>>> + {
>>>> + using __clock_t = chrono::system_clock;
>>>> +
>>>> + __platform_semaphore(ptrdiff_t __count) noexcept
>>>
>>> Should this constructor be explicit?
>>>
>>>> + {
>>>> + static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX");
>>>
>>> Our static_assert messages should state the positive condition, not
>>> the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX",
>>> which is what the real condition is anyway, so you might as well omit
>>> the string literal.
>>>
>>>> + auto __e = sem_init(&_M_semaphore, 0, __count);
>>>> + if (__e)
>>>> + std::terminate();
>>>> + }
>>>> +
>>>> + ~__platform_semaphore()
>>>> + {
>>>> + auto __e = sem_destroy(&_M_semaphore);
>>>> + if (__e)
>>>> + std::terminate();
>>>> + }
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + acquire() noexcept
>>>> + {
>>>> + auto __err = sem_wait(&_M_semaphore);
>>>> + if (__err)
>>>> + std::terminate();
>>>> + }
>>>> +
>>>> + template<typename _Duration>
>>>> + _GLIBCXX_ALWAYS_INLINE bool
>>>
>>> Do we really need this to be always_inline?
>>>
>>>> + __try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime) noexcept
>>>> + {
>>>> + auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>> + struct timespec __ts =
>>>> + {
>>>> + static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> + static_cast<long>(__ns.count())
>>>> + };
>>>> +
>>>> + auto __err = sem_timedwait(&_M_semaphore, &__ts);
>>>> + if (__err && (errno == ETIMEDOUT))
>>>> + return false;
>>>> + else if (__err)
>>>> + std::terminate();
>>>> + return true;
>>>> + }
>>>> +
>>>> + template<typename _Clock, typename _Duration>
>>>> + _GLIBCXX_ALWAYS_INLINE bool
>>>
>>> always_inline?
>>>
>>>> + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>>>> + {
>>>> + if constexpr (std::is_same<__clock_t, _Clock>::value)
>>>
>>> is_same_v
>>>
>>>> + {
>>>> + return __try_acquire_until_impl(__atime);
>>>> + }
>>>> + else
>>>> + {
>>>> + const typename _Clock::time_point __c_entry = _Clock::now();
>>>> + const __clock_t __s_entry = __clock_t::now();
>>>> + const auto __delta = __atime - __c_entry;
>>>> + const auto __s_atime = __s_entry + __delta;
>>>> + if (__try_acquire_until_impl(__s_atime))
>>>> + return true;
>>>> +
>>>> + // We got a timeout when measured against __clock_t but
>>>> + // we need to check against the caller-supplied clock
>>>> + // to tell whether we should return a timeout.
>>>> + return (_Clock::now() < __atime);
>>>> + }
>>>> + }
>>>> +
>>>> + template<typename _Rep, typename _Period>
>>>> + _GLIBCXX_ALWAYS_INLINE bool
>>>> + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
>>>> + { return try_acquire_until(__clock_t::now() + __rtime); }
>>>> +
>>>> + template<typename _Clock, typename _Duration>
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + release(ptrdiff_t __update) noexcept
>>>> + {
>>>> + do
>>>> + {
>>>> + auto __err = sem_post(&_M_semaphore);
>>>> + if (__err)
>>>> + std::terminate();
>>>> + } while (--__update);
>>>> + }
>>>> +
>>>> + private:
>>>> + sem_t _M_semaphore;
>>>> + };
>>>> +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>>> +
>>>> + template<typename _Tp>
>>>> + struct __atomic_semaphore
>>>> + {
>>>> + static constexpr size_t _S_alignment = __alignof__(_Tp);
>>>> +
>>>> + __atomic_semaphore(_Tp __count)
>>>
>>> Should this be explicit?
>>>
>>>> + : _M_a(__count)
>>>> + { }
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + acquire() noexcept
>>>> + {
>>>> + auto const __pred = [this]
>>>> + {
>>>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>>>> + if (__old == 0)
>>>> + return false;
>>>> + return __atomic_impl::compare_exchange_strong(&this->_M_a,
>>>> + __old, __old - 1,
>>>> + memory_order::acquire,
>>>> + memory_order::release);
>>>> + };
>>>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>>>> + __atomic_wait(&_M_a, __old, __pred);
>>>> + }
>>>> +
>>>> + bool
>>>> + try_acquire() noexcept
>>>> + {
>>>> + auto __old = __atomic_impl::load(&_M_a, memory_order::acquire);
>>>> + if (__old == 0)
>>>> + return false;
>>>> +
>>>> + return __atomic_spin([this, &__old]
>>>> + {
>>>> + return __atomic_impl::compare_exchange_weak(&this->_M_a,
>>>> + __old, __old - 1,
>>>> + memory_order::acquire,
>>>> + memory_order::release);
>>>> + });
>>>> + }
>>>> +
>>>> + template<typename _Clock, typename _Duration>
>>>> + _GLIBCXX_ALWAYS_INLINE bool
>>>> + try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>>>> + {
>>>> + auto const __pred = [this]
>>>> + {
>>>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>>>> + if (__old == 0)
>>>> + return false;
>>>> + return __atomic_impl::compare_exchange_strong(&this->_M_a,
>>>> + __old, __old - 1,
>>>> + memory_order::acquire,
>>>> + memory_order::release);
>>>> + };
>>>> +
>>>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>>>> + return __atomic_wait_until(&_M_a, __old, __pred, __atime);
>>>> + }
>>>> +
>>>> + template<typename _Rep, typename _Period>
>>>> + _GLIBCXX_ALWAYS_INLINE bool
>>>> + try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
>>>> + {
>>>> + auto const __pred = [this]
>>>> + {
>>>> + auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>>>> + if (__old == 0)
>>>> + return false;
>>>> + return __atomic_impl::compare_exchange_strong(&this->_M_a,
>>>> + __old, __old - 1,
>>>> + memory_order::acquire,
>>>> + memory_order::release);
>>>> + };
>>>> +
>>>> + auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>>>> + return __atomic_wait_for(&_M_a, __old, __pred, __rtime);
>>>> + }
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + release(ptrdiff_t __update) noexcept
>>>> + {
>>>> + if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release))
>>>> + return;
>>>> + if (__update > 1)
>>>> + __atomic_impl::notify_all(&_M_a);
>>>> + else
>>>> + __atomic_impl::notify_one(&_M_a);
>>>> + }
>>>> +
>>>> + private:
>>>> + alignas(_S_alignment) _Tp _M_a;
>>>
>>> Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no
>>> need for the _S_alignment constant if it's only used in one place.
>>>
>>>> + };
>>>> +
>>>> +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
>>>> + template<ptrdiff_t __least_max_t>
>>>
>>> Rename __least_max_t here too.
>>>
>>>> + using __semaphore_base = __platform_semaphore<__least_max_t>;
>>>> +#else
>>>> +# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> + template<ptrdiff_t __least_max_t>
>>>> + using __semaphore_base = std::conditional<(__least_max_t > 0
>>>
>>> This should use conditional_t<> not conditional<>::type.
>>>
>>> The least-max_value can't be negative. If it's zero, can't we use a
>>> futex or semaphore? So the '__least_max_t > 0' condition is wrong?
>>>
>>>> + && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()),
>>>
>>> Should that be <= rather than < ?
>>>
>>>> + __atomic_semaphore<__detail::__platform_wait_t>,
>>>> + __atomic_semaphore<ptrdiff_t>>::type;
>>>> + // __platform_semaphore
>>>> +# else
>>>> +# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>>
>>> Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid
>>> an extra level of #if nesting.
>>>
>>>> + template<ptrdiff_t __least_max_t>
>>>> + using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX),
>>>> + __platform_semaphore<__least_max_t>,
>>>> + __atomic_semaphore<ptrdiff_t>>::type;
>>>> +# else
>>>> + template<ptrdiff_t __least_max_t>
>>>> + using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
>>>> +# endif
>>>> +# endif
>>>> +#endif
>>>> +
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace std
>>>> +
>>>> +#endif
>>>> diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
>>>> index a455286a784..3f18774031d 100644
>>>> --- a/libstdc++-v3/include/std/atomic
>>>> +++ b/libstdc++-v3/include/std/atomic
>>>> @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> compare_exchange_strong(bool& __i1, bool __i2,
>>>> memory_order __m = memory_order_seq_cst) volatile noexcept
>>>> { return _M_base.compare_exchange_strong(__i1, __i2, __m); }
>>>> +
>>>> +#if __cplusplus > 201703L
>>>> + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept
>>>> + { _M_base.wait(__old, __m); }
>>>> +
>>>> + // TODO add const volatile overload
>>>> +
>>>> + void notify_one() const noexcept
>>>> + { _M_base.notify_one(); }
>>>> +
>>>> + void notify_all() const noexcept
>>>> + { _M_base.notify_all(); }
>>>> +#endif
>>>> };
>>>>
>>>> #if __cplusplus <= 201703L
>>>> @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> memory_order __m = memory_order_seq_cst) volatile noexcept
>>>> { return compare_exchange_strong(__e, __i, __m,
>>>> __cmpexch_failure_order(__m)); }
>>>> +#if __cplusplus > 201703L
>>>> + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept
>>>> + { _M_i.wait(__old, __m); }
>>>> +
>>>> + // TODO add const volatile overload
>>>> +
>>>> + void notify_one() const noexcept
>>>> + { _M_i.notify_one(); }
>>>> +
>>>> + void notify_all() const noexcept
>>>> + { _M_i.notify_all(); }
>>>> +#endif
>>>> +
>>>> };
>>>> #undef _GLIBCXX20_INIT
>>>>
>>>> @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> __cmpexch_failure_order(__m));
>>>> }
>>>>
>>>> +#if __cplusplus > 201703L
>>>> + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept
>>>> + { _M_b.wait(__old, __m); }
>>>> +
>>>> + // TODO add const volatile overload
>>>> +
>>>> + void notify_one() const noexcept
>>>> + { _M_b.notify_one(); }
>>>> +
>>>> + void notify_all() const noexcept
>>>> + { _M_b.notify_all(); }
>>>> +#endif
>>>> __pointer_type
>>>> fetch_add(ptrdiff_t __d,
>>>> memory_order __m = memory_order_seq_cst) noexcept
>>>> @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> memory_order_seq_cst);
>>>> }
>>>>
>>>> +
>>>> +#if __cplusplus > 201703L
>>>> + template<typename _Tp>
>>>> + inline void atomic_wait(const atomic<_Tp>* __a,
>>>> + typename std::atomic<_Tp>::value_type __old) noexcept
>>>> + { __a->wait(__old); }
>>>> +
>>>> + template<typename _Tp>
>>>> + inline void atomic_wait_explicit(const atomic<_Tp>* __a,
>>>> + typename std::atomic<_Tp>::value_type __old,
>>>> + std::memory_order __m) noexcept
>>>> + { __a->wait(__old, __m); }
>>>> +
>>>> + template<typename _Tp>
>>>> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept
>>>> + { __a->notify_one(); }
>>>> +
>>>> + template<typename _Tp>
>>>> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept
>>>> + { __a->notify_all(); }
>>>> +
>>>> +#endif // C++2a
>>>> +
>>>> // Function templates for atomic_integral and atomic_pointer operations only.
>>>> // Some operations (and, or, xor) are only available for atomic integrals,
>>>> // which is implemented by taking a parameter of type __atomic_base<_ITp>*.
>>>> diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch
>>>> new file mode 100644
>>>> index 00000000000..0099877416e
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/std/latch
>>>> @@ -0,0 +1,91 @@
>>>> +//<latch> -*- C++ -*-
>>>
>>> A space before <latch>.
>>>
>>>> +
>>>> +// Copyright (C) 2020 Free Software Foundation, Inc.
>>>> +//
>>>> +// This file is part of the GNU ISO C++ Library. This library is free
>>>> +// software; you can redistribute it and/or modify it under the
>>>> +// terms of the GNU General Public License as published by the
>>>> +// Free Software Foundation; either version 3, or (at your option)
>>>> +// any later version.
>>>> +
>>>> +// This library is distributed in the hope that it will be useful,
>>>> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +// GNU General Public License for more details.
>>>> +
>>>> +// Under Section 7 of GPL version 3, you are granted additional
>>>> +// permissions described in the GCC Runtime Library Exception, version
>>>> +// 3.1, as published by the Free Software Foundation.
>>>> +
>>>> +// You should have received a copy of the GNU General Public License and
>>>> +// a copy of the GCC Runtime Library Exception along with this program;
>>>> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>>>> +// <http://www.gnu.org/licenses/>.
>>>> +
>>>> +/** @file include/latch
>>>> + * This is a Standard C++ Library header.
>>>
>>> Align "This" with "@file" here.
>>>
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_LATCH
>>>> +#define _GLIBCXX_LATCH
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#if __cplusplus > 201703L
>>>> +#define __cpp_lib_latch 201907L
>>>> +
>>>> +#include <bits/atomic_base.h>
>>>> +#include <limits>
>>>
>>> Use <ext/numeric_traits.h> here too.
>>>
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +
>>>> + class latch
>>>> + {
>>>> + static constexpr size_t _S_alignment = __alignof__(ptrdiff_t);
>>>> + public:
>>>> + static constexpr
>>>> + _GLIBCXX_ALWAYS_INLINE ptrdiff_t
>>>> + max() noexcept
>>>> + { return numeric_limits<ptrdiff_t>::max(); }
>>>> +
>>>> + constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { }
>>>> +
>>>> + ~latch() = default;
>>>> + latch(const latch&) = delete;
>>>> + latch& operator=(const latch&) = delete;
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + count_down(ptrdiff_t __update = 1)
>>>> + {
>>>> + auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release);
>>>> + if (__old == __update)
>>>> + __atomic_impl::notify_all(&_M_a);
>>>> + }
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE bool
>>>> + try_wait() const noexcept
>>>> + { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; }
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + wait() const
>>>> + {
>>>> + auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire);
>>>> + __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); });
>>>> + }
>>>> +
>>>> + _GLIBCXX_ALWAYS_INLINE void
>>>> + arrive_and_wait(ptrdiff_t __update = 1)
>>>> + {
>>>> + count_down();
>>>> + wait();
>>>> + }
>>>> +
>>>> + private:
>>>> + alignas(_S_alignment) ptrdiff_t _M_a;
>>>
>>> Just use __alignof__ directly here and get rid of _S_alignment?
>>>
>>>> + };
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace
>>>> +#endif // __cplusplus > 201703L
>>>> +#endif // _GLIBCXX_LATCH
>>>> diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore
>>>> new file mode 100644
>>>> index 00000000000..b51940b46ac
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/std/semaphore
>>>> @@ -0,0 +1,81 @@
>>>> +//<semaphore> -*- C++ -*-
>>>
>>> A space before <semaphore>.
>>>
>>>> +
>>>> +// Copyright (C) 2020 Free Software Foundation, Inc.
>>>> +//
>>>> +// This file is part of the GNU ISO C++ Library. This library is free
>>>> +// software; you can redistribute it and/or modify it under the
>>>> +// terms of the GNU General Public License as published by the
>>>> +// Free Software Foundation; either version 3, or (at your option)
>>>> +// any later version.
>>>> +
>>>> +// This library is distributed in the hope that it will be useful,
>>>> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +// GNU General Public License for more details.
>>>> +
>>>> +// Under Section 7 of GPL version 3, you are granted additional
>>>> +// permissions described in the GCC Runtime Library Exception, version
>>>> +// 3.1, as published by the Free Software Foundation.
>>>> +
>>>> +// You should have received a copy of the GNU General Public License and
>>>> +// a copy of the GCC Runtime Library Exception along with this program;
>>>> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>>>> +// <http://www.gnu.org/licenses/>.
>>>> +
>>>> +/** @file include/semaphore
>>>> + * This is a Standard C++ Library header.
>>>
>>> Align "This" with "@file" here.
>>>
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_SEMAPHORE
>>>> +#define _GLIBCXX_SEMAPHORE
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#if __cplusplus > 201703L
>>>> +#define __cpp_lib_semaphore 201907L
>>>> +#include <bits/semaphore_base.h>
>>>> +
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +
>>>> + template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()>
>>>> + class counting_semaphore
>>>> + {
>>>
>>> I don't see a static_assert making it ill-formed to use a negative
>>> value for __least_max_value. Is that enforced somewhere else?
>>>
>>> The standard says it's ill-formed, so we should also have a _neg.cc
>>> test checking that we reject it.
>>>
>>>> + __semaphore_base<__least_max_value> _M_sem;
>>>
>>> Blank line after this please.
>>>
>>>> + public:
>>>> + explicit counting_semaphore(ptrdiff_t __desired) noexcept
>>>> + : _M_sem(__desired)
>>>> + { }
>>>> +
>>>> + ~counting_semaphore() = default;
>>>> +
>>>> + counting_semaphore(const counting_semaphore&) = delete;
>>>> + counting_semaphore& operator=(const counting_semaphore&) = delete;
>>>> +
>>>> + static constexpr ptrdiff_t max() noexcept
>>>> + { return __least_max_value; }
>>>> +
>>>> + void release(ptrdiff_t __update = 1)
>>>> + { _M_sem.release(__update); }
>>>> +
>>>> + void acquire()
>>>> + { _M_sem.acquire(); }
>>>> +
>>>> + bool try_acquire() noexcept
>>>> + { return _M_sem.try_acquire(); }
>>>> +
>>>> + template<class _Rep, class _Period>
>>>
>>> s/class/template/
>>>
>>>> + bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time)
>>>> + { return _M_sem.try_acquire_for(__rel_time); }
>>>> +
>>>> + template<class _Clock, class _Duration>
>>>
>>> s/class/template/
>>>
>>>> + bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time)
>>>> + { return _M_sem.try_acquire_until(__abs_time); }
>>>> + };
>>>> +
>>>> + using binary_semaphore = std::counting_semaphore<1>;
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace
>>>> +#endif // __cplusplus > 201703L
>>>> +#endif // _GLIBCXX_SEMAPHORE
>>>> diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>>>> index c3a5bd26e63..390990282b0 100644
>>>> --- a/libstdc++-v3/include/std/version
>>>> +++ b/libstdc++-v3/include/std/version
>>>> @@ -188,6 +188,8 @@
>>>> #endif
>>>> #define __cpp_lib_type_identity 201806L
>>>> #define __cpp_lib_unwrap_ref 201811L
>>>> +#define __cpp_lib_semaphore 201907L
>>>> +#define __cpp_lib_latch 201907L
>>>
>>> These features aren't supported in a freestanding implementation, so
>>> should be in the #if _GLIBCXX_HOSTED block (and the macros should be
>>> in alphabetical order).
>>>
>>>>
>>>> #if _GLIBCXX_HOSTED
>>>> #undef __cpp_lib_array_constexpr
>>>> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
>>>> new file mode 100644
>>>> index 00000000000..1ced9d44b20
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
>>>> @@ -0,0 +1,103 @@
>>>> +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
>>>
>>> Use { dg-add-options libatomic } instead of adding -latomic -L...
>>
>
More information about the Libstdc++
mailing list