[libc++] Implementation of C++20's P1135R6 for libcxx
Differential Revision: https://reviews.llvm.org/D68480
This commit is contained in:
committed by
Louis Dionne
parent
d66d25f838
commit
54fa9ecd30
@@ -25,6 +25,7 @@ set(files
|
||||
any
|
||||
array
|
||||
atomic
|
||||
barrier
|
||||
bit
|
||||
bitset
|
||||
cassert
|
||||
@@ -103,6 +104,7 @@ set(files
|
||||
iostream
|
||||
istream
|
||||
iterator
|
||||
latch
|
||||
limits
|
||||
limits.h
|
||||
list
|
||||
@@ -122,6 +124,7 @@ set(files
|
||||
ratio
|
||||
regex
|
||||
scoped_allocator
|
||||
semaphore
|
||||
set
|
||||
setjmp.h
|
||||
shared_mutex
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
#if defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
|
||||
# include <pthread.h>
|
||||
# include <sched.h>
|
||||
# include <semaphore.h>
|
||||
# ifdef __APPLE__
|
||||
# define _LIBCPP_NO_NATIVE_SEMAPHORES
|
||||
# endif
|
||||
#elif defined(_LIBCPP_HAS_THREAD_API_C11)
|
||||
# include <threads.h>
|
||||
#endif
|
||||
@@ -65,6 +69,10 @@ typedef pthread_mutex_t __libcpp_recursive_mutex_t;
|
||||
typedef pthread_cond_t __libcpp_condvar_t;
|
||||
#define _LIBCPP_CONDVAR_INITIALIZER PTHREAD_COND_INITIALIZER
|
||||
|
||||
// Semaphore
|
||||
typedef sem_t __libcpp_semaphore_t;
|
||||
#define _LIBCPP_SEMAPHORE_MAX SEM_VALUE_MAX
|
||||
|
||||
// Execute once
|
||||
typedef pthread_once_t __libcpp_exec_once_flag;
|
||||
#define _LIBCPP_EXEC_ONCE_INITIALIZER PTHREAD_ONCE_INIT
|
||||
@@ -127,6 +135,9 @@ typedef void* __libcpp_recursive_mutex_t[5];
|
||||
typedef void* __libcpp_condvar_t;
|
||||
#define _LIBCPP_CONDVAR_INITIALIZER 0
|
||||
|
||||
// Semaphore
|
||||
typedef void* __libcpp_semaphore_t;
|
||||
|
||||
// Execute Once
|
||||
typedef void* __libcpp_exec_once_flag;
|
||||
#define _LIBCPP_EXEC_ONCE_INITIALIZER 0
|
||||
@@ -191,6 +202,22 @@ int __libcpp_condvar_timedwait(__libcpp_condvar_t *__cv, __libcpp_mutex_t *__m,
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv);
|
||||
|
||||
// Semaphore
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init);
|
||||
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem);
|
||||
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem);
|
||||
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem);
|
||||
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns);
|
||||
|
||||
// Execute once
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
|
||||
@@ -229,6 +256,16 @@ void __libcpp_thread_yield();
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns);
|
||||
|
||||
struct __libcpp_timed_backoff_policy {
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
bool operator()(chrono::nanoseconds __elapsed) const;
|
||||
};
|
||||
|
||||
template<class _Fn, class _BFn>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool __libcpp_thread_poll_with_backoff(
|
||||
_Fn && __f, _BFn && __bf, chrono::nanoseconds __max_elapsed = chrono::nanoseconds::zero());
|
||||
|
||||
// Thread local storage
|
||||
_LIBCPP_THREAD_ABI_VISIBILITY
|
||||
int __libcpp_tls_create(__libcpp_tls_key* __key,
|
||||
@@ -364,6 +401,38 @@ int __libcpp_condvar_destroy(__libcpp_condvar_t *__cv)
|
||||
return pthread_cond_destroy(__cv);
|
||||
}
|
||||
|
||||
#ifndef _LIBCPP_NO_NATIVE_SEMAPHORES
|
||||
|
||||
// Semaphore
|
||||
bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init)
|
||||
{
|
||||
return sem_init(__sem, 0, __init) == 0;
|
||||
}
|
||||
|
||||
bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem)
|
||||
{
|
||||
return sem_destroy(__sem) == 0;
|
||||
}
|
||||
|
||||
bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem)
|
||||
{
|
||||
return sem_post(__sem) == 0;
|
||||
}
|
||||
|
||||
bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem)
|
||||
{
|
||||
return sem_wait(__sem) == 0;
|
||||
}
|
||||
|
||||
bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns)
|
||||
{
|
||||
auto const __abs_time = chrono::system_clock::now().time_since_epoch() + __ns;
|
||||
__libcpp_timespec_t __ts = __thread_detail::__convert_to_timespec(__abs_time);
|
||||
return sem_timedwait(__sem, &__ts) == 0;
|
||||
}
|
||||
|
||||
#endif //_LIBCPP_NO_NATIVE_SEMAPHORES
|
||||
|
||||
// Execute once
|
||||
int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
|
||||
void (*init_routine)()) {
|
||||
@@ -425,6 +494,40 @@ void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns)
|
||||
while (nanosleep(&__ts, &__ts) == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
bool __libcpp_timed_backoff_policy::operator()(chrono::nanoseconds __elapsed) const
|
||||
{
|
||||
if(__elapsed > chrono::milliseconds(128))
|
||||
__libcpp_thread_sleep_for(chrono::milliseconds(8));
|
||||
else if(__elapsed > chrono::microseconds(64))
|
||||
__libcpp_thread_sleep_for(__elapsed / 2);
|
||||
else if(__elapsed > chrono::microseconds(4))
|
||||
__libcpp_thread_yield();
|
||||
else
|
||||
; // poll
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr int __libcpp_polling_count = 64;
|
||||
|
||||
template<class _Fn, class _BFn>
|
||||
bool __libcpp_thread_poll_with_backoff(_Fn && __f, _BFn && __bf, chrono::nanoseconds __max_elapsed)
|
||||
{
|
||||
auto const __start = chrono::high_resolution_clock::now();
|
||||
for(int __count = 0;;) {
|
||||
if(__f())
|
||||
return true; // _Fn completion means success
|
||||
if(__count < __libcpp_polling_count) {
|
||||
__count += 1;
|
||||
continue;
|
||||
}
|
||||
chrono::nanoseconds const __elapsed = chrono::high_resolution_clock::now() - __start;
|
||||
if(__max_elapsed != chrono::nanoseconds::zero() && __max_elapsed < __elapsed)
|
||||
return false; // timeout failure
|
||||
if(__bf(__elapsed))
|
||||
return false; // _BFn completion means failure
|
||||
}
|
||||
}
|
||||
|
||||
// Thread local storage
|
||||
int __libcpp_tls_create(__libcpp_tls_key *__key, void (*__at_exit)(void *))
|
||||
{
|
||||
|
||||
@@ -547,8 +547,10 @@ void atomic_signal_fence(memory_order m) noexcept;
|
||||
*/
|
||||
|
||||
#include <__config>
|
||||
#include <__threading_support>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <version>
|
||||
|
||||
@@ -629,6 +631,11 @@ typedef enum memory_order {
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17
|
||||
|
||||
template <typename _Tp> _LIBCPP_INLINE_VISIBILITY
|
||||
bool __cxx_nonatomic_compare_equal(_Tp const& __lhs, _Tp const& __rhs) {
|
||||
return memcmp(&__lhs, &__rhs, sizeof(_Tp)) == 0;
|
||||
}
|
||||
|
||||
static_assert((is_same<underlying_type<memory_order>::type, __memory_order_underlying_t>::value),
|
||||
"unexpected underlying type for std::memory_order");
|
||||
|
||||
@@ -1218,9 +1225,9 @@ _LIBCPP_INLINE_VISIBILITY
|
||||
bool __cxx_atomic_compare_exchange_strong(volatile __cxx_atomic_lock_impl<_Tp>* __a,
|
||||
_Tp* __expected, _Tp __value, memory_order, memory_order) {
|
||||
__a->__lock();
|
||||
_Tp temp;
|
||||
__cxx_atomic_assign_volatile(temp, __a->__a_value);
|
||||
bool __ret = temp == *__expected;
|
||||
_Tp __temp;
|
||||
__cxx_atomic_assign_volatile(__temp, __a->__a_value);
|
||||
bool __ret = __temp == *__expected;
|
||||
if(__ret)
|
||||
__cxx_atomic_assign_volatile(__a->__a_value, __value);
|
||||
else
|
||||
@@ -1247,9 +1254,9 @@ _LIBCPP_INLINE_VISIBILITY
|
||||
bool __cxx_atomic_compare_exchange_weak(volatile __cxx_atomic_lock_impl<_Tp>* __a,
|
||||
_Tp* __expected, _Tp __value, memory_order, memory_order) {
|
||||
__a->__lock();
|
||||
_Tp temp;
|
||||
__cxx_atomic_assign_volatile(temp, __a->__a_value);
|
||||
bool __ret = temp == *__expected;
|
||||
_Tp __temp;
|
||||
__cxx_atomic_assign_volatile(__temp, __a->__a_value);
|
||||
bool __ret = __temp == *__expected;
|
||||
if(__ret)
|
||||
__cxx_atomic_assign_volatile(__a->__a_value, __value);
|
||||
else
|
||||
@@ -1452,6 +1459,73 @@ struct __cxx_atomic_impl : public _Base {
|
||||
: _Base(value) {}
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
using __cxx_contention_t = int32_t;
|
||||
#else
|
||||
using __cxx_contention_t = int64_t;
|
||||
#endif //__linux__
|
||||
|
||||
#if _LIBCPP_STD_VER >= 11
|
||||
|
||||
using __cxx_atomic_contention_t = __cxx_atomic_impl<__cxx_contention_t>;
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_PLATFORM_WAIT
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile*);
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile*);
|
||||
_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile*);
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(void const volatile*, __cxx_contention_t);
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile*);
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile*);
|
||||
_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile*);
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile*, __cxx_contention_t);
|
||||
|
||||
template <class _Atp, class _Fn>
|
||||
_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp* __a, _Fn && __test_fn)
|
||||
{
|
||||
auto const __libcpp_atomic_wait_backoff = [=](chrono::nanoseconds __elapsed) -> bool {
|
||||
if(__elapsed > chrono::microseconds(64))
|
||||
{
|
||||
auto const __monitor = __libcpp_atomic_monitor(__a);
|
||||
if(__test_fn())
|
||||
return true;
|
||||
__libcpp_atomic_wait(__a, __monitor);
|
||||
}
|
||||
else if(__elapsed > chrono::microseconds(4))
|
||||
__libcpp_thread_yield();
|
||||
else
|
||||
; // poll
|
||||
return false;
|
||||
};
|
||||
return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_atomic_wait_backoff);
|
||||
}
|
||||
|
||||
#else // _LIBCPP_HAS_NO_PLATFORM_WAIT
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile*) { }
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile*) { }
|
||||
template <class _Atp, class _Fn>
|
||||
_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp*, _Fn && __test_fn)
|
||||
{
|
||||
return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
|
||||
}
|
||||
|
||||
#endif // _LIBCPP_HAS_NO_PLATFORM_WAIT
|
||||
|
||||
template <class _Atp, class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp* __a, _Tp const __val, memory_order __order)
|
||||
{
|
||||
auto const __test_fn = [=]() -> bool {
|
||||
return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__a, __order), __val);
|
||||
};
|
||||
return __cxx_atomic_wait(__a, __test_fn);
|
||||
}
|
||||
|
||||
#endif //_LIBCPP_STD_VER >= 11
|
||||
|
||||
// general atomic<T>
|
||||
|
||||
template <class _Tp, bool = is_integral<_Tp>::value && !is_same<_Tp, bool>::value>
|
||||
@@ -1532,6 +1606,19 @@ struct __atomic_base // false
|
||||
memory_order __m = memory_order_seq_cst) _NOEXCEPT
|
||||
{return __cxx_atomic_compare_exchange_strong(&__a_, &__e, __d, __m, __m);}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
|
||||
{__cxx_atomic_wait(&__a_, __v, __m);}
|
||||
_LIBCPP_INLINE_VISIBILITY void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
|
||||
{__cxx_atomic_wait(&__a_, __v, __m);}
|
||||
_LIBCPP_INLINE_VISIBILITY void notify_one() volatile _NOEXCEPT
|
||||
{__cxx_atomic_notify_one(&__a_);}
|
||||
_LIBCPP_INLINE_VISIBILITY void notify_one() _NOEXCEPT
|
||||
{__cxx_atomic_notify_one(&__a_);}
|
||||
_LIBCPP_INLINE_VISIBILITY void notify_all() volatile _NOEXCEPT
|
||||
{__cxx_atomic_notify_all(&__a_);}
|
||||
_LIBCPP_INLINE_VISIBILITY void notify_all() _NOEXCEPT
|
||||
{__cxx_atomic_notify_all(&__a_);}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__atomic_base() _NOEXCEPT _LIBCPP_DEFAULT
|
||||
|
||||
@@ -1544,8 +1631,11 @@ struct __atomic_base // false
|
||||
__atomic_base& operator=(const __atomic_base&) volatile = delete;
|
||||
#else
|
||||
private:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__atomic_base(const __atomic_base&);
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__atomic_base& operator=(const __atomic_base&);
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__atomic_base& operator=(const __atomic_base&) volatile;
|
||||
#endif
|
||||
};
|
||||
@@ -1643,6 +1733,7 @@ struct atomic
|
||||
: public __atomic_base<_Tp>
|
||||
{
|
||||
typedef __atomic_base<_Tp> __base;
|
||||
typedef _Tp value_type;
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
atomic() _NOEXCEPT _LIBCPP_DEFAULT
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
@@ -1663,6 +1754,7 @@ struct atomic<_Tp*>
|
||||
: public __atomic_base<_Tp*>
|
||||
{
|
||||
typedef __atomic_base<_Tp*> __base;
|
||||
typedef _Tp* value_type;
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
atomic() _NOEXCEPT _LIBCPP_DEFAULT
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
@@ -1947,6 +2039,76 @@ atomic_compare_exchange_strong_explicit(atomic<_Tp>* __o, _Tp* __e,
|
||||
return __o->compare_exchange_strong(*__e, __d, __s, __f);
|
||||
}
|
||||
|
||||
// atomic_wait
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_wait(const volatile atomic<_Tp>* __o,
|
||||
typename atomic<_Tp>::value_type __v) _NOEXCEPT
|
||||
{
|
||||
return __o->wait(__v);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_wait(const atomic<_Tp>* __o,
|
||||
typename atomic<_Tp>::value_type __v) _NOEXCEPT
|
||||
{
|
||||
return __o->wait(__v);
|
||||
}
|
||||
|
||||
// atomic_wait_explicit
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_wait_explicit(const volatile atomic<_Tp>* __o,
|
||||
typename atomic<_Tp>::value_type __v,
|
||||
memory_order __m) _NOEXCEPT
|
||||
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)
|
||||
{
|
||||
return __o->wait(__v, __m);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_wait_explicit(const atomic<_Tp>* __o,
|
||||
typename atomic<_Tp>::value_type __v,
|
||||
memory_order __m) _NOEXCEPT
|
||||
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)
|
||||
{
|
||||
return __o->wait(__v, __m);
|
||||
}
|
||||
|
||||
// atomic_notify_one
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_notify_one(volatile atomic<_Tp>* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_one();
|
||||
}
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_notify_one(atomic<_Tp>* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_one();
|
||||
}
|
||||
|
||||
// atomic_notify_one
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_notify_all(volatile atomic<_Tp>* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_all();
|
||||
}
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void atomic_notify_all(atomic<_Tp>* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_all();
|
||||
}
|
||||
|
||||
// atomic_fetch_add
|
||||
|
||||
template <class _Tp>
|
||||
@@ -2279,6 +2441,13 @@ typedef struct atomic_flag
|
||||
{
|
||||
__cxx_atomic_impl<_LIBCPP_ATOMIC_FLAG_TYPE> __a_;
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool test(memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
|
||||
{return _LIBCPP_ATOMIC_FLAG_TYPE(true) == __cxx_atomic_load(&__a_, __m);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool test(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
|
||||
{return _LIBCPP_ATOMIC_FLAG_TYPE(true) == __cxx_atomic_load(&__a_, __m);}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool test_and_set(memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
|
||||
{return __cxx_atomic_exchange(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(true), __m);}
|
||||
@@ -2292,6 +2461,25 @@ typedef struct atomic_flag
|
||||
void clear(memory_order __m = memory_order_seq_cst) _NOEXCEPT
|
||||
{__cxx_atomic_store(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(false), __m);}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void wait(bool __v, memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
|
||||
{__cxx_atomic_wait(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(__v), __m);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void wait(bool __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
|
||||
{__cxx_atomic_wait(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(__v), __m);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void notify_one() volatile _NOEXCEPT
|
||||
{__cxx_atomic_notify_one(&__a_);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void notify_one() _NOEXCEPT
|
||||
{__cxx_atomic_notify_one(&__a_);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void notify_all() volatile _NOEXCEPT
|
||||
{__cxx_atomic_notify_all(&__a_);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void notify_all() _NOEXCEPT
|
||||
{__cxx_atomic_notify_all(&__a_);}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
atomic_flag() _NOEXCEPT _LIBCPP_DEFAULT
|
||||
|
||||
@@ -2304,12 +2492,44 @@ typedef struct atomic_flag
|
||||
atomic_flag& operator=(const atomic_flag&) volatile = delete;
|
||||
#else
|
||||
private:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
atomic_flag(const atomic_flag&);
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
atomic_flag& operator=(const atomic_flag&);
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
atomic_flag& operator=(const atomic_flag&) volatile;
|
||||
#endif
|
||||
} atomic_flag;
|
||||
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
atomic_flag_test(const volatile atomic_flag* __o) _NOEXCEPT
|
||||
{
|
||||
return __o->test();
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
atomic_flag_test(const atomic_flag* __o) _NOEXCEPT
|
||||
{
|
||||
return __o->test();
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
atomic_flag_test_explicit(const volatile atomic_flag* __o, memory_order __m) _NOEXCEPT
|
||||
{
|
||||
return __o->test(__m);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
atomic_flag_test_explicit(const atomic_flag* __o, memory_order __m) _NOEXCEPT
|
||||
{
|
||||
return __o->test(__m);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
atomic_flag_test_and_set(volatile atomic_flag* __o) _NOEXCEPT
|
||||
@@ -2366,6 +2586,64 @@ atomic_flag_clear_explicit(atomic_flag* __o, memory_order __m) _NOEXCEPT
|
||||
__o->clear(__m);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_wait(const volatile atomic_flag* __o, bool __v) _NOEXCEPT
|
||||
{
|
||||
__o->wait(__v);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_wait(const atomic_flag* __o, bool __v) _NOEXCEPT
|
||||
{
|
||||
__o->wait(__v);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_wait_explicit(const volatile atomic_flag* __o,
|
||||
bool __v, memory_order __m) _NOEXCEPT
|
||||
{
|
||||
__o->wait(__v, __m);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_wait_explicit(const atomic_flag* __o,
|
||||
bool __v, memory_order __m) _NOEXCEPT
|
||||
{
|
||||
__o->wait(__v, __m);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_notify_one(volatile atomic_flag* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_one();
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_notify_one(atomic_flag* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_one();
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_notify_all(volatile atomic_flag* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_all();
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
atomic_flag_notify_all(atomic_flag* __o) _NOEXCEPT
|
||||
{
|
||||
__o->notify_all();
|
||||
}
|
||||
|
||||
// fences
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
@@ -2434,6 +2712,33 @@ typedef atomic<ptrdiff_t> atomic_ptrdiff_t;
|
||||
typedef atomic<intmax_t> atomic_intmax_t;
|
||||
typedef atomic<uintmax_t> atomic_uintmax_t;
|
||||
|
||||
// atomic_*_lock_free : prefer the contention type most highly, then the largest lock-free type
|
||||
|
||||
#ifdef __cpp_lib_atomic_is_always_lock_free
|
||||
# define _LIBCPP_CONTENTION_LOCK_FREE __atomic_always_lock_free(sizeof(__cxx_contention_t), 0)
|
||||
#else
|
||||
# define _LIBCPP_CONTENTION_LOCK_FREE false
|
||||
#endif
|
||||
|
||||
#if ATOMIC_LLONG_LOCK_FREE == 2
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, long long>::type __libcpp_signed_lock_free;
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned long long>::type __libcpp_unsigned_lock_free;
|
||||
#elif ATOMIC_INT_LOCK_FREE == 2
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, int>::type __libcpp_signed_lock_free;
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned int>::type __libcpp_unsigned_lock_free;
|
||||
#elif ATOMIC_SHORT_LOCK_FREE == 2
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, short>::type __libcpp_signed_lock_free;
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned short>::type __libcpp_unsigned_lock_free;
|
||||
#elif ATOMIC_CHAR_LOCK_FREE == 2
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, char>::type __libcpp_signed_lock_free;
|
||||
typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned char>::type __libcpp_unsigned_lock_free;
|
||||
#else
|
||||
// No signed/unsigned lock-free types
|
||||
#endif
|
||||
|
||||
typedef atomic<__libcpp_signed_lock_free> atomic_signed_lock_free;
|
||||
typedef atomic<__libcpp_unsigned_lock_free> atomic_unsigned_lock_free;
|
||||
|
||||
#define ATOMIC_FLAG_INIT {false}
|
||||
#define ATOMIC_VAR_INIT(__v) {__v}
|
||||
|
||||
|
||||
322
libcxx/include/barrier
Normal file
322
libcxx/include/barrier
Normal file
@@ -0,0 +1,322 @@
|
||||
// -*- C++ -*-
|
||||
//===--------------------------- barrier ----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP_BARRIER
|
||||
#define _LIBCPP_BARRIER
|
||||
|
||||
/*
|
||||
barrier synopsis
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<class CompletionFunction = see below>
|
||||
class barrier
|
||||
{
|
||||
public:
|
||||
using arrival_token = see below;
|
||||
|
||||
constexpr explicit barrier(ptrdiff_t phase_count,
|
||||
CompletionFunction f = CompletionFunction());
|
||||
~barrier();
|
||||
|
||||
barrier(const barrier&) = delete;
|
||||
barrier& operator=(const barrier&) = delete;
|
||||
|
||||
[[nodiscard]] arrival_token arrive(ptrdiff_t update = 1);
|
||||
void wait(arrival_token&& arrival) const;
|
||||
|
||||
void arrive_and_wait();
|
||||
void arrive_and_drop();
|
||||
|
||||
private:
|
||||
CompletionFunction completion; // exposition only
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
#include <__config>
|
||||
#include <atomic>
|
||||
#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
|
||||
# include <memory>
|
||||
#endif
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#ifdef _LIBCPP_HAS_NO_THREADS
|
||||
# error <barrier> is not supported on this single threaded system
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER < 14
|
||||
# error <barrier> requires C++14 or later
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
struct __empty_completion
|
||||
{
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void operator()() noexcept
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
|
||||
|
||||
/*
|
||||
|
||||
The default implementation of __barrier_base is a classic tree barrier.
|
||||
|
||||
It looks different from literature pseudocode for two main reasons:
|
||||
1. Threads that call into std::barrier functions do not provide indices,
|
||||
so a numbering step is added before the actual barrier algorithm,
|
||||
appearing as an N+1 round to the N rounds of the tree barrier.
|
||||
2. A great deal of attention has been paid to avoid cache line thrashing
|
||||
by flattening the tree structure into cache-line sized arrays, that
|
||||
are indexed in an efficient way.
|
||||
|
||||
*/
|
||||
|
||||
using __barrier_phase_t = uint8_t;
|
||||
|
||||
class __barrier_algorithm_base;
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
__barrier_algorithm_base* __construct_barrier_algorithm_base(ptrdiff_t& __expected);
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
|
||||
__barrier_phase_t __old_phase);
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier);
|
||||
|
||||
template<class _CompletionF>
|
||||
class __barrier_base {
|
||||
|
||||
ptrdiff_t __expected;
|
||||
unique_ptr<__barrier_algorithm_base,
|
||||
decltype(&__destroy_barrier_algorithm_base)> __base;
|
||||
__atomic_base<ptrdiff_t> __expected_adjustment;
|
||||
_CompletionF __completion;
|
||||
__atomic_base<__barrier_phase_t> __phase;
|
||||
|
||||
public:
|
||||
using arrival_token = __barrier_phase_t;
|
||||
|
||||
static constexpr ptrdiff_t max() noexcept {
|
||||
return numeric_limits<ptrdiff_t>::max();
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
|
||||
: __expected(__expected), __base(__construct_barrier_algorithm_base(this->__expected),
|
||||
&__destroy_barrier_algorithm_base),
|
||||
__expected_adjustment(0), __completion(move(__completion)), __phase(0)
|
||||
{
|
||||
}
|
||||
[[nodiscard]] _LIBCPP_INLINE_VISIBILITY
|
||||
arrival_token arrive(ptrdiff_t update)
|
||||
{
|
||||
auto const __old_phase = __phase.load(memory_order_relaxed);
|
||||
for(; update; --update)
|
||||
if(__arrive_barrier_algorithm_base(__base.get(), __old_phase)) {
|
||||
__completion();
|
||||
__expected += __expected_adjustment.load(memory_order_relaxed);
|
||||
__expected_adjustment.store(0, memory_order_relaxed);
|
||||
__phase.store(__old_phase + 2, memory_order_release);
|
||||
__phase.notify_all();
|
||||
}
|
||||
return __old_phase;
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void wait(arrival_token&& __old_phase) const
|
||||
{
|
||||
auto const __test_fn = [=]() -> bool {
|
||||
return __phase.load(memory_order_acquire) != __old_phase;
|
||||
};
|
||||
__libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void arrive_and_drop()
|
||||
{
|
||||
__expected_adjustment.fetch_sub(1, memory_order_relaxed);
|
||||
(void)arrive(1);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
|
||||
The alternative implementation of __barrier_base is a central barrier.
|
||||
|
||||
Two versions of this algorithm are provided:
|
||||
1. A fairly straightforward implementation of the litterature for the
|
||||
general case where the completion function is not empty.
|
||||
2. An optimized implementation that exploits 2's complement arithmetic
|
||||
and well-defined overflow in atomic arithmetic, to handle the phase
|
||||
roll-over for free.
|
||||
|
||||
*/
|
||||
|
||||
template<class _CompletionF>
|
||||
class __barrier_base {
|
||||
|
||||
__atomic_base<ptrdiff_t> __expected;
|
||||
__atomic_base<ptrdiff_t> __arrived;
|
||||
_CompletionF __completion;
|
||||
__atomic_base<bool> __phase;
|
||||
public:
|
||||
using arrival_token = bool;
|
||||
|
||||
static constexpr ptrdiff_t max() noexcept {
|
||||
return numeric_limits<ptrdiff_t>::max();
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
|
||||
: __expected(__expected), __arrived(__expected), __completion(move(__completion)), __phase(false)
|
||||
{
|
||||
}
|
||||
[[nodiscard]] _LIBCPP_INLINE_VISIBILITY
|
||||
arrival_token arrive(ptrdiff_t update)
|
||||
{
|
||||
auto const __old_phase = __phase.load(memory_order_relaxed);
|
||||
auto const __result = __arrived.fetch_sub(update, memory_order_acq_rel) - update;
|
||||
auto const new_expected = __expected.load(memory_order_relaxed);
|
||||
if(0 == __result) {
|
||||
__completion();
|
||||
__arrived.store(new_expected, memory_order_relaxed);
|
||||
__phase.store(!__old_phase, memory_order_release);
|
||||
__phase.notify_all();
|
||||
}
|
||||
return __old_phase;
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void wait(arrival_token&& __old_phase) const
|
||||
{
|
||||
__phase.wait(__old_phase, memory_order_acquire);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void arrive_and_drop()
|
||||
{
|
||||
__expected.fetch_sub(1, memory_order_relaxed);
|
||||
(void)arrive(1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class __barrier_base<__empty_completion> {
|
||||
|
||||
static constexpr uint64_t __expected_unit = 1ull;
|
||||
static constexpr uint64_t __arrived_unit = 1ull << 32;
|
||||
static constexpr uint64_t __expected_mask = __arrived_unit - 1;
|
||||
static constexpr uint64_t __phase_bit = 1ull << 63;
|
||||
static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask;
|
||||
|
||||
__atomic_base<uint64_t> __phase_arrived_expected;
|
||||
|
||||
static _LIBCPP_INLINE_VISIBILITY
|
||||
constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT
|
||||
{
|
||||
return ((uint64_t(1u << 31) - __count) << 32)
|
||||
| (uint64_t(1u << 31) - __count);
|
||||
}
|
||||
|
||||
public:
|
||||
using arrival_token = uint64_t;
|
||||
|
||||
static constexpr ptrdiff_t max() noexcept {
|
||||
return ptrdiff_t(1u << 31) - 1;
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
explicit inline __barrier_base(ptrdiff_t __count, __empty_completion = __empty_completion())
|
||||
: __phase_arrived_expected(__init(__count))
|
||||
{
|
||||
}
|
||||
[[nodiscard]] inline _LIBCPP_INLINE_VISIBILITY
|
||||
arrival_token arrive(ptrdiff_t update)
|
||||
{
|
||||
auto const __inc = __arrived_unit * update;
|
||||
auto const __old = __phase_arrived_expected.fetch_add(__inc, memory_order_acq_rel);
|
||||
if((__old ^ (__old + __inc)) & __phase_bit) {
|
||||
__phase_arrived_expected.fetch_add((__old & __expected_mask) << 32, memory_order_relaxed);
|
||||
__phase_arrived_expected.notify_all();
|
||||
}
|
||||
return __old & __phase_bit;
|
||||
}
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void wait(arrival_token&& __phase) const
|
||||
{
|
||||
auto const __test_fn = [=]() -> bool {
|
||||
uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire);
|
||||
return ((__current & __phase_bit) != __phase);
|
||||
};
|
||||
__libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
|
||||
}
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void arrive_and_drop()
|
||||
{
|
||||
__phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed);
|
||||
(void)arrive(1);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_LIBCPP_HAS_NO_TREE_BARRIER
|
||||
|
||||
template<class _CompletionF = __empty_completion>
|
||||
class barrier {
|
||||
|
||||
__barrier_base<_CompletionF> __b;
|
||||
public:
|
||||
using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
|
||||
|
||||
static constexpr ptrdiff_t max() noexcept {
|
||||
return __barrier_base<_CompletionF>::max();
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
|
||||
: __b(__count, std::move(__completion)) {
|
||||
}
|
||||
|
||||
barrier(barrier const&) = delete;
|
||||
barrier& operator=(barrier const&) = delete;
|
||||
|
||||
[[nodiscard]] _LIBCPP_INLINE_VISIBILITY
|
||||
arrival_token arrive(ptrdiff_t update = 1)
|
||||
{
|
||||
return __b.arrive(update);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void wait(arrival_token&& __phase) const
|
||||
{
|
||||
__b.wait(std::move(__phase));
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void arrive_and_wait()
|
||||
{
|
||||
wait(arrive());
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void arrive_and_drop()
|
||||
{
|
||||
__b.arrive_and_drop();
|
||||
}
|
||||
};
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif //_LIBCPP_BARRIER
|
||||
104
libcxx/include/latch
Normal file
104
libcxx/include/latch
Normal file
@@ -0,0 +1,104 @@
|
||||
// -*- C++ -*-
|
||||
//===--------------------------- latch -----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP_LATCH
|
||||
#define _LIBCPP_LATCH
|
||||
|
||||
/*
|
||||
latch synopsis
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
class latch
|
||||
{
|
||||
public:
|
||||
constexpr explicit latch(ptrdiff_t __expected);
|
||||
~latch();
|
||||
|
||||
latch(const latch&) = delete;
|
||||
latch& operator=(const latch&) = delete;
|
||||
|
||||
void count_down(ptrdiff_t __update = 1);
|
||||
bool try_wait() const noexcept;
|
||||
void wait() const;
|
||||
void arrive_and_wait(ptrdiff_t __update = 1);
|
||||
|
||||
private:
|
||||
ptrdiff_t __counter; // exposition only
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
#include <__config>
|
||||
#include <atomic>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#ifdef _LIBCPP_HAS_NO_THREADS
|
||||
# error <latch> is not supported on this single threaded system
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER < 14
|
||||
# error <latch> requires C++14 or later
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
class latch
|
||||
{
|
||||
__atomic_base<ptrdiff_t> __a;
|
||||
|
||||
public:
|
||||
static constexpr ptrdiff_t max() noexcept {
|
||||
return numeric_limits<ptrdiff_t>::max();
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
constexpr explicit latch(ptrdiff_t __expected) : __a(__expected) { }
|
||||
|
||||
~latch() = default;
|
||||
latch(const latch&) = delete;
|
||||
latch& operator=(const latch&) = delete;
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void count_down(ptrdiff_t __update = 1)
|
||||
{
|
||||
auto const __old = __a.fetch_sub(__update, memory_order_release);
|
||||
if(__old == __update)
|
||||
__a.notify_all();
|
||||
}
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool try_wait() const noexcept
|
||||
{
|
||||
return 0 == __a.load(memory_order_acquire);
|
||||
}
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void wait() const
|
||||
{
|
||||
auto const __test_fn = [=]() -> bool {
|
||||
return try_wait();
|
||||
};
|
||||
__cxx_atomic_wait(&__a.__a_, __test_fn);
|
||||
}
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void arrive_and_wait(ptrdiff_t __update = 1)
|
||||
{
|
||||
count_down(__update);
|
||||
wait();
|
||||
}
|
||||
};
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif //_LIBCPP_LATCH
|
||||
@@ -231,6 +231,10 @@ module std [system] {
|
||||
header "atomic"
|
||||
export *
|
||||
}
|
||||
module barrier {
|
||||
header "barrier"
|
||||
export *
|
||||
}
|
||||
module bit {
|
||||
header "bit"
|
||||
export *
|
||||
@@ -334,6 +338,10 @@ module std [system] {
|
||||
header "iterator"
|
||||
export *
|
||||
}
|
||||
module latch {
|
||||
header "latch"
|
||||
export *
|
||||
}
|
||||
module limits {
|
||||
header "limits"
|
||||
export *
|
||||
@@ -400,6 +408,10 @@ module std [system] {
|
||||
header "scoped_allocator"
|
||||
export *
|
||||
}
|
||||
module semaphore {
|
||||
header "semaphore"
|
||||
export *
|
||||
}
|
||||
module set {
|
||||
header "set"
|
||||
export initializer_list
|
||||
|
||||
233
libcxx/include/semaphore
Normal file
233
libcxx/include/semaphore
Normal file
@@ -0,0 +1,233 @@
|
||||
// -*- C++ -*-
|
||||
//===--------------------------- semaphore --------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP_SEMAPHORE
|
||||
#define _LIBCPP_SEMAPHORE
|
||||
|
||||
/*
|
||||
semaphore synopsis
|
||||
|
||||
namespace std {
|
||||
|
||||
template<ptrdiff_t least_max_value = implementation-defined>
|
||||
class counting_semaphore
|
||||
{
|
||||
public:
|
||||
static constexpr ptrdiff_t max() noexcept;
|
||||
|
||||
constexpr explicit counting_semaphore(ptrdiff_t desired);
|
||||
~counting_semaphore();
|
||||
|
||||
counting_semaphore(const counting_semaphore&) = delete;
|
||||
counting_semaphore& operator=(const counting_semaphore&) = delete;
|
||||
|
||||
void release(ptrdiff_t update = 1);
|
||||
void acquire();
|
||||
bool try_acquire() noexcept;
|
||||
template<class Rep, class Period>
|
||||
bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
|
||||
template<class Clock, class Duration>
|
||||
bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
|
||||
|
||||
private:
|
||||
ptrdiff_t counter; // exposition only
|
||||
};
|
||||
|
||||
using binary_semaphore = counting_semaphore<1>;
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
#include <__config>
|
||||
#include <__threading_support>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#ifdef _LIBCPP_HAS_NO_THREADS
|
||||
# error <semaphore> is not supported on this single threaded system
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER < 14
|
||||
# error <semaphore> is requires C++14 or later
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
/*
|
||||
|
||||
__atomic_semaphore_base is the general-case implementation, to be used for
|
||||
user-requested least-max values that exceed the OS implementation support
|
||||
(incl. when the OS has no support of its own) and for binary semaphores.
|
||||
|
||||
It is a typical Dijsktra semaphore algorithm over atomics, wait and notify
|
||||
functions. It avoids contention against users' own use of those facilities.
|
||||
|
||||
*/
|
||||
|
||||
class __atomic_semaphore_base
|
||||
{
|
||||
__atomic_base<ptrdiff_t> __a;
|
||||
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__atomic_semaphore_base(ptrdiff_t __count) : __a(__count)
|
||||
{
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void release(ptrdiff_t __update = 1)
|
||||
{
|
||||
if(0 < __a.fetch_add(__update, memory_order_release))
|
||||
;
|
||||
else if(__update > 1)
|
||||
__a.notify_all();
|
||||
else
|
||||
__a.notify_one();
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void acquire()
|
||||
{
|
||||
auto const __test_fn = [=]() -> bool {
|
||||
auto __old = __a.load(memory_order_relaxed);
|
||||
return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
|
||||
};
|
||||
__cxx_atomic_wait(&__a.__a_, __test_fn);
|
||||
}
|
||||
template <class Rep, class Period>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
|
||||
{
|
||||
auto const __test_fn = [=]() -> bool {
|
||||
auto __old = __a.load(memory_order_acquire);
|
||||
while(1) {
|
||||
if (__old == 0)
|
||||
return false;
|
||||
if(__a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed))
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy(), __rel_time);
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef _LIBCPP_NO_NATIVE_SEMAPHORES
|
||||
|
||||
/*
|
||||
|
||||
__platform_semaphore_base a simple wrapper for the OS semaphore type. That
|
||||
is, every call is routed to the OS in the most direct manner possible.
|
||||
|
||||
*/
|
||||
|
||||
class __platform_semaphore_base
|
||||
{
|
||||
__libcpp_semaphore_t __semaphore;
|
||||
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__platform_semaphore_base(ptrdiff_t __count) :
|
||||
__semaphore()
|
||||
{
|
||||
__libcpp_semaphore_init(&__semaphore, __count);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
~__platform_semaphore_base() {
|
||||
__libcpp_semaphore_destroy(&__semaphore);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void release(ptrdiff_t __update)
|
||||
{
|
||||
for(; __update; --__update)
|
||||
__libcpp_semaphore_post(&__semaphore);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void acquire()
|
||||
{
|
||||
__libcpp_semaphore_wait(&__semaphore);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool try_acquire_for(chrono::nanoseconds __rel_time)
|
||||
{
|
||||
return __libcpp_semaphore_wait_timed(&__semaphore, __rel_time);
|
||||
}
|
||||
};
|
||||
|
||||
template<ptrdiff_t __least_max_value>
|
||||
using __semaphore_base =
|
||||
typename conditional<(__least_max_value > 1 && __least_max_value <= _LIBCPP_SEMAPHORE_MAX),
|
||||
__platform_semaphore_base,
|
||||
__atomic_semaphore_base>::type;
|
||||
|
||||
#else
|
||||
|
||||
template<ptrdiff_t __least_max_value>
|
||||
using __semaphore_base =
|
||||
__atomic_semaphore_base;
|
||||
|
||||
#endif //_LIBCPP_NO_NATIVE_SEMAPHORES
|
||||
|
||||
template<ptrdiff_t __least_max_value = _LIBCPP_SEMAPHORE_MAX>
|
||||
class counting_semaphore
|
||||
{
|
||||
__semaphore_base<__least_max_value> __semaphore;
|
||||
|
||||
public:
|
||||
static constexpr ptrdiff_t max() noexcept {
|
||||
return __least_max_value;
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
counting_semaphore(ptrdiff_t __count = 0) : __semaphore(__count) { }
|
||||
~counting_semaphore() = default;
|
||||
|
||||
counting_semaphore(const counting_semaphore&) = delete;
|
||||
counting_semaphore& operator=(const counting_semaphore&) = delete;
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void release(ptrdiff_t __update = 1)
|
||||
{
|
||||
__semaphore.release(__update);
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void acquire()
|
||||
{
|
||||
__semaphore.acquire();
|
||||
}
|
||||
template<class Rep, class Period>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
|
||||
{
|
||||
return __semaphore.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time));
|
||||
}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool try_acquire()
|
||||
{
|
||||
return try_acquire_for(chrono::nanoseconds::zero());
|
||||
}
|
||||
template <class Clock, class Duration>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool try_acquire_until(chrono::time_point<Clock, Duration> const& __abs_time)
|
||||
{
|
||||
auto const current = Clock::now();
|
||||
if(current >= __abs_time)
|
||||
return try_acquire();
|
||||
else
|
||||
return try_acquire_for(__abs_time - current);
|
||||
}
|
||||
};
|
||||
|
||||
using binary_semaphore = counting_semaphore<1>;
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif //_LIBCPP_SEMAPHORE
|
||||
@@ -4,6 +4,8 @@ set(LIBCXX_LIB_CMAKEFILES_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTOR
|
||||
set(LIBCXX_SOURCES
|
||||
algorithm.cpp
|
||||
any.cpp
|
||||
atomic.cpp
|
||||
barrier.cpp
|
||||
bind.cpp
|
||||
charconv.cpp
|
||||
chrono.cpp
|
||||
|
||||
189
libcxx/src/atomic.cpp
Normal file
189
libcxx/src/atomic.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
//===------------------------- atomic.cpp ---------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <__config>
|
||||
#ifndef _LIBCPP_HAS_NO_THREADS
|
||||
|
||||
#include <climits>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <linux/futex.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#else // <- Add other operating systems here
|
||||
|
||||
// Baseline needs no new headers
|
||||
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
|
||||
__cxx_contention_t __val)
|
||||
{
|
||||
static constexpr timespec __timeout = { 2, 0 };
|
||||
syscall(SYS_futex, __ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0);
|
||||
}
|
||||
|
||||
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
|
||||
bool __notify_one)
|
||||
{
|
||||
syscall(SYS_futex, __ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK)
|
||||
|
||||
extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value,
|
||||
uint32_t timeout); /* timeout is specified in microseconds */
|
||||
extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
|
||||
|
||||
#define UL_COMPARE_AND_WAIT 1
|
||||
#define ULF_WAKE_ALL 0x00000100
|
||||
|
||||
static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
|
||||
__cxx_contention_t __val)
|
||||
{
|
||||
__ulock_wait(UL_COMPARE_AND_WAIT,
|
||||
const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0);
|
||||
}
|
||||
|
||||
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
|
||||
bool __notify_one)
|
||||
{
|
||||
__ulock_wake(UL_COMPARE_AND_WAIT | (__notify_one ? 0 : ULF_WAKE_ALL),
|
||||
const_cast<__cxx_atomic_contention_t*>(__ptr), 0);
|
||||
}
|
||||
|
||||
#else // <- Add other operating systems here
|
||||
|
||||
// Baseline is just a timed backoff
|
||||
|
||||
static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
|
||||
__cxx_contention_t __val)
|
||||
{
|
||||
__libcpp_thread_poll_with_backoff([=]() -> bool {
|
||||
return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val);
|
||||
}, __libcpp_timed_backoff_policy());
|
||||
}
|
||||
|
||||
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) { }
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
static constexpr size_t __libcpp_contention_table_size = (1 << 8); /* < there's no magic in this number */
|
||||
|
||||
struct alignas(64) /* aim to avoid false sharing */ __libcpp_contention_table_entry
|
||||
{
|
||||
__cxx_atomic_contention_t __contention_state;
|
||||
__cxx_atomic_contention_t __platform_state;
|
||||
inline constexpr __libcpp_contention_table_entry() :
|
||||
__contention_state(0), __platform_state(0) { }
|
||||
};
|
||||
|
||||
static __libcpp_contention_table_entry __libcpp_contention_table[ __libcpp_contention_table_size ];
|
||||
|
||||
static hash<void const volatile*> __libcpp_contention_hasher;
|
||||
|
||||
static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile * p)
|
||||
{
|
||||
return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)];
|
||||
}
|
||||
|
||||
/* Given an atomic to track contention and an atomic to actually wait on, which may be
|
||||
the same atomic, we try to detect contention to avoid spuriously calling the platform. */
|
||||
|
||||
static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state,
|
||||
__cxx_atomic_contention_t const volatile* __platform_state,
|
||||
bool __notify_one)
|
||||
{
|
||||
if(0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst))
|
||||
// We only call 'wake' if we consumed a contention bit here.
|
||||
__libcpp_platform_wake_by_address(__platform_state, __notify_one);
|
||||
}
|
||||
static __cxx_contention_t __libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* __contention_state,
|
||||
__cxx_atomic_contention_t const volatile* __platform_state)
|
||||
{
|
||||
// We will monitor this value.
|
||||
return __cxx_atomic_load(__platform_state, memory_order_acquire);
|
||||
}
|
||||
static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state,
|
||||
__cxx_atomic_contention_t const volatile* __platform_state,
|
||||
__cxx_contention_t __old_value)
|
||||
{
|
||||
__cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst);
|
||||
// We sleep as long as the monitored value hasn't changed.
|
||||
__libcpp_platform_wait_on_address(__platform_state, __old_value);
|
||||
__cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release);
|
||||
}
|
||||
|
||||
/* When the incoming atomic is the wrong size for the platform wait size, need to
|
||||
launder the value sequence through an atomic from our table. */
|
||||
|
||||
static void __libcpp_atomic_notify(void const volatile* __location)
|
||||
{
|
||||
auto const __entry = __libcpp_contention_state(__location);
|
||||
// The value sequence laundering happens on the next line below.
|
||||
__cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release);
|
||||
__libcpp_contention_notify(&__entry->__contention_state,
|
||||
&__entry->__platform_state,
|
||||
false /* when laundering, we can't handle notify_one */);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __cxx_atomic_notify_one(void const volatile* __location)
|
||||
{ __libcpp_atomic_notify(__location); }
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __cxx_atomic_notify_all(void const volatile* __location)
|
||||
{ __libcpp_atomic_notify(__location); }
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
__cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location)
|
||||
{
|
||||
auto const __entry = __libcpp_contention_state(__location);
|
||||
return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value)
|
||||
{
|
||||
auto const __entry = __libcpp_contention_state(__location);
|
||||
__libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value);
|
||||
}
|
||||
|
||||
/* When the incoming atomic happens to be the platform wait size, we still need to use the
|
||||
table for the contention detection, but we can use the atomic directly for the wait. */
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location)
|
||||
{
|
||||
__libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location)
|
||||
{
|
||||
__libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
__cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location)
|
||||
{
|
||||
return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value)
|
||||
{
|
||||
__libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value);
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif //_LIBCPP_HAS_NO_THREADS
|
||||
95
libcxx/src/barrier.cpp
Normal file
95
libcxx/src/barrier.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
//===------------------------- barrier.cpp ---------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <__config>
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_THREADS
|
||||
|
||||
#include <barrier>
|
||||
#include <thread>
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_TREE_BARRIER) && (_LIBCPP_STD_VER > 11)
|
||||
|
||||
class __barrier_algorithm_base {
|
||||
public:
|
||||
struct alignas(64) /* naturally-align the heap state */ __state_t
|
||||
{
|
||||
struct {
|
||||
__atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
|
||||
} __tickets[64];
|
||||
};
|
||||
|
||||
ptrdiff_t& __expected;
|
||||
unique_ptr<__state_t[]> __state;
|
||||
|
||||
_LIBCPP_HIDDEN
|
||||
__barrier_algorithm_base(ptrdiff_t& __expected)
|
||||
: __expected(__expected), __state(new __barrier_algorithm_base::__state_t[(__expected + 1) >> 1])
|
||||
{
|
||||
}
|
||||
_LIBCPP_HIDDEN
|
||||
bool __arrive(__barrier_phase_t __old_phase)
|
||||
{
|
||||
__barrier_phase_t const __half_step = __old_phase + 1,
|
||||
__full_step = __old_phase + 2;
|
||||
size_t __current_expected = __expected,
|
||||
__current = hash<thread::id>()(this_thread::get_id()) % ((__expected + 1) >> 1);
|
||||
for(int __round = 0;; ++__round) {
|
||||
if(__current_expected <= 1)
|
||||
return true;
|
||||
size_t const __end_node = ((__current_expected + 1) >> 1),
|
||||
__last_node = __end_node - 1;
|
||||
for(;;++__current) {
|
||||
if(__current == __end_node)
|
||||
__current = 0;
|
||||
__barrier_phase_t expect = __old_phase;
|
||||
if(__current == __last_node && (__current_expected & 1))
|
||||
{
|
||||
if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __full_step, memory_order_acq_rel))
|
||||
break; // I'm 1 in 1, go to next __round
|
||||
}
|
||||
else if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __half_step, memory_order_acq_rel))
|
||||
{
|
||||
return false; // I'm 1 in 2, done with arrival
|
||||
}
|
||||
else if(expect == __half_step)
|
||||
{
|
||||
if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __full_step, memory_order_acq_rel))
|
||||
break; // I'm 2 in 2, go to next __round
|
||||
}
|
||||
}
|
||||
__current_expected = __last_node + 1;
|
||||
__current >>= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
__barrier_algorithm_base * __construct_barrier_algorithm_base(ptrdiff_t& __expected)
|
||||
{
|
||||
return new __barrier_algorithm_base(__expected);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
|
||||
__barrier_phase_t __old_phase)
|
||||
{
|
||||
return __barrier->__arrive(__old_phase);
|
||||
}
|
||||
_LIBCPP_EXPORTED_FROM_ABI
|
||||
void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier)
|
||||
{
|
||||
delete __barrier;
|
||||
}
|
||||
|
||||
#endif //!defined(_LIBCPP_HAS_NO_TREE_BARRIER) && (_LIBCPP_STD_VER >= 11)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif //_LIBCPP_HAS_NO_THREADS
|
||||
@@ -46,6 +46,24 @@
|
||||
#endif
|
||||
#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
|
||||
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
|
||||
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101500
|
||||
#define _LIBCPP_USE_ULOCK
|
||||
#endif
|
||||
#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
|
||||
#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000
|
||||
#define _LIBCPP_USE_ULOCK
|
||||
#endif
|
||||
#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
|
||||
#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 130000
|
||||
#define _LIBCPP_USE_ULOCK
|
||||
#endif
|
||||
#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
|
||||
#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 60000
|
||||
#define _LIBCPP_USE_ULOCK
|
||||
#endif
|
||||
#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
#endif // _LIBCPP_SRC_INCLUDE_APPLE_AVAILABILITY_H
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <array>
|
||||
#ifndef _LIBCPP_HAS_NO_THREADS
|
||||
#include <atomic>
|
||||
#include <latch>
|
||||
#include <barrier>
|
||||
#include <semaphore>
|
||||
#endif
|
||||
#include <bit>
|
||||
#include <bitset>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// XFAIL: c++98, c++03
|
||||
|
||||
// <atomic>
|
||||
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "../atomics.types.operations.req/atomic_helpers.h"
|
||||
|
||||
template <class T>
|
||||
struct TestFn {
|
||||
void operator()() const {
|
||||
typedef std::atomic<T> A;
|
||||
|
||||
A t;
|
||||
std::atomic_init(&t, T(1));
|
||||
assert(std::atomic_load(&t) == T(1));
|
||||
std::atomic_wait(&t, T(0));
|
||||
std::thread t_([&](){
|
||||
std::atomic_store(&t, T(3));
|
||||
std::atomic_notify_one(&t);
|
||||
});
|
||||
std::atomic_wait(&t, T(1));
|
||||
t_.join();
|
||||
|
||||
volatile A vt;
|
||||
std::atomic_init(&vt, T(2));
|
||||
assert(std::atomic_load(&vt) == T(2));
|
||||
std::atomic_wait(&vt, T(1));
|
||||
std::thread t2_([&](){
|
||||
std::atomic_store(&vt, T(4));
|
||||
std::atomic_notify_one(&vt);
|
||||
});
|
||||
std::atomic_wait(&vt, T(2));
|
||||
t2_.join();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
TestEachAtomicType<TestFn>()();
|
||||
|
||||
return 0;
|
||||
}
|
||||
125
libcxx/test/std/atomics/types.pass.cpp
Normal file
125
libcxx/test/std/atomics/types.pass.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <atomic>
|
||||
|
||||
// Test nested types
|
||||
|
||||
// template <class T>
|
||||
// class atomic
|
||||
// {
|
||||
// public:
|
||||
// typedef T value_type;
|
||||
// };
|
||||
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#if TEST_STD_VER >= 20
|
||||
# include <memory>
|
||||
#endif
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
template <class A>
|
||||
void
|
||||
test_atomic()
|
||||
{
|
||||
A a;
|
||||
#if TEST_STD_VER >= 17
|
||||
static_assert((std::is_same<typename A::value_type, decltype(a.load())>::value), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
test()
|
||||
{
|
||||
using A = std::atomic<T>;
|
||||
#if TEST_STD_VER >= 17
|
||||
static_assert((std::is_same<typename A::value_type, T>::value), "");
|
||||
#endif
|
||||
test_atomic<A>();
|
||||
}
|
||||
|
||||
struct TriviallyCopyable {
|
||||
int i_;
|
||||
};
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test<bool> ();
|
||||
test<char> ();
|
||||
test<signed char> ();
|
||||
test<unsigned char> ();
|
||||
test<short> ();
|
||||
test<unsigned short> ();
|
||||
test<int> ();
|
||||
test<unsigned int> ();
|
||||
test<long> ();
|
||||
test<unsigned long> ();
|
||||
test<long long> ();
|
||||
test<unsigned long long> ();
|
||||
test<char16_t> ();
|
||||
test<char32_t> ();
|
||||
test<wchar_t> ();
|
||||
|
||||
test<int_least8_t> ();
|
||||
test<uint_least8_t> ();
|
||||
test<int_least16_t> ();
|
||||
test<uint_least16_t> ();
|
||||
test<int_least32_t> ();
|
||||
test<uint_least32_t> ();
|
||||
test<int_least64_t> ();
|
||||
test<uint_least64_t> ();
|
||||
|
||||
test<int_fast8_t> ();
|
||||
test<uint_fast8_t> ();
|
||||
test<int_fast16_t> ();
|
||||
test<uint_fast16_t> ();
|
||||
test<int_fast32_t> ();
|
||||
test<uint_fast32_t> ();
|
||||
test<int_fast64_t> ();
|
||||
test<uint_fast64_t> ();
|
||||
|
||||
test< int8_t> ();
|
||||
test<uint8_t> ();
|
||||
test< int16_t> ();
|
||||
test<uint16_t> ();
|
||||
test< int32_t> ();
|
||||
test<uint32_t> ();
|
||||
test< int64_t> ();
|
||||
test<uint64_t> ();
|
||||
|
||||
test<intptr_t> ();
|
||||
test<uintptr_t> ();
|
||||
test<size_t> ();
|
||||
test<ptrdiff_t> ();
|
||||
test<intmax_t> ();
|
||||
test<uintmax_t> ();
|
||||
|
||||
test<uintmax_t> ();
|
||||
test<uintmax_t> ();
|
||||
|
||||
test<TriviallyCopyable>();
|
||||
test<std::thread::id>();
|
||||
test<std::chrono::nanoseconds>();
|
||||
test<float>();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
test_atomic<std::atomic_signed_lock_free>();
|
||||
test_atomic<std::atomic_unsigned_lock_free>();
|
||||
/*
|
||||
test<std::shared_ptr<int>>();
|
||||
*/
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
33
libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
Normal file
33
libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <barrier>
|
||||
|
||||
#include <barrier>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::barrier<> b(2);
|
||||
|
||||
auto tok = b.arrive();
|
||||
std::thread t([&](){
|
||||
(void)b.arrive();
|
||||
});
|
||||
b.wait(std::move(tok));
|
||||
t.join();
|
||||
|
||||
auto tok2 = b.arrive(2);
|
||||
b.wait(std::move(tok2));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <barrier>
|
||||
|
||||
#include <barrier>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::barrier<> b(2);
|
||||
|
||||
std::thread t([&](){
|
||||
b.arrive_and_drop();
|
||||
});
|
||||
|
||||
b.arrive_and_wait();
|
||||
b.arrive_and_wait();
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <barrier>
|
||||
|
||||
#include <barrier>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::barrier<> b(2);
|
||||
|
||||
std::thread t([&](){
|
||||
for(int i = 0; i < 10; ++i)
|
||||
b.arrive_and_wait();
|
||||
});
|
||||
for(int i = 0; i < 10; ++i)
|
||||
b.arrive_and_wait();
|
||||
t.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
37
libcxx/test/std/thread/thread.barrier/completion.pass.cpp
Normal file
37
libcxx/test/std/thread/thread.barrier/completion.pass.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <barrier>
|
||||
|
||||
#include <barrier>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
int x = 0;
|
||||
auto comp = [&]() { x += 1; };
|
||||
std::barrier<decltype(comp)> b(2, comp);
|
||||
|
||||
std::thread t([&](){
|
||||
for(int i = 0; i < 10; ++i)
|
||||
b.arrive_and_wait();
|
||||
});
|
||||
|
||||
for(int i = 0; i < 10; ++i)
|
||||
b.arrive_and_wait();
|
||||
|
||||
assert(x == 10);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
26
libcxx/test/std/thread/thread.barrier/max.pass.cpp
Normal file
26
libcxx/test/std/thread/thread.barrier/max.pass.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <barrier>
|
||||
|
||||
#include <barrier>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
static_assert(std::barrier<>::max() > 0, "");
|
||||
auto l = [](){};
|
||||
static_assert(std::barrier<decltype(l)>::max() > 0, "");
|
||||
return 0;
|
||||
}
|
||||
25
libcxx/test/std/thread/thread.barrier/version.pass.cpp
Normal file
25
libcxx/test/std/thread/thread.barrier/version.pass.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <barrier>
|
||||
|
||||
#include <barrier>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
#ifndef _LIBCPP_VERSION
|
||||
#error _LIBCPP_VERSION not defined
|
||||
#endif
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
30
libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
Normal file
30
libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <latch>
|
||||
|
||||
#include <latch>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::latch l(2);
|
||||
|
||||
std::thread t([&](){
|
||||
l.arrive_and_wait();
|
||||
});
|
||||
l.arrive_and_wait();
|
||||
t.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
libcxx/test/std/thread/thread.latch/count_down.pass.cpp
Normal file
31
libcxx/test/std/thread/thread.latch/count_down.pass.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <latch>
|
||||
|
||||
#include <latch>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::latch l(2);
|
||||
|
||||
l.count_down();
|
||||
std::thread t([&](){
|
||||
l.count_down();
|
||||
});
|
||||
l.wait();
|
||||
t.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
23
libcxx/test/std/thread/thread.latch/max.pass.cpp
Normal file
23
libcxx/test/std/thread/thread.latch/max.pass.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <latch>
|
||||
|
||||
#include <latch>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
static_assert(std::latch::max() > 0, "");
|
||||
return 0;
|
||||
}
|
||||
28
libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
Normal file
28
libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <latch>
|
||||
|
||||
#include <latch>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::latch l(1);
|
||||
|
||||
l.count_down();
|
||||
bool const b = l.try_wait();
|
||||
assert(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
libcxx/test/std/thread/thread.latch/version.pass.cpp
Normal file
25
libcxx/test/std/thread/thread.latch/version.pass.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <latch>
|
||||
|
||||
#include <latch>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
#ifndef _LIBCPP_VERSION
|
||||
#error _LIBCPP_VERSION not defined
|
||||
#endif
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
31
libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
Normal file
31
libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::counting_semaphore<> s(2);
|
||||
|
||||
std::thread t([&](){
|
||||
s.acquire();
|
||||
});
|
||||
t.join();
|
||||
|
||||
s.acquire();
|
||||
|
||||
return 0;
|
||||
}
|
||||
38
libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
Normal file
38
libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::binary_semaphore s(1);
|
||||
|
||||
auto l = [&](){
|
||||
for(int i = 0; i < 1024; ++i) {
|
||||
s.acquire();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
s.release();
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t(l);
|
||||
l();
|
||||
|
||||
t.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
libcxx/test/std/thread/thread.semaphore/max.pass.cpp
Normal file
28
libcxx/test/std/thread/thread.semaphore/max.pass.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
static_assert(std::counting_semaphore<>::max() > 0, "");
|
||||
static_assert(std::counting_semaphore<1>::max() >= 1, "");
|
||||
static_assert(std::counting_semaphore<std::numeric_limits<int>::max()>::max() >= 1, "");
|
||||
static_assert(std::counting_semaphore<std::numeric_limits<unsigned>::max()>::max() >= 1, "");
|
||||
static_assert(std::counting_semaphore<std::numeric_limits<ptrdiff_t>::max()>::max() >= 1, "");
|
||||
static_assert(std::counting_semaphore<1>::max() == std::binary_semaphore::max(), "");
|
||||
return 0;
|
||||
}
|
||||
34
libcxx/test/std/thread/thread.semaphore/release.pass.cpp
Normal file
34
libcxx/test/std/thread/thread.semaphore/release.pass.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::counting_semaphore<> s(0);
|
||||
|
||||
s.release();
|
||||
s.acquire();
|
||||
|
||||
std::thread t([&](){
|
||||
s.acquire();
|
||||
});
|
||||
s.release(2);
|
||||
t.join();
|
||||
s.acquire();
|
||||
|
||||
return 0;
|
||||
}
|
||||
44
libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
Normal file
44
libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
|
||||
std::counting_semaphore<> s(0);
|
||||
|
||||
assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
|
||||
assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
|
||||
|
||||
std::thread t([&](){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
s.release();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
s.release();
|
||||
});
|
||||
|
||||
assert(s.try_acquire_until(start + std::chrono::seconds(2)));
|
||||
assert(s.try_acquire_for(std::chrono::seconds(2)));
|
||||
t.join();
|
||||
|
||||
auto const end = std::chrono::steady_clock::now();
|
||||
assert(end - start < std::chrono::seconds(10));
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
Normal file
34
libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
std::counting_semaphore<> s(1);
|
||||
|
||||
assert(s.try_acquire());
|
||||
s.release();
|
||||
assert(s.try_acquire());
|
||||
s.release(2);
|
||||
std::thread t([&](){
|
||||
assert(s.try_acquire());
|
||||
});
|
||||
t.join();
|
||||
assert(s.try_acquire());
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
libcxx/test/std/thread/thread.semaphore/version.pass.cpp
Normal file
25
libcxx/test/std/thread/thread.semaphore/version.pass.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// UNSUPPORTED: libcpp-has-no-threads
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
|
||||
// <semaphore>
|
||||
|
||||
#include <semaphore>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
#ifndef _LIBCPP_VERSION
|
||||
#error _LIBCPP_VERSION not defined
|
||||
#endif
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -169,7 +169,7 @@
|
||||
<tr><td><a href="https://wg21.link/P1004">P1004</a></td><td>LWG</td><td>Making std::vector constexpr</td><td>Cologne</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1035">P1035</a></td><td>LWG</td><td>Input Range Adaptors</td><td>Cologne</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1065">P1065</a></td><td>LWG</td><td>Constexpr INVOKE</td><td>Cologne</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1135">P1135</a></td><td>LWG</td><td>The C++20 Synchronization Library</td><td>Cologne</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1135">P1135</a></td><td>LWG</td><td>The C++20 Synchronization Library</td><td>Cologne</td><td>Complete</td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1207">P1207</a></td><td>LWG</td><td>Movability of Single-pass Iterators</td><td>Cologne</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1208">P1208</a></td><td>LWG</td><td>Adopt source_location for C++20</td><td>Cologne</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P1355">P1355</a></td><td>LWG</td><td>Exposing a narrow contract for ceil2</td><td>Cologne</td><td>Complete</td><td>9.0</td></tr>
|
||||
|
||||
Reference in New Issue
Block a user