libstdc++: Add C++2a synchronization support

Add support for -
  * atomic_flag::wait/notify_one/notify_all
  * atomic::wait/notify_one/notify_all
  * counting_semaphore
  * binary_semaphore
  * latch

libstdc++-v3/ChangeLog:

	* include/Makefile.am (bits_headers): Add new header.
	* include/Makefile.in: Regenerate.
	* include/bits/atomic_base.h (__atomic_flag::wait): Define.
	(__atomic_flag::notify_one): Likewise.
	(__atomic_flag::notify_all): Likewise.
	(__atomic_base<_Itp>::wait): Likewise.
	(__atomic_base<_Itp>::notify_one): Likewise.
	(__atomic_base<_Itp>::notify_all): Likewise.
	(__atomic_base<_Ptp*>::wait): Likewise.
	(__atomic_base<_Ptp*>::notify_one): Likewise.
	(__atomic_base<_Ptp*>::notify_all): Likewise.
	(__atomic_impl::wait): Likewise.
	(__atomic_impl::notify_one): Likewise.
	(__atomic_impl::notify_all): Likewise.
	(__atomic_float<_Fp>::wait): Likewise.
	(__atomic_float<_Fp>::notify_one): Likewise.
	(__atomic_float<_Fp>::notify_all): Likewise.
	(__atomic_ref<_Tp>::wait): Likewise.
	(__atomic_ref<_Tp>::notify_one): Likewise.
	(__atomic_ref<_Tp>::notify_all): Likewise.
	(atomic_wait<_Tp>): Likewise.
	(atomic_wait_explicit<_Tp>): Likewise.
	(atomic_notify_one<_Tp>): Likewise.
	(atomic_notify_all<_Tp>): Likewise.
	* include/bits/atomic_wait.h: New file.
	* include/bits/atomic_timed_wait.h: New file.
	* include/bits/semaphore_base.h: New file.
	* include/std/atomic (atomic<bool>::wait): Define.
	(atomic<bool>::wait_one): Likewise.
	(atomic<bool>::wait_all): Likewise.
	(atomic<_Tp>::wait): Likewise.
	(atomic<_Tp>::wait_one): Likewise.
	(atomic<_Tp>::wait_all): Likewise.
	(atomic<_Tp*>::wait): Likewise.
	(atomic<_Tp*>::wait_one): Likewise.
	(atomic<_Tp*>::wait_all): Likewise.
	* include/std/latch: New file.
	* include/std/semaphore: New file.
	* include/std/version: Add __cpp_lib_semaphore and
	__cpp_lib_latch defines.
	* testsuite/29_atomics/atomic/wait_notify/bool.cc: New test.
	* testsuite/29_atomics/atomic/wait_notify/pointers.cc: Likewise.
	* testsuite/29_atomics/atomic/wait_notify/generic.cc: Liekwise.
	* testsuite/29_atomics/atomic_flag/wait_notify/1.cc: Likewise.
	* testsuite/29_atomics/atomic_float/wait_notify.cc: Likewise.
	* testsuite/29_atomics/atomic_integral/wait_notify.cc: Likewise.
	* testsuite/29_atomics/atomic_ref/wait_notify.cc: Likewise.
	* testsuite/30_threads/semaphore/1.cc: New test.
	* testsuite/30_threads/semaphore/2.cc: Likewise.
	* testsuite/30_threads/semaphore/least_max_value_neg.cc: Likewise.
	* testsuite/30_threads/semaphore/try_acquire.cc: Likewise.
	* testsuite/30_threads/semaphore/try_acquire_for.cc: Likewise.
	* testsuite/30_threads/semaphore/try_acquire_posix.cc: Likewise.
	* testsuite/30_threads/semaphore/try_acquire_until.cc: Likewise.
	* testsuite/30_threads/latch/1.cc: New test.
	* testsuite/30_threads/latch/2.cc: New test.
	* testsuite/30_threads/latch/3.cc: New test.
	* testsuite/util/atomic/wait_notify_util.h: New File.
This commit is contained in:
Thomas Rodgers 2020-11-20 12:29:34 -08:00
parent 89d9c634dc
commit 83a1beee27
28 changed files with 2506 additions and 1 deletions

View file

@ -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 \
@ -103,6 +105,8 @@ bits_headers = \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_wait.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
${bits_srcdir}/basic_string.h \
@ -178,6 +182,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 \

View file

@ -398,6 +398,7 @@ std_headers = \
${std_srcdir}/iostream \
${std_srcdir}/istream \
${std_srcdir}/iterator \
${std_srcdir}/latch \
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@ -415,6 +416,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@ -449,6 +451,8 @@ bits_headers = \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_wait.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
${bits_srcdir}/basic_string.h \
@ -524,6 +528,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 \

View file

@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
#if __cplusplus > 201703L
#include <bits/atomic_wait.h>
#endif
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@ -226,6 +229,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__atomic_load(&_M_i, &__v, int(__m));
return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL;
}
_GLIBCXX_ALWAYS_INLINE void
wait(bool __old,
memory_order __m = memory_order_seq_cst) const noexcept
{
std::__atomic_wait(&_M_i, __old,
[__m, this, __old]()
{ return this->test(__m) != __old; });
}
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ std::__atomic_notify(&_M_i, false); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ std::__atomic_notify(&_M_i, true); }
// TODO add const volatile overload
#endif // C++20
_GLIBCXX_ALWAYS_INLINE void
@ -576,6 +602,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__m));
}
#if __cplusplus > 201703L
_GLIBCXX_ALWAYS_INLINE void
wait(__int_type __old,
memory_order __m = memory_order_seq_cst) const noexcept
{
std::__atomic_wait(&_M_i, __old,
[__m, this, __old]
{ return this->load(__m) != __old; });
}
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ std::__atomic_notify(&_M_i, false); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ std::__atomic_notify(&_M_i, true); }
// TODO add const volatile overload
#endif // C++2a
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@ -845,6 +896,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
int(__m1), int(__m2));
}
#if __cplusplus > 201703L
_GLIBCXX_ALWAYS_INLINE void
wait(__pointer_type __old,
memory_order __m = memory_order_seq_cst) noexcept
{
std::__atomic_wait(&_M_p, __old,
[__m, this, __old]()
{ return this->load(__m) != __old; });
}
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ std::__atomic_notify(&_M_p, false); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ std::__atomic_notify(&_M_p, true); }
// TODO add const volatile overload
#endif // C++2a
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@ -933,6 +1009,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
int(__success), int(__failure));
}
#if __cplusplus > 201703L
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE void
wait(const _Tp* __ptr, _Val<_Tp> __old,
memory_order __m = memory_order_seq_cst) noexcept
{
std::__atomic_wait(__ptr, __old,
[=]() { return load(__ptr, __m) == __old; });
}
// TODO add const volatile overload
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE void
notify_one(const _Tp* __ptr) noexcept
{ std::__atomic_notify(__ptr, false); }
// TODO add const volatile overload
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE void
notify_all(const _Tp* __ptr) noexcept
{ std::__atomic_notify(__ptr, true); }
// TODO add const volatile overload
#endif // C++2a
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@ -1186,6 +1289,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__order));
}
_GLIBCXX_ALWAYS_INLINE void
wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept
{ __atomic_impl::wait(&_M_fp, __old, __m); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ __atomic_impl::notify_one(&_M_fp); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ __atomic_impl::notify_all(&_M_fp); }
// TODO add const volatile overload
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@ -1323,6 +1444,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__order));
}
_GLIBCXX_ALWAYS_INLINE void
wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
{ __atomic_impl::wait(_M_ptr, __old, __m); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ __atomic_impl::notify_one(_M_ptr); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ __atomic_impl::notify_all(_M_ptr); }
// TODO add const volatile overload
private:
_Tp* _M_ptr;
};
@ -1418,6 +1557,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__order));
}
_GLIBCXX_ALWAYS_INLINE void
wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
{ __atomic_impl::wait(_M_ptr, __old, __m); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ __atomic_impl::notify_one(_M_ptr); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ __atomic_impl::notify_all(_M_ptr); }
// TODO add const volatile overload
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@ -1573,6 +1730,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__order));
}
_GLIBCXX_ALWAYS_INLINE void
wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept
{ __atomic_impl::wait(_M_ptr, __old, __m); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ __atomic_impl::notify_one(_M_ptr); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ __atomic_impl::notify_all(_M_ptr); }
// TODO add const volatile overload
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@ -1682,6 +1857,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__order));
}
_GLIBCXX_ALWAYS_INLINE void
wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
{ __atomic_impl::wait(_M_ptr, __old, __m); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_one() const noexcept
{ __atomic_impl::notify_one(_M_ptr); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE void
notify_all() const noexcept
{ __atomic_impl::notify_all(_M_ptr); }
// TODO add const volatile overload
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept

View file

@ -0,0 +1,287 @@
// -*- C++ -*- header.
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/atomic_timed_wait.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{atomic}
*/
#ifndef _GLIBCXX_ATOMIC_TIMED_WAIT_H
#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1
#pragma GCC system_header
#include <bits/c++config.h>
#include <bits/functional_hash.h>
#include <bits/atomic_wait.h>
#include <chrono>
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
#include <sys/time.h>
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
enum class __atomic_wait_status { no_timeout, timeout };
namespace __detail
{
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
using __platform_wait_clock_t = chrono::steady_clock;
template<typename _Duration>
__atomic_wait_status
__platform_wait_until_impl(__platform_wait_t* __addr,
__platform_wait_t __val,
const chrono::time_point<
__platform_wait_clock_t, _Duration>&
__atime) noexcept
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
struct timespec __rt =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
auto __e = syscall (SYS_futex, __addr,
static_cast<int>(__futex_wait_flags::
__wait_bitset_private),
__val, &__rt, nullptr,
static_cast<int>(__futex_wait_flags::
__bitset_match_any));
if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT))
std::terminate();
return (__platform_wait_clock_t::now() < __atime)
? __atomic_wait_status::no_timeout
: __atomic_wait_status::timeout;
}
template<typename _Clock, typename _Duration>
__atomic_wait_status
__platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val,
const chrono::time_point<_Clock, _Duration>&
__atime)
{
if constexpr (is_same_v<__platform_wait_clock_t, _Clock>)
{
return __detail::__platform_wait_until_impl(__addr, __val, __atime);
}
else
{
const typename _Clock::time_point __c_entry = _Clock::now();
const __platform_wait_clock_t::time_point __s_entry =
__platform_wait_clock_t::now();
const auto __delta = __atime - __c_entry;
const auto __s_atime = __s_entry + __delta;
if (__detail::__platform_wait_until_impl(__addr, __val, __s_atime)
== __atomic_wait_status::no_timeout)
return __atomic_wait_status::no_timeout;
// We got a timeout when measured against __clock_t but
// we need to check against the caller-supplied clock
// to tell whether we should return a timeout.
if (_Clock::now() < __atime)
return __atomic_wait_status::no_timeout;
return __atomic_wait_status::timeout;
}
}
#endif
#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
template<typename _Duration>
__atomic_wait_status
__cond_wait_until_impl(__gthread_cond_t* __cv,
unique_lock<mutex>& __lock,
const chrono::time_point<chrono::steady_clock, _Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(),
CLOCK_MONOTONIC,
&__ts);
return (chrono::steady_clock::now() < __atime)
? __atomic_wait_status::no_timeout
: __atomic_wait_status::timeout;
}
#endif
template<typename _Duration>
__atomic_wait_status
__cond_wait_until_impl(__gthread_cond_t* __cv,
unique_lock<std::mutex>& __lock,
const chrono::time_point<chrono::system_clock, _Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
__gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(),
&__ts);
return (chrono::system_clock::now() < __atime)
? __atomic_wait_status::no_timeout
: __atomic_wait_status::timeout;
}
// return true if timeout
template<typename _Clock, typename _Duration>
__atomic_wait_status
__cond_wait_until(__gthread_cond_t* __cv,
unique_lock<std::mutex>& __lock,
const chrono::time_point<_Clock, _Duration>& __atime)
{
#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
using __clock_t = chrono::steady_clock;
#else
using __clock_t = chrono::system_clock;
#endif
const typename _Clock::time_point __c_entry = _Clock::now();
const __clock_t::time_point __s_entry = __clock_t::now();
const auto __delta = __atime - __c_entry;
const auto __s_atime = __s_entry + __delta;
if (std::__detail::__cond_wait_until_impl(__cv, __lock, __s_atime))
return __atomic_wait_status::no_timeout;
// We got a timeout when measured against __clock_t but
// we need to check against the caller-supplied clock
// to tell whether we should return a timeout.
if (_Clock::now() < __atime)
return __atomic_wait_status::no_timeout;
return __atomic_wait_status::timeout;
}
struct __timed_waiters : __waiters
{
template<typename _Clock, typename _Duration>
__atomic_wait_status
_M_do_wait_until(__platform_wait_t __version,
const chrono::time_point<_Clock, _Duration>& __atime)
{
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
return __detail::__platform_wait_until(&_M_ver, __version, __atime);
#else
__platform_wait_t __cur = 0;
__waiters::__lock_t __l(_M_mtx);
while (__cur <= __version)
{
if (__detail::__cond_wait_until(&_M_cv, __l, __atime)
== __atomic_wait_status::timeout)
return __atomic_wait_status::timeout;
__platform_wait_t __last = __cur;
__atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
if (__cur < __last)
break; // break the loop if version overflows
}
return __atomic_wait_status::no_timeout;
#endif
}
static __timed_waiters&
_S_timed_for(void* __t)
{
static_assert(sizeof(__timed_waiters) == sizeof(__waiters));
return static_cast<__timed_waiters&>(__waiters::_S_for(__t));
}
};
} // namespace __detail
template<typename _Tp, typename _Pred,
typename _Clock, typename _Duration>
bool
__atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred,
const chrono::time_point<_Clock, _Duration>&
__atime) noexcept
{
using namespace __detail;
if (std::__atomic_spin(__pred))
return true;
auto& __w = __timed_waiters::_S_timed_for((void*)__addr);
auto __version = __w._M_enter_wait();
do
{
__atomic_wait_status __res;
if constexpr (__platform_wait_uses_type<_Tp>)
{
__res = __detail::__platform_wait_until((__platform_wait_t*)(void*) __addr,
__old, __atime);
}
else
{
__res = __w._M_do_wait_until(__version, __atime);
}
if (__res == __atomic_wait_status::timeout)
return false;
}
while (!__pred() && __atime < _Clock::now());
__w._M_leave_wait();
// if timed out, return false
return (_Clock::now() < __atime);
}
template<typename _Tp, typename _Pred,
typename _Rep, typename _Period>
bool
__atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred,
const chrono::duration<_Rep, _Period>& __rtime) noexcept
{
using namespace __detail;
if (std::__atomic_spin(__pred))
return true;
if (!__rtime.count())
return false; // no rtime supplied, and spin did not acquire
using __dur = chrono::steady_clock::duration;
auto __reltime = chrono::duration_cast<__dur>(__rtime);
if (__reltime < __rtime)
++__reltime;
return __atomic_wait_until(__addr, __old, std::move(__pred),
chrono::steady_clock::now() + __reltime);
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -0,0 +1,306 @@
// -*- C++ -*- header.
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/atomic_wait.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{atomic}
*/
#ifndef _GLIBCXX_ATOMIC_WAIT_H
#define _GLIBCXX_ATOMIC_WAIT_H 1
#pragma GCC system_header
#include <bits/c++config.h>
#include <bits/functional_hash.h>
#include <bits/gthr.h>
#include <bits/std_mutex.h>
#include <bits/unique_lock.h>
#include <ext/numeric_traits.h>
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
#include <climits>
#include <unistd.h>
#include <syscall.h>
#endif
// TODO get this from Autoconf
#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace __detail
{
using __platform_wait_t = int;
constexpr auto __atomic_spin_count_1 = 16;
constexpr auto __atomic_spin_count_2 = 12;
inline constexpr
auto __platform_wait_max_value =
__gnu_cxx::__int_traits<__platform_wait_t>::__max;
template<typename _Tp>
inline constexpr bool __platform_wait_uses_type
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
= is_same_v<remove_cv_t<_Tp>, __platform_wait_t>;
#else
= false;
#endif
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
enum class __futex_wait_flags : int
{
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
__private_flag = 128,
#else
__private_flag = 0,
#endif
__wait = 0,
__wake = 1,
__wait_bitset = 9,
__wake_bitset = 10,
__wait_private = __wait | __private_flag,
__wake_private = __wake | __private_flag,
__wait_bitset_private = __wait_bitset | __private_flag,
__wake_bitset_private = __wake_bitset | __private_flag,
__bitset_match_any = -1
};
template<typename _Tp>
void
__platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept
{
for(;;)
{
auto __e = syscall (SYS_futex, static_cast<const void*>(__addr),
static_cast<int>(__futex_wait_flags::__wait_private),
__val, nullptr);
if (!__e)
break;
else if (!(errno == EINTR || errno == EAGAIN))
__throw_system_error(__e);
}
}
template<typename _Tp>
void
__platform_notify(const _Tp* __addr, bool __all) noexcept
{
syscall (SYS_futex, static_cast<const void*>(__addr),
static_cast<int>(__futex_wait_flags::__wake_private),
__all ? INT_MAX : 1);
}
#endif
struct __waiters
{
alignas(64) __platform_wait_t _M_ver = 0;
alignas(64) __platform_wait_t _M_wait = 0;
#ifndef _GLIBCXX_HAVE_LINUX_FUTEX
using __lock_t = std::unique_lock<std::mutex>;
mutable __lock_t::mutex_type _M_mtx;
# ifdef __GTHREAD_COND_INIT
mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT;
__waiters() noexcept = default;
# else
mutable __gthread_cond_t _M_cv;
__waiters() noexcept
{
__GTHREAD_COND_INIT_FUNCTION(&_M_cond);
}
# endif
#endif
__platform_wait_t
_M_enter_wait() noexcept
{
__platform_wait_t __res;
__atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE);
__atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL);
return __res;
}
void
_M_leave_wait() noexcept
{
__atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL);
}
void
_M_do_wait(__platform_wait_t __version) noexcept
{
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
__platform_wait(&_M_ver, __version);
#else
__platform_wait_t __cur = 0;
while (__cur <= __version)
{
__waiters::__lock_t __l(_M_mtx);
auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle());
if (__e)
__throw_system_error(__e);
__platform_wait_t __last = __cur;
__atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
if (__cur < __last)
break; // break the loop if version overflows
}
#endif
}
bool
_M_waiting() const noexcept
{
__platform_wait_t __res;
__atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE);
return __res;
}
void
_M_notify(bool __all) noexcept
{
__atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL);
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
__platform_notify(&_M_ver, __all);
#else
auto __e = __gthread_cond_broadcast(&_M_cv);
if (__e)
__throw_system_error(__e);
#endif
}
static __waiters&
_S_for(const void* __t)
{
const unsigned char __mask = 0xf;
static __waiters __w[__mask + 1];
auto __key = _Hash_impl::hash(__t) & __mask;
return __w[__key];
}
};
struct __waiter
{
__waiters& _M_w;
__platform_wait_t _M_version;
template<typename _Tp>
__waiter(const _Tp* __addr) noexcept
: _M_w(__waiters::_S_for(static_cast<const void*>(__addr)))
, _M_version(_M_w._M_enter_wait())
{ }
~__waiter()
{ _M_w._M_leave_wait(); }
void _M_do_wait() noexcept
{ _M_w._M_do_wait(_M_version); }
};
void
__thread_relax() noexcept
{
#if defined __i386__ || defined __x86_64__
__builtin_ia32_pause();
#elif defined _GLIBCXX_USE_SCHED_YIELD
__gthread_yield();
#endif
}
void
__thread_yield() noexcept
{
#if defined _GLIBCXX_USE_SCHED_YIELD
__gthread_yield();
#endif
}
} // namespace __detail
template<typename _Pred>
bool
__atomic_spin(_Pred& __pred) noexcept
{
for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i)
{
if (__pred())
return true;
if (__i < __detail::__atomic_spin_count_2)
__detail::__thread_relax();
else
__detail::__thread_yield();
}
return false;
}
template<typename _Tp, typename _Pred>
void
__atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept
{
using namespace __detail;
if (std::__atomic_spin(__pred))
return;
__waiter __w(__addr);
while (!__pred())
{
if constexpr (__platform_wait_uses_type<_Tp>)
{
__platform_wait(__addr, __old);
}
else
{
// TODO support timed backoff when this can be moved into the lib
__w._M_do_wait();
}
}
}
template<typename _Tp>
void
__atomic_notify(const _Tp* __addr, bool __all) noexcept
{
using namespace __detail;
auto& __w = __waiters::_S_for((void*)__addr);
if (!__w._M_waiting())
return;
if constexpr (__platform_wait_uses_type<_Tp>)
{
__platform_notify((__platform_wait_t*)(void*) __addr, __all);
}
else
{
__w._M_notify(__all);
}
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -0,0 +1,296 @@
// -*- C++ -*- header.
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/semaphore_base.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{semaphore}
*/
#ifndef _GLIBCXX_SEMAPHORE_BASE_H
#define _GLIBCXX_SEMAPHORE_BASE_H 1
#pragma GCC system_header
#include <bits/c++config.h>
#include <bits/atomic_base.h>
#include <bits/atomic_timed_wait.h>
#include <ext/numeric_traits.h>
#if __has_include(<semaphore.h>)
#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1
#include <semaphore.h>
#endif
#include <chrono>
#include <type_traits>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
struct __platform_semaphore
{
using __clock_t = chrono::system_clock;
static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
explicit __platform_semaphore(ptrdiff_t __count) noexcept
{
sem_init(&_M_semaphore, 0, __count);
}
__platform_semaphore(const __platform_semaphore&) = delete;
__platform_semaphore& operator=(const __platform_semaphore&) = delete;
~__platform_semaphore()
{ sem_destroy(&_M_semaphore); }
_GLIBCXX_ALWAYS_INLINE void
_M_acquire() noexcept
{
for (;;)
{
auto __err = sem_wait(&_M_semaphore);
if (__err && (errno == EINTR))
continue;
else if (__err)
std::terminate();
else
break;
}
}
_GLIBCXX_ALWAYS_INLINE void
_M_release(std::ptrdiff_t __update) noexcept
{
for(; __update != 0; --__update)
{
auto __err = sem_post(&_M_semaphore);
if (__err)
std::terminate();
}
}
bool
_M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
noexcept
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
struct timespec __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
for (;;)
{
if (auto __err = sem_timedwait(&_M_semaphore, &__ts))
{
if (errno == EINTR)
continue;
else if (errno == ETIMEDOUT || errno == EINVAL)
return false;
else
std::terminate();
}
else
break;
}
return true;
}
template<typename _Clock, typename _Duration>
bool
_M_try_acquire_until(const chrono::time_point<_Clock,
_Duration>& __atime) noexcept
{
if constexpr (std::is_same_v<__clock_t, _Clock>)
{
return _M_try_acquire_until_impl(__atime);
}
else
{
const typename _Clock::time_point __c_entry = _Clock::now();
const __clock_t __s_entry = __clock_t::now();
const auto __delta = __atime - __c_entry;
const auto __s_atime = __s_entry + __delta;
if (_M_try_acquire_until_impl(__s_atime))
return true;
// We got a timeout when measured against __clock_t but
// we need to check against the caller-supplied clock
// to tell whether we should return a timeout.
return (_Clock::now() < __atime);
}
}
template<typename _Rep, typename _Period>
_GLIBCXX_ALWAYS_INLINE bool
_M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
noexcept
{ return _M_try_acquire_until(__clock_t::now() + __rtime); }
private:
sem_t _M_semaphore;
};
#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
template<typename _Tp>
struct __atomic_semaphore
{
static_assert(std::is_integral_v<_Tp>);
static_assert(__gnu_cxx::__int_traits<_Tp>::__max
<= __gnu_cxx::__int_traits<ptrdiff_t>::__max);
static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max;
explicit __atomic_semaphore(_Tp __count) noexcept
: _M_counter(__count)
{
__glibcxx_assert(__count >= 0 && __count <= _S_max);
}
__atomic_semaphore(const __atomic_semaphore&) = delete;
__atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
_GLIBCXX_ALWAYS_INLINE void
_M_acquire() noexcept
{
auto const __pred = [this]
{
auto __old = __atomic_impl::load(&this->_M_counter,
memory_order::acquire);
if (__old == 0)
return false;
return __atomic_impl::compare_exchange_strong(&this->_M_counter,
__old, __old - 1,
memory_order::acquire,
memory_order::release);
};
auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed);
std::__atomic_wait(&_M_counter, __old, __pred);
}
bool
_M_try_acquire() noexcept
{
auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire);
auto const __pred = [this, __old]
{
if (__old == 0)
return false;
auto __prev = __old;
return __atomic_impl::compare_exchange_weak(&this->_M_counter,
__prev, __prev - 1,
memory_order::acquire,
memory_order::release);
};
return std::__atomic_spin(__pred);
}
template<typename _Clock, typename _Duration>
_GLIBCXX_ALWAYS_INLINE bool
_M_try_acquire_until(const chrono::time_point<_Clock,
_Duration>& __atime) noexcept
{
auto const __pred = [this]
{
auto __old = __atomic_impl::load(&this->_M_counter,
memory_order::acquire);
if (__old == 0)
return false;
return __atomic_impl::compare_exchange_strong(&this->_M_counter,
__old, __old - 1,
memory_order::acquire,
memory_order::release);
};
auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed);
return __atomic_wait_until(&_M_counter, __old, __pred, __atime);
}
template<typename _Rep, typename _Period>
_GLIBCXX_ALWAYS_INLINE bool
_M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
noexcept
{
auto const __pred = [this]
{
auto __old = __atomic_impl::load(&this->_M_counter,
memory_order::acquire);
if (__old == 0)
return false;
return __atomic_impl::compare_exchange_strong(&this->_M_counter,
__old, __old - 1,
memory_order::acquire,
memory_order::release);
};
auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed);
return __atomic_wait_for(&_M_counter, __old, __pred, __rtime);
}
_GLIBCXX_ALWAYS_INLINE void
_M_release(ptrdiff_t __update) noexcept
{
if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
return;
if (__update > 1)
__atomic_impl::notify_all(&_M_counter);
else
__atomic_impl::notify_one(&_M_counter);
}
private:
alignas(__alignof__(_Tp)) _Tp _M_counter;
};
// Note: the _GLIBCXX_REQUIRE_POSIX_SEMAPHORE macro can be used to force the
// use of Posix semaphores (sem_t). Doing so however, alters the ABI.
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE
// Use futex if available and didn't force use of POSIX
using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>;
#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
using __fast_semaphore = __platform_semaphore;
#else
using __fast_semaphore = __atomic_semaphore<ptrdiff_t>;
#endif
template<ptrdiff_t __least_max_value>
using __semaphore_impl = conditional_t<
(__least_max_value > 1),
conditional_t<
(__least_max_value <= __fast_semaphore::_S_max),
__fast_semaphore,
__atomic_semaphore<ptrdiff_t>>,
__fast_semaphore>;
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -163,6 +163,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
compare_exchange_strong(bool& __i1, bool __i2,
memory_order __m = memory_order_seq_cst) volatile noexcept
{ return _M_base.compare_exchange_strong(__i1, __i2, __m); }
#if __cplusplus > 201703L
void wait(bool __old, memory_order __m = memory_order_seq_cst) const
noexcept
{ _M_base.wait(__old, __m); }
// TODO add const volatile overload
void notify_one() const noexcept
{ _M_base.notify_one(); }
void notify_all() const noexcept
{ _M_base.notify_all(); }
#endif
};
#if __cplusplus <= 201703L
@ -363,6 +377,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
memory_order __m = memory_order_seq_cst) volatile noexcept
{ return compare_exchange_strong(__e, __i, __m,
__cmpexch_failure_order(__m)); }
#if __cplusplus > 201703L
void wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
{
std::__atomic_wait(&_M_i, __old,
[__m, this, __old]
{
const auto __v = this->load(__m);
// TODO make this ignore padding bits when we
// can do that
return __builtin_memcmp(&__old, &__v,
sizeof(_Tp)) != 0;
});
}
// TODO add const volatile overload
void notify_one() const noexcept
{ std::__atomic_notify(&_M_i, false); }
void notify_all() const noexcept
{ std::__atomic_notify(&_M_i, true); }
#endif
};
#undef _GLIBCXX20_INIT
@ -601,6 +639,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__cmpexch_failure_order(__m));
}
#if __cplusplus > 201703L
void wait(__pointer_type __old,
memory_order __m = memory_order_seq_cst) noexcept
{ _M_b.wait(__old, __m); }
// TODO add const volatile overload
void notify_one() const noexcept
{ _M_b.notify_one(); }
void notify_all() const noexcept
{ _M_b.notify_all(); }
#endif
__pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@ -1353,6 +1404,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
memory_order_seq_cst);
}
#if __cplusplus > 201703L
template<typename _Tp>
inline void
atomic_wait(const atomic<_Tp>* __a,
typename std::atomic<_Tp>::value_type __old) noexcept
{ __a->wait(__old); }
template<typename _Tp>
inline void
atomic_wait_explicit(const atomic<_Tp>* __a,
typename std::atomic<_Tp>::value_type __old,
std::memory_order __m) noexcept
{ __a->wait(__old, __m); }
template<typename _Tp>
inline void
atomic_notify_one(atomic<_Tp>* __a) noexcept
{ __a->notify_one(); }
template<typename _Tp>
inline void
atomic_notify_all(atomic<_Tp>* __a) noexcept
{ __a->notify_all(); }
#endif // C++2a
// Function templates for atomic_integral and atomic_pointer operations only.
// Some operations (and, or, xor) are only available for atomic integrals,
// which is implemented by taking a parameter of type __atomic_base<_ITp>*.

View file

@ -0,0 +1,91 @@
// <latch> -*- C++ -*-
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file include/latch
* This is a Standard C++ Library header.
*/
#ifndef _GLIBCXX_LATCH
#define _GLIBCXX_LATCH
#pragma GCC system_header
#if __cplusplus > 201703L
#define __cpp_lib_latch 201907L
#include <bits/atomic_base.h>
#include <ext/numeric_traits.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
class latch
{
public:
static constexpr ptrdiff_t
max() noexcept
{ return __gnu_cxx::__int_traits<ptrdiff_t>::__max; }
constexpr explicit latch(ptrdiff_t __expected) noexcept
: _M_a(__expected) { }
~latch() = default;
latch(const latch&) = delete;
latch& operator=(const latch&) = delete;
_GLIBCXX_ALWAYS_INLINE void
count_down(ptrdiff_t __update = 1)
{
auto const __old = __atomic_impl::fetch_sub(&_M_a,
__update, memory_order::release);
if (__old == __update)
__atomic_impl::notify_all(&_M_a);
}
_GLIBCXX_ALWAYS_INLINE bool
try_wait() const noexcept
{ return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; }
_GLIBCXX_ALWAYS_INLINE void
wait() const noexcept
{
auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire);
std::__atomic_wait(&_M_a, __old, [this] { return this->try_wait(); });
}
_GLIBCXX_ALWAYS_INLINE void
arrive_and_wait(ptrdiff_t __update = 1) noexcept
{
count_down(__update);
wait();
}
private:
alignas(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
};
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif // __cplusplus > 201703L
#endif // _GLIBCXX_LATCH

View file

@ -0,0 +1,92 @@
// <semaphore> -*- C++ -*-
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file include/semaphore
* This is a Standard C++ Library header.
*/
#ifndef _GLIBCXX_SEMAPHORE
#define _GLIBCXX_SEMAPHORE
#pragma GCC system_header
#if __cplusplus > 201703L
#define __cpp_lib_semaphore 201907L
#include <bits/semaphore_base.h>
#include <ext/numeric_traits.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
template<ptrdiff_t __least_max_value =
__gnu_cxx::__int_traits<ptrdiff_t>::__max>
class counting_semaphore
{
static_assert(__least_max_value >= 0);
__semaphore_impl<__least_max_value> _M_sem;
public:
explicit counting_semaphore(ptrdiff_t __desired) noexcept
: _M_sem(__desired)
{ }
~counting_semaphore() = default;
counting_semaphore(const counting_semaphore&) = delete;
counting_semaphore& operator=(const counting_semaphore&) = delete;
static constexpr ptrdiff_t
max() noexcept
{ return __least_max_value; }
void
release(ptrdiff_t __update = 1) noexcept(noexcept(_M_sem._M_release(1)))
{ _M_sem._M_release(__update); }
void
acquire() noexcept(noexcept(_M_sem._M_acquire()))
{ _M_sem._M_acquire(); }
bool
try_acquire() noexcept(noexcept(_M_sem._M_try_acquire()))
{ return _M_sem._M_try_acquire(); }
template<typename _Rep, typename _Period>
bool
try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime)
{ return _M_sem._M_try_acquire_for(__rtime); }
template<typename _Clock, typename _Dur>
bool
try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime)
{ return _M_sem._M_try_acquire_until(__atime); }
};
using binary_semaphore = std::counting_semaphore<1>;
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif // __cplusplus > 201703L
#endif // _GLIBCXX_SEMAPHORE

View file

@ -216,6 +216,7 @@
#ifdef _GLIBCXX_HAS_GTHREADS
# define __cpp_lib_jthread 201911L
#endif
#define __cpp_lib_latch 201907L
#define __cpp_lib_list_remove_return_type 201806L
#if __cpp_lib_concepts
# define __cpp_lib_make_obj_using_allocator 201811L
@ -225,6 +226,7 @@
#if __cpp_lib_concepts
# define __cpp_lib_ranges 201911L
#endif
#define __cpp_lib_semaphore 201907L
#define __cpp_lib_shift 201806L
#if __cpp_lib_concepts
# define __cpp_lib_span 202002L

View file

@ -0,0 +1,59 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <type_traits>
#include <chrono>
#include <testsuite_hooks.h>
int
main ()
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
std::atomic<bool> a(false);
std::atomic<bool> b(false);
std::thread t([&]
{
cv.notify_one();
a.wait(false);
if (a.load())
{
b.store(true);
}
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(true);
a.notify_one();
t.join();
VERIFY( b.load() );
return 0;
}

View file

@ -0,0 +1,31 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include "atomic/wait_notify_util.h"
int
main ()
{
struct S{ int i; };
check<S> check_s{S{0},S{42}};
return 0;
}

View file

@ -0,0 +1,59 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <type_traits>
#include <chrono>
#include <testsuite_hooks.h>
int
main ()
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
long aa;
long bb;
std::atomic<long*> a(nullptr);
std::thread t([&]
{
cv.notify_one();
a.wait(nullptr);
if (a.load() == &aa)
a.store(&bb);
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(&aa);
a.notify_one();
t.join();
VERIFY( a.load() == &bb);
return 0;
}

View file

@ -0,0 +1,61 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <concepts>
#include <mutex>
#include <thread>
#include <testsuite_hooks.h>
int
main()
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
std::atomic_flag a;
std::atomic_flag b;
std::thread t([&]
{
cv.notify_one();
a.wait(false);
b.test_and_set();
b.notify_one();
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.test_and_set();
a.notify_one();
b.wait(false);
t.join();
VERIFY( a.test() );
VERIFY( b.test() );
return 0;
}

View file

@ -0,0 +1,31 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include "atomic/wait_notify_util.h"
int
main ()
{
check<float> f;
check<double> d;
return 0;
}

View file

@ -0,0 +1,65 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include "atomic/wait_notify_util.h"
void
test01()
{
struct S{ int i; };
std::atomic<S> s;
s.wait(S{42});
}
int
main ()
{
// check<bool> bb;
check<char> ch;
check<signed char> sch;
check<unsigned char> uch;
check<short> s;
check<unsigned short> us;
check<int> i;
check<unsigned int> ui;
check<long> l;
check<unsigned long> ul;
check<long long> ll;
check<unsigned long long> ull;
check<wchar_t> wch;
check<char8_t> ch8;
check<char16_t> ch16;
check<char32_t> ch32;
check<int8_t> i8;
check<int16_t> i16;
check<int32_t> i32;
check<int64_t> i64;
check<uint8_t> u8;
check<uint16_t> u16;
check<uint32_t> u32;
check<uint64_t> u64;
return 0;
}

View file

@ -0,0 +1,90 @@
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <type_traits>
#include <testsuite_hooks.h>
template<typename Tp>
Tp check_wait_notify(Tp val1, Tp val2)
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
Tp aa = val1;
std::atomic_ref<Tp> a(aa);
std::thread t([&]
{
cv.notify_one();
a.wait(val1);
if (a.load() != val2)
a = val1;
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(val2);
a.notify_one();
t.join();
return a.load();
}
template<typename Tp,
bool = std::is_integral_v<Tp>
|| std::is_floating_point_v<Tp>>
struct check;
template<typename Tp>
struct check<Tp, true>
{
check()
{
Tp a = 0;
Tp b = 42;
VERIFY(check_wait_notify(a, b) == b);
}
};
template<typename Tp>
struct check<Tp, false>
{
check(Tp b)
{
Tp a;
VERIFY(check_wait_notify(a, b) == b);
}
};
int
main ()
{
check<long>();
check<double>();
return 0;
}

View file

@ -0,0 +1,27 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <latch>
#ifndef __cpp_lib_latch
# error "Feature-test macro for latch missing in <latch>"
#elif __cpp_lib_latch!= 201907L
# error "Feature-test macro for latch has wrong value in <latch>"
#endif

View file

@ -0,0 +1,27 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <version>
#ifndef __cpp_lib_latch
# error "Feature-test macro for latch missing in <version>"
#elif __cpp_lib_latch != 201907L
# error "Feature-test macro for latch has wrong value in <version>"
#endif

View file

@ -0,0 +1,69 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
//
#include <latch>
#include <atomic>
#include <thread>
#include <testsuite_hooks.h>
void
test01()
{
std::latch l(3);
VERIFY( !l.try_wait() );
auto fn = [&]
{
l.count_down();
};
std::thread t0(fn);
std::thread t1(fn);
l.arrive_and_wait();
t0.join();
t1.join();
VERIFY( l.try_wait() );
}
void
test02()
{
std::latch l(3);
std::thread t([&]
{
l.count_down();
});
l.arrive_and_wait(2);
t.join();
VERIFY( l.try_wait() );
}
int main()
{
test01();
test02();
return 0;
}

View file

@ -0,0 +1,27 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <semaphore>
#ifndef __cpp_lib_semaphore
# error "Feature-test macro for semaphore missing in <semaphore>"
#elif __cpp_lib_semaphore != 201907L
# error "Feature-test macro for semaphore has wrong value in <semaphore>"
#endif

View file

@ -0,0 +1,27 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <version>
#ifndef __cpp_lib_semaphore
# error "Feature-test macro for semaphore missing in <version>"
#elif __cpp_lib_semaphore != 201907L
# error "Feature-test macro for semaphore has wrong value in <version>"
#endif

View file

@ -0,0 +1,30 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
#include <semaphore>
int main()
{
std::counting_semaphore<-1> sem(2);
return 0;
}
// { dg-error "static assertion failed" "" { target *-*-* } 0 }

View file

@ -0,0 +1,55 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
#include <semaphore>
#include <limits>
#include <cstddef>
#include <testsuite_hooks.h>
void test01()
{
std::counting_semaphore<10> s(3);
s.acquire();
VERIFY( s.try_acquire() );
VERIFY( s.try_acquire() );
VERIFY( !s.try_acquire() );
s.release();
VERIFY( s.try_acquire() );
}
void test02()
{
std::binary_semaphore s(1);
s.acquire();
VERIFY( !s.try_acquire() );
s.release();
VERIFY( s.try_acquire() );
}
int main()
{
test01();
test02();
}

View file

@ -0,0 +1,85 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
#include <semaphore>
#include <chrono>
#include <thread>
#include <atomic>
#include <testsuite_hooks.h>
void test01()
{
using namespace std::chrono_literals;
std::counting_semaphore<10> s(2);
s.acquire();
auto const dur = 250ms;
{
auto const t0 = std::chrono::steady_clock::now();
VERIFY( s.try_acquire_for(dur) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff < dur );
}
{
auto const t0 = std::chrono::steady_clock::now();
VERIFY( !s.try_acquire_for(dur) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff >= dur );
}
}
void test02()
{
using namespace std::chrono_literals;
std::binary_semaphore s(1);
std::atomic<int> a(0), b(0);
std::thread t([&] {
a.wait(0);
auto const dur = 250ms;
VERIFY( !s.try_acquire_for(dur) );
b++;
b.notify_one();
a.wait(1);
VERIFY( s.try_acquire_for(dur) );
b++;
b.notify_one();
});
t.detach();
s.acquire();
a++;
a.notify_one();
b.wait(0);
s.release();
a++;
a.notify_one();
b.wait(1);
}
int main()
{
test01();
test02();
}

View file

@ -0,0 +1,153 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
#include <semaphore>
#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
#include <chrono>
#include <thread>
#include <atomic>
#include <testsuite_hooks.h>
void test01()
{
using namespace std::chrono_literals;
std::__platform_semaphore s(2);
s._M_acquire();
auto const dur = 250ms;
{
auto const t0 = std::chrono::steady_clock::now();
VERIFY( s._M_try_acquire_for(dur) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff < dur );
}
{
auto const t0 = std::chrono::steady_clock::now();
VERIFY( !s._M_try_acquire_for(dur) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff >= dur );
}
}
void test02()
{
using namespace std::chrono_literals;
std::__platform_semaphore s(1);
std::atomic<int> a(0), b(0);
std::thread t([&] {
a.wait(0);
auto const dur = 250ms;
VERIFY( !s._M_try_acquire_for(dur) );
b++;
b.notify_one();
a.wait(1);
VERIFY( s._M_try_acquire_for(dur) );
b++;
b.notify_one();
});
t.detach();
s._M_acquire();
a++;
a.notify_one();
b.wait(0);
s._M_release(1);
a++;
a.notify_one();
b.wait(1);
}
void test03()
{
using namespace std::chrono_literals;
std::__platform_semaphore s(2);
s._M_acquire();
auto const dur = 250ms;
{
auto const at = std::chrono::system_clock::now() + dur;
auto const t0 = std::chrono::steady_clock::now();
VERIFY( s._M_try_acquire_until(at) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff < dur );
}
{
auto const at = std::chrono::system_clock::now() + dur;
auto const t0 = std::chrono::steady_clock::now();
VERIFY( !s._M_try_acquire_until(at) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff >= dur );
}
}
void test04()
{
using namespace std::chrono_literals;
std::__platform_semaphore s(1);
std::atomic<int> a(0), b(0);
std::thread t([&] {
a.wait(0);
auto const dur = 250ms;
{
auto const at = std::chrono::system_clock::now() + dur;
VERIFY( !s._M_try_acquire_until(at) );
b++;
b.notify_one();
}
a.wait(1);
{
auto const at = std::chrono::system_clock::now() + dur;
VERIFY( s._M_try_acquire_until(at) );
}
b++;
b.notify_one();
});
t.detach();
s._M_acquire();
a++;
a.notify_one();
b.wait(0);
s._M_release(1);
a++;
a.notify_one();
b.wait(1);
}
#endif
int main()
{
#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
test01();
test02();
test03();
test04();
#endif
return 0;
}

View file

@ -0,0 +1,94 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a -pthread" }
// { dg-do run { target c++2a } }
// { dg-require-effective-target pthread }
// { dg-require-gthreads "" }
#include <semaphore>
#include <chrono>
#include <thread>
#include <atomic>
#include <testsuite_hooks.h>
void test01()
{
using namespace std::chrono_literals;
std::counting_semaphore<10> s(2);
s.acquire();
auto const dur = 250ms;
{
auto const at = std::chrono::system_clock::now() + dur;
auto const t0 = std::chrono::steady_clock::now();
VERIFY( s.try_acquire_until(at) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff < dur );
}
{
auto const at = std::chrono::system_clock::now() + dur;
auto const t0 = std::chrono::steady_clock::now();
VERIFY( !s.try_acquire_until(at) );
auto const diff = std::chrono::steady_clock::now() - t0;
VERIFY( diff >= dur );
}
}
void test02()
{
using namespace std::chrono_literals;
std::binary_semaphore s(1);
std::atomic<int> a(0), b(0);
std::thread t([&] {
a.wait(0);
auto const dur = 250ms;
{
auto const at = std::chrono::system_clock::now() + dur;
VERIFY( !s.try_acquire_until(at) );
b++;
b.notify_one();
}
a.wait(1);
{
auto const at = std::chrono::system_clock::now() + dur;
VERIFY( s.try_acquire_until(at) );
}
b++;
b.notify_one();
});
t.detach();
s.acquire();
a++;
a.notify_one();
b.wait(0);
s.release();
a++;
a.notify_one();
b.wait(1);
}
int main()
{
test01();
test02();
}

View file

@ -0,0 +1,160 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <concepts>
#include <mutex>
#include <thread>
#include <testsuite_hooks.h>
#include <iostream>
template<typename Tp>
Tp check_wait_notify(Tp val1, Tp val2)
requires std::equality_comparable<Tp>
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
std::atomic<Tp> a(val1);
std::thread t([&]
{
cv.notify_one();
a.wait(val1);
if (a.load() != val2)
a = val1;
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(val2);
a.notify_one();
t.join();
return a.load();
}
template<typename Tp>
Tp check_wait_notify(Tp val1, Tp val2)
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
std::atomic<Tp> a(val1);
std::thread t([&]
{
cv.notify_one();
a.wait(val1);
auto v = a.load();
// TODO this needs to zero padding bits when we can do that
if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
a = val1;
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(val2);
a.notify_one();
t.join();
return a.load();
}
template<typename Tp>
Tp check_atomic_wait_notify(Tp val1, Tp val2)
requires std::equality_comparable<Tp>
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
std::atomic<Tp> a(val1);
std::thread t([&]
{
cv.notify_one();
std::atomic_wait(&a, val1);
if (a.load() != val2)
a = val1;
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(val2);
std::atomic_notify_one(&a);
t.join();
return a.load();
}
template<typename Tp>
Tp check_atomic_wait_notify(Tp val1, Tp val2)
{
using namespace std::literals::chrono_literals;
std::mutex m;
std::condition_variable cv;
std::atomic<Tp> a(val1);
std::thread t([&]
{
cv.notify_one();
std::atomic_wait(&a, val1);
auto v = a.load();
// TODO this needs to zero padding bits when we can do that
if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
a = val1;
});
std::unique_lock<std::mutex> l(m);
cv.wait(l);
std::this_thread::sleep_for(100ms);
a.store(val2);
std::atomic_notify_one(&a);
t.join();
return a.load();
}
template<typename Tp>
struct check
{
check(Tp a = 0, Tp b = 42)
{
if constexpr (std::equality_comparable<Tp>)
{
VERIFY( check_wait_notify(a, b) == b);
VERIFY( check_atomic_wait_notify(a, b) == b);
}
else
{
{
// TODO this needs to zero padding bits when we can do that
auto v = check_wait_notify(a, b);
VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 );
}
{
// TODO this needs to zero padding bits when we can do that
auto v = check_atomic_wait_notify(a, b);
VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0);
}
}
}
};