1. Introduction
This paper is the unification of the wording for a series of related C++20 proposals for introducing new synchronization and thread coordination facilities and enhancing existing ones:
-
[P0514r4]: Efficient
waiting and semaphores.atomic -
[P0666r2]: Latches and barriers.
-
[P0995r1]:
and lockfree integral types.atomic_flag :: test -
[P1258r0]: Don’t make C++ unimplementable for small CPUs.
1.1. Changelog
Revision 0: Post Rapperswil changes from [P0514r4], [P0666r2], and [P0995r1] based on Rapperswil feedback.
-
Refactored
andbasic_barrier
into one class with a default template parameter as suggested by LEWG at Rapperswil.barrier -
Refactored
andbasic_semaphore
into one class with a default template parameter as suggested by LEWG at Rapperswil.counting_semaphore -
Fixed
parameters in semaphore, latch, and barrier member functions to consistently default to 1 to resolve mistakes identified by LEWG at Rapperswil.update
Revision 1: Pre San Diego 2018 changes based on Rapperswil feedback and a June discussion on the LEWG and SG1 mailing lists.
-
Added member function versions of
andatomic_wait_ *
, for consistency. Refactored wording to accommodate this.atomic_notify_ * -
Renamed the
overloads ofatomic_flag
andatomic_wait
toatomic_wait_explicit
andatomic_flag_wait
for consistency and to leave the door open for future compatibility with C.atomic_flag_wait_explicit -
Renamed
andlatch :: arrive_and_wait
tobarrier :: arrive_and_wait
andlatch :: sync
, because LEWG at Rapperswil expected these methods to be the common use case and prefers they have a short name.barrier :: sync -
Renamed
tolatch :: arrive
to further separate and distinguish thelatch :: count_down
andlatch
interfaces.barrier -
Removed
to resolve concerns raised during LEWG discussion at Rapperswil regarding its "maybe consuming" nature.barrier :: try_wait -
Required that
's move constructor and move assignment operators arebarrier :: arrival_token
to resolve discussions in LEWG at Rapperswil regarding exceptions being thrown when using the split arrive and wait barrier interface.noexcept -
Clarified that
,counting_semaphore :: release
,latch :: count_down
,latch :: sync
, andbarrier :: wait
throw nothing (but cannot bebarrier :: arrive_and_drop
, because they have preconditions) to resolve discussions in LEWG at Rapperswil and on the mailing list.noexcept -
Made
,counting_semaphore :: acquire
, andcounting_semaphore :: try_acquire latch :: wait
, because participants in the mailing list discussion preferred that synchronization operations not throw and that any resource acquisition failures be reported by throwing during construction of synchronization objects.noexcept -
Made
,counting_semaphore
, andlatch
's constructors nonbarrier
and allowed them to throwconstexpr
if the latch cannot be created, because participants in the mailing list discussion preferred that synchronization operations not throw and that any resource acquisition failures be reported by throwing during construction of synchronization objects.system_error
Revision 2: San Diego 2018 changes to incorporate [P1258r0] and pre-meeting feedback.
-
Made
take itsbarrier :: wait
parameter by rvalue reference.arrival_token -
Made the
andatomic_signed_lock_free
types optional for freestanding implementations, as per [P1258r0].atomic_unsigned_lock_free
2. Wording
Note: The following changes are relative to the post-Rapperswil 2018 working draft of ISO/IEC 14882, ([N4762]).
Note: The � character is used to denote a placeholder number which shall be selected by the editor.
Add
,
, and
to Table 18 "C++ library headers" in [headers].
Modify the header synopsis for
in [atomics.syn] as follows:
29.2 Headersynopsis [atomics.syn]
< atomic >
namespace std { // ... // 29.8, non-member functions // ...
template < class T >
void atomic_notify_one ( const volatile atomic < T >* );
template < class T >
void atomic_notify_one ( const atomic < T >* );
void atomic_notify_one ( const volatile atomic_flag * );
void atomic_notify_one ( const atomic_flag * );
template < class T >
void atomic_notify_all ( const volatile atomic < T >* );
template < class T >
void atomic_notify_all ( const atomic < T >* );
void atomic_notify_all ( const volatile atomic_flag * );
void atomic_notify_all ( const atomic_flag * );
template < class T >
void atomic_wait ( const volatile atomic < T >* ,
typename atomic < T >:: value_type );
template < class T >
void atomic_wait ( const atomic < T >* ,
typename atomic < T >:: value_type );
template < class T >
void atomic_wait_explicit ( const volatile atomic < T >* ,
typename atomic < T >:: value_type ,
memory_order );
template < class T >
void atomic_wait_explicit ( const atomic < T >* ,
typename atomic < T >:: value_type ,
memory_order ); // 29.3, type aliases // ... using atomic_intptr_t = atomic < intptr_t > ; using atomic_uintptr_t = atomic < uintptr_t > ; using atomic_size_t = atomic < size_t > ; using atomic_ptrdiff_t = atomic < ptrdiff_t > ; using atomic_intmax_t = atomic < intmax_t > ; using atomic_uintmax_t = atomic < uintmax_t > ;
using atomic_int_fast_wait_t = atomic < implementation - defined > ;
using atomic_uint_fast_wait_t = atomic < implementation - defined > ;
using atomic_signed_lock_free = see below ;
using atomic_unsigned_lock_free = see below ;
// ... // 29.8, flag type and operations struct atomic_flag ; bool atomic_flag_test ( volatile atomic_flag * ) noexcept ; bool atomic_flag_test ( atomic_flag * ) noexcept ; bool atomic_flag_test_explicit ( volatile atomic_flag * , memory_order ) noexcept ; bool atomic_flag_test_explicit ( atomic_flag * , memory_order ) noexcept ;
bool atomic_flag_test_and_set ( volatile atomic_flag * ) noexcept ; bool atomic_flag_test_and_set ( atomic_flag * ) noexcept ; bool atomic_flag_test_and_set_explicit ( volatile atomic_flag * , memory_order ) noexcept ; bool atomic_flag_test_and_set_explicit ( atomic_flag * , memory_order ) noexcept ; void atomic_flag_clear ( volatile atomic_flag * ) noexcept ; void atomic_flag_clear ( atomic_flag * ) noexcept ; void atomic_flag_clear_explicit ( volatile atomic_flag * , memory_order ) noexcept ; void atomic_flag_clear_explicit ( atomic_flag * , memory_order ) noexcept ;
void atomic_flag_wait ( const volatile atomic_flag * , bool ) noexcept ;
void atomic_flag_wait ( const atomic_flag * , bool ) noexcept ;
void atomic_flag_wait_explicit ( const volatile atomic_flag * , bool , memory_order ) noexcept ;
void atomic_flag_wait_explicit ( const atomic_flag * , bool , memory_order ) noexcept ;
void atomic_flag_notify_one ( volatile atomic_flag * ) noexcept ;
void atomic_flag_notify_one ( atomic_flag * ) noexcept ;
void atomic_flag_notify_all ( volatile atomic_flag * ) const noexcept ;
void atomic_flag_notify_all ( atomic_flag * ) const noexcept ; #
define ATOMIC_FLAG_INIT see below // 29.9, fences extern "C" void atomic_thread_fence ( memory_order ) noexcept ; extern "C" void atomic_signal_fence ( memory_order ) noexcept ; }
Modify [atomics.alias] as follows:
29.3 Type aliases [atomics.alias]The type aliases,
atomic_intN_t ,
atomic_uintN_t , and
atomic_intptr_t are defined if and only if
atomic_uintptr_t ,
intN_t ,
uintN_t , and
intptr_t are defined, respectively.
uintptr_t The type aliasesand
atomic_signed_lock_free are defined to be specializations of
atomic_unsigned_lock_free whose template arguments are integral types, respectively signed and unsigned, other than
atomic . In freestanding implementations (4.1), these aliases are optional. If an implementation provides a integral specialization of
bool other than
atomic for which
bool is true, it shall define
is_always_lock_free and
atomic_signed_lock_free . Otherwise, they shall not be defined.
atomic_unsigned_lock_free shall be
is_always_lock_free true
forand
atomic_signed_lock_free . An implementation which defines these type aliases should choose the integral specialization of
atomic_unsigned_lock_free for which the atomic waiting and notifying operations are most efficient.
atomic The type aliasesand
atomic_int_fast_wait_t are integral atomic types. Implementations should ensure that invocations of atomic waiting and notifying operations (29.�) with these types have the lowest performance overhead among integer types.
atomic_uint_fast_wait_t
Note: The reference to "atomic waiting and notifying operations" in the above change should refer to the new [atomic.wait] subclause.
Add a new subclause after [atomics.lockfree]:
29.� Waiting and notifying [atomics.wait]Atomic waiting and notifying operations provide a mechanism to wait for the value of an atomic object to change more efficiently than can be achieved with polling.The following functions are atomic waiting operations:
.
atomic < T >:: wait
.
atomic_flag :: wait
and
atomic_wait .
atomic_wait_explicit The following functions are atomic notifying operations:
and
atomic < T >:: notify_one .
atomic < T >:: notify_all
and
atomic_flag :: notify_one .
atomic_flag :: notify_all
and
atomic_notify_one .
atomic_notify_one_explicit
and
atomic_flag_notify_one .
atomic_flag_notify_one_explicit
and
atomic_notify_all .
atomic_notify_all_explicit
and
atomic_flag_notify_all .
atomic_flag_notify_all_explicit Atomic waiting operations in this facility may block until they are unblocked by atomic notifying operations, according to each function’s effects. [ Note: Programs are not guaranteed to observe transient atomic values, an issue known as the A-B-A problem, resulting in continued blocking if a condition is only temporarily met. – end note ]
Modify [atomics.types.generic] as follows:
29.7 Class template[atomics.type.generic]
atomic
namespace std { template < class T > struct atomic { using value_type = T ; static constexpr bool is_always_lock_free = implementation - defined ; bool is_lock_free () const volatile noexcept ; bool is_lock_free () const noexcept ; void store ( T , memory_order = memory_order :: seq_cst ) volatile noexcept ; void store ( T , memory_order = memory_order :: seq_cst ) noexcept ; T load ( memory_order = memory_order :: seq_cst ) const volatile noexcept ; T load ( memory_order = memory_order :: seq_cst ) const noexcept ; operator T () const volatile noexcept ; operator T () const noexcept ; T exchange ( T , memory_order = memory_order :: seq_cst ) volatile noexcept ; T exchange ( T , memory_order = memory_order :: seq_cst ) noexcept ; bool compare_exchange_weak ( T & , T , memory_order , memory_order ) volatile noexcept ; bool compare_exchange_weak ( T & , T , memory_order , memory_order ) noexcept ; bool compare_exchange_strong ( T & , T , memory_order , memory_order ) volatile noexcept ; bool compare_exchange_strong ( T & , T , memory_order , memory_order ) noexcept ; bool compare_exchange_weak ( T & , T , memory_order = memory_order :: seq_cst ) volatile noexcept ; bool compare_exchange_weak ( T & , T , memory_order = memory_order :: seq_cst ) noexcept ; bool compare_exchange_strong ( T & , T , memory_order = memory_order :: seq_cst ) volatile noexcept ; bool compare_exchange_strong ( T & , T , memory_order = memory_order :: seq_cst ) noexcept ;
void wait ( T old , memory_order = memory_order :: seq_cst ) const volatile noexcept ;
void wait ( T old , memory_order = memory_order :: seq_cst ) const noexcept ;
void notify_one () const volatile noexcept ;
void notify_one () const noexcept ;
void notify_all () const volatile noexcept ;
void notify_all () const noexcept ;
atomic () noexcept = default ; constexpr atomic ( T ) noexcept ; atomic ( const atomic & ) = delete ; atomic & operator = ( const atomic & ) = delete ; atomic & operator = ( const atomic & ) volatile = delete ; T operator = ( T ) volatile noexcept ; T operator = ( T ) noexcept ; }; }
Add the following to the end of [atomics.types.operations]:
void wait ( T old , memory_order order = memory_order :: seq_cst ) const volatile noexcept ; void wait ( T old , memory_order order = memory_order :: seq_cst ) const noexcept ; Requires: Theargument shall not be
order nor
memory_order_release .
memory_order_acq_rel Effects: Repeatedly performs the following steps, in order:
Evaluates
then, if the result is
object -> load ( order ) != old true
, returns.Blocks until an implementation-defined condition has been met. [ Note: Consequently, it may unblock for reasons other than an atomic notifying operation. — end note ]
Remarks: This function is an atomic waiting operation.void notify_one () const volatile noexcept ; void notify_one () const noexcept ; Effects: Unblocks up to execution of an atomic waiting operation that blocked after observing the result of an atomic operation, if there exists another atomic operation
X , such that
Y precedes
X in the modification order of
Y , and
* this happens before this call.
Y Remarks: This function is an atomic notifying operation.void notify_all () const volatile noexcept ; void notify_all () const noexcept ; Effects: Unblocks each execution of an atomic waiting operation that blocked after observing the result of an atomic operation, if there exists another atomic operation
X , such that
Y precedes
X in the modification order of
Y , and
* this happens before this call.
Y Remarks: This function is an atomic notifying operation.
Modify [atomics.flag] as follows:
29.8 Flag type and operations [atomics.flag]namespace std { struct atomic_flag { bool test ( memory_order = memory_order_seq_cst ) volatile noexcept ; bool test ( memory_order = memory_order_seq_cst ) noexcept ;
bool test_and_set ( memory_order = memory_order_seq_cst ) volatile noexcept ; bool test_and_set ( memory_order = memory_order_seq_cst ) noexcept ; void clear ( memory_order = memory_order_seq_cst ) volatile noexcept ; void clear ( memory_order = memory_order_seq_cst ) noexcept ;
void wait ( bool , memory_order = memory_order :: seq_cst ) const volatile noexcept ;
void wait ( bool , memory_order = memory_order :: seq_cst ) const noexcept ;
void notify_one () const volatile noexcept ;
void notify_one () const noexcept ;
void notify_all () const volatile noexcept ;
void notify_all () const noexcept ;
atomic_flag () noexcept = default ; atomic_flag ( const atomic_flag & ) = delete ; atomic_flag & operator = ( const atomic_flag & ) = delete ; atomic_flag & operator = ( const atomic_flag & ) volatile = delete ; }; bool atomic_flag_test ( volatile atomic_flag * ) noexcept ; bool atomic_flag_test ( atomic_flag * ) noexcept ; bool atomic_flag_test_explicit ( volatile atomic_flag * , memory_order ) noexcept ; bool atomic_flag_test_explicit ( atomic_flag * , memory_order ) noexcept ;
bool atomic_flag_test_and_set ( volatile atomic_flag * ) noexcept ; bool atomic_flag_test_and_set ( atomic_flag * ) noexcept ; bool atomic_flag_test_and_set_explicit ( volatile atomic_flag * , memory_order ) noexcept ; bool atomic_flag_test_and_set_explicit ( atomic_flag * , memory_order ) noexcept ; void atomic_flag_clear ( volatile atomic_flag * ) noexcept ; void atomic_flag_clear ( atomic_flag * ) noexcept ; void atomic_flag_clear_explicit ( volatile atomic_flag * , memory_order ) noexcept ; void atomic_flag_clear_explicit ( atomic_flag * , memory_order ) noexcept ; void atomic_flag_wait ( const volatile atomic_flag * , bool ) noexcept ; void atomic_flag_wait ( const atomic_flag * , bool ) noexcept ; void atomic_flag_wait_explicit ( const volatile atomic_flag * , bool , memory_order ) noexcept ; void atomic_flag_wait_explicit ( const atomic_flag * , bool , memory_order ) noexcept ; void atomic_flag_notify_one ( volatile atomic_flag * ) noexcept ; void atomic_flag_notify_one ( atomic_flag * ) noexcept ; void atomic_flag_notify_all ( volatile atomic_flag * ) const noexcept ; void atomic_flag_notify_all ( atomic_flag * ) const noexcept ; #
define ATOMIC_FLAG_INIT see below } Thetype provides the classic test-and-set functionality. It has two states, set and clear.
atomic_flag Operations on an object of typeshall be lock-free. [ Note: Hence the operations should also be address-free. — end note ]
atomic_flag Thetype is a standard-layout struct. It has a trivial default constructor and a trivial destructor.
atomic_flag The macroshall be defined in such a way that it can be used to initialize an object of type
ATOMIC_FLAG_INIT to the clear state. The macro can be used in the form:
atomic_flag atomic_flag guard = ATOMIC_FLAG_INIT ; It is unspecified whether the macro can be used in other initialization contexts. For a complete static-duration object, that initialization shall be static. Unless initialized with
, it is unspecified whether an
ATOMIC_FLAG_INIT object has an initial state of set or clear.
atomic_flag bool atomic_flag_test ( volatile atomic_flag * object ) noexcept ; bool atomic_flag_test ( atomic_flag * object ) noexcept ; bool atomic_flag_test_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ; bool atomic_flag_test_explicit ( atomic_flag * object , memory_order order ) noexcept ; bool atomic_flag :: test ( memory_order order = memory_order_seq_cst ) volatile noexcept ; bool atomic_flag :: test ( memory_order order = memory_order_seq_cst ) noexcept ; Requires: Theargument shall not be
order nor
memory_order_release .
memory_order_acq_rel Effects: Memory is affected according to the value of.
order Returns: Atomically returns the value pointed to byor
object .
this bool atomic_flag_test_and_set ( volatile atomic_flag * object ) noexcept ; bool atomic_flag_test_and_set ( atomic_flag * object ) noexcept ; bool atomic_flag_test_and_set_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ; bool atomic_flag_test_and_set_explicit ( atomic_flag * object , memory_order order ) noexcept ; bool atomic_flag :: test_and_set ( memory_order order = memory_order_seq_cst ) volatile noexcept ; bool atomic_flag :: test_and_set ( memory_order order = memory_order_seq_cst ) noexcept ; Effects: Atomically sets the value pointed to byor by
object to
this true
. Memory is affected according to the value of. These operations are atomic read-modify-write operations (4.7).
order Returns: Atomically, the value of the object immediately before the effects.void atomic_flag_clear ( volatile atomic_flag * object ) noexcept ; void atomic_flag_clear ( atomic_flag * object ) noexcept ; void atomic_flag_clear_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ; void atomic_flag_clear_explicit ( atomic_flag * object , memory_order order ) noexcept ; void atomic_flag :: clear ( memory_order order = memory_order_seq_cst ) volatile noexcept ; void atomic_flag :: clear ( memory_order order = memory_order_seq_cst ) noexcept ; Requires: The
argument shall not be
order ,
memory_order_consume , nor
memory_order_acquire .
memory_order_acq_rel Effects: Atomically sets the value pointed to by
or by
object to
this false
. Memory is affected according to the value of.
order void atomic_flag_wait ( const volatile atomic_flag * object , bool old ) noexcept ; void atomic_flag_wait ( const atomic_flag * object , bool old ) noexcept ; void atomic_flag_wait_explicit ( const volatile atomic_flag * object , bool old , memory_order order ) noexcept ; void atomic_flag_wait_explicit ( const atomic_flag * object , bool old , memory_order order ) noexcept ; void atomic_flag :: wait ( bool old , memory_order order = memory_order :: seq_cst ) const volatile noexcept ; void atomic_flag :: wait ( bool old , memory_order order = memory_order :: seq_cst ) const noexcept ; Requires: Theargument shall not be
order nor
memory_order_release .
memory_order_acq_rel Effects: Repeatedly performs the following steps, in order:
Evaluates
then, if the result is
object -> load ( order ) != old true
, returns.Blocks until an implementation-defined condition has been met. [ Note: Consequently, it may unblock for reasons other than an atomic notifying operation. — end note ]
Remarks: This function is an atomic waiting operation.void atomic_flag_notify_one ( volatile atomic_flag * object ) noexcept ; void atomic_flag_notify_one ( atomic_flag * object ) noexcept ; void atomic_flag :: notify_one () const volatile noexcept ; void atomic_flag :: notify_one () const noexcept ; Effects: Unblocks up to one execution of a atomic waiting operation that blocked after observing the result of an atomic operation, if there exists another atomic operation
X , such that
Y precedes
X in the modification order of
Y or
* object , and
* this happens before this call.
Y Remarks: This function is an atomic notifying operation.void atomic_flag_notify_all ( volatile atomic_flag * object ) const noexcept ; void atomic_flag_notify_all ( atomic_flag * object ) const noexcept ; void atomic_flag :: notify_all () const volatile noexcept ; void atomic_flag :: notify_all () const noexcept ; Effects: Unblocks each execution of a atomic waiting operation that blocked after observing the result of an atomic operation, if there exists another atomic operation
X , such that
Y precedes
X in the modification order of
Y or
* object , and
* this happens before this call.
Y Remarks: This function is an atomic notifying operation.
Modify Table 135 "Thread support library summary" in [thread.general] as follows:
Table 135 — Thread support library summary
Subclause Header(s) 30.2 Requirements 30.3 Threads
< thread > 30.4 Mutual exclusion
< mutex >
< shared_mutex > 30.5 Condition variables
< condition_variable > 30.� Semaphores
< semaphore > 30.� Latches and barriers
< latch >
< barrier > 30.6 Futures
< future >
Add two new subclauses after [thread.condition]:
30.� Semaphores [thread.semaphore]Semaphores are lightweight synchronization primitives used to constrain concurrent access to a shared resource. They are widely used to implement other synchronization primitives and, whenever both are applicable, can be more efficient than condition variables.A counting semaphore is a semaphore object that models a non-negative resource count. A binary semaphore is a semaphore object that has only two states, also known as available and unavailable. [ Note: A binary semaphore should be more efficient than a counting semaphore with a unit magnitude count. – end note ]
30.�.1 Headersynopsis [thread.semaphore.syn]
< semaphore >
namespace std { template < ptrdiff_t least_max_value = implementation - defined > class counting_semaphore ; using binary_semaphore = counting_semaphore < 1 > ; }
30.�.2 Class template[thread.semaphore.counting.class]
counting_semaphore
namespace std { template < ptrdiff_t least_max_value > class counting_semaphore { public : static constexpr ptrdiff_t max () noexcept ; explicit counting_semaphore ( ptrdiff_t ); ~ counting_semaphore (); counting_semaphore ( const basic_semaphore & ) = delete ; counting_semaphore ( basic_semaphore && ) = delete ; counting_semaphore & operator = ( const basic_semaphore & ) = delete ; counting_semaphore & operator = ( basic_semaphore && ) = delete ; void release ( ptrdiff_t update = 1 ); void acquire () noexcept ; bool try_acquire () noexcept ; template < class Clock , class Duration > bool try_acquire_until ( const chrono :: time_point < Clock , Duration >& ); template < class Rep , class Period > bool try_acquire_for ( const chrono :: duration < Rep , Period >& ); private : ptrdiff_t counter ; // exposition only }; } Classmaintains an internal counter that is initialized when the semaphore is created. Threads may block waiting until
counting_semaphore .
counter >= 1 Semaphores permit concurrent invocation of the,
release ,
acquire ,
try_acquire , and
try_acquire_for member functions.
try_acquire_until static constexpr ptrdiff_t max () noexcept ; Returns: The maximum value of. This value shall not be less than that of the template argument
counter . [ Note: The value may exceed least_max_value. – end note ]
least_max_value explicit counting_semaphore ( ptrdiff_t desired ); Requires:and
desired >= 0 .
desired <= max () Effects:.
counter = desired Throws:if unable to create the semaphore.
system_error ~ counting_semaphore (); Requires: For every function call that blocks on, a function call that will cause it to unblock and return shall happen before this call. [ Note: This relaxes the usual rules, which would have required all wait calls to happen before destruction. — end note ]
counter Effects: Destroys the object.Throws: Nothing.void release ( ptrdiff_t update = 1 ); Requires:, and
update >= 0 .
counter + update <= max () Effects:, executed atomically. If any threads are blocked on counter, unblocks them.
counter += update Throws: Nothing.Synchronization: Strongly happens before invocations ofthat observe the result of the effects.
try_acquire bool try_acquire () noexcept ; Effects:
With low probability, returns immediately. [ Note: An implementation should ensure that
does not consistently return
try_acquire false
in the absence of contending acquisitions. — end note ]Otherwise, if
, then
counter >= 1 is executed atomically.
counter -= 1 Returns:true
ifwas decremented, otherwise
counter false
.void acquire () noexcept ; Effects: Repeatedly performs the following steps, in order:
Evaluates
, then, if the result is
try_acquire true
, returns.Blocks until
.
counter >= 1 template < class Clock , class Duration > bool try_acquire_until ( const chrono :: time_point < Clock , Duration >& abs_time ); template < class Rep , class Period > bool try_acquire_for ( const chrono :: duration < Rep , Period >& rel_time ); Effects: Repeatedly performs the following steps, in order:
Evaluates
. If the result is
try_acquire true
, returnstrue
.Blocks until the timeout expires or
. If the timeout expired, returns
counter >= 1 false
.Throws: Timeout-related exceptions (30.2.4).
30.� Coordination Types [thread.coord]This section describes various concepts related to thread coordination, and defines the coordination typesand
latch . These types facilitate concurrent computation performed by a number of threads, in one or more phases.
barrier In this subclause, a synchronization point represents a condition that a thread may contribute to or wait for, potentially blocking until it is satisfied. A thread arrives at the synchronization point when it has an effect on the state of the condition, even if it does not cause it to become satisfied.Concurrent invocations of the member functions of coordination types, other than their destructors, do not introduce data races.
30.�.1 Latches [thread.coord.latch]A latch is a thread coordination mechanism that allows any number of threads to block until an expected count is summed (exactly) by threads that arrived at the latch. The expected count is set when the latch is constructed. An individual latch is a single-use object; once the count has been reached, the latch cannot be reused.
30.�.1.1 Headersynopsis [thread.coord.latch.syn]
< latch > namespace std { class latch ; }
30.�.1.2 Class[thread.coord.latch.class]
latch
namespace std { class latch { public : explicit latch ( ptrdiff_t expected ); ~ latch (); latch ( const latch & ) = delete ; latch ( latch && ) = delete ; latch & operator = ( const latch & ) = delete ; latch & operator = ( latch && ) = delete ; void count_down ( ptrdiff_t update = 1 ); bool try_wait () const noexcept ; void wait () const noexcept ; void sync ( ptrdiff_t update = 1 ); private : ptrdiff_t counter ; // exposition only }; } Amaintains an internal counter that is initialized when the
latch is created. Threads may block at the
latch ’s synchronization point, waiting for
latch to be decremented to
counter .
0 explicit latch ( ptrdiff_t expected ); Requires:.
expected >= 0 Effects:.
counter = expected Throws:if unable to create the latch.
system_error ~ latch (); Requires: No threads are blocked at the synchronization point.Effects: Destroys the latch.Throws: Nothing.Remarks: May be called even if some threads have not yet returned from functions that block at the synchronization point, provided that they are unblocked. [ Note: The destructor may block until all threads have exited invocations ofon this object. — end note ]
wait void count_down ( ptrdiff_t update = 1 ); Requires:and
counter >= update .
update >= 0 Effects: Atomically decrementsby
counter .
update Throws: Nothing.Synchronization: Synchronizes with the returns from all calls unblocked by the effects.Remarks: Arrives at the synchronization point withcount.
update bool try_wait () const noexcept ; Returns:.
counter == 0 void wait () const noexcept ; Effects: If, returns immediately. Otherwise, blocks the calling thread at the synchronization point until
counter == 0 .
counter == 0 void sync ( ptrdiff_t update = 1 ); Effects: Equivalent to.
count_down ( update ); wait (); Throws: Nothing.
30.�.2 Barriers [thread.coord.barrier]A barrier is a thread coordination mechanism that allows at most an expected count of threads to block until that count is summed (exactly) by threads that arrived at the barrier in each of its successive phases. Once threads are released from blocking at the synchronization point for a phase, they can reuse the same barrier immediately in its next phase. [ Note: It is thus useful for managing repeated tasks, or phases of a larger task, that are handled by multiple threads. — end note ]A barrier has a completion step that is a (possibly empty) set of effects associated with a phase of the barrier. When the member functions defined in this subclause arrive at the barrier, they have the following effects:
When the expected number of threads for this phase have arrived at the barrier, one of those threads executes the barrier type’s completion step.
When the completion step is completed, all threads blocked at the synchronization point for this phase are unblocked and the barrier enters its next phase. The end of the completion step strongly happens before the returns from all calls unblocked by its completion.
30.�.2.1 Headersynopsis [thread.coord.barrier.syn]
< barrier >
namespace std { template < class CompletionFunction = implementation - defined > class barrier ; }
30.�.2.2 Class template[thread.coord.barrier.class]
barrier
namespace std { template < class CompletionFunction > class barrier { public : using arrival_token = implementation - defined ; explicit barrier ( ptrdiff_t expected , CompletionFunction f = CompletionFunction ()); ~ barrier (); barrier ( const barrier & ) = delete ; barrier ( barrier && ) = delete ; barrier & operator = ( const barrier & ) = delete ; barrier & operator = ( barrier && ) = delete ; [[ nodiscard ]] arrival_token arrive ( ptrdiff_t update = 1 ); void wait ( arrival_token && arrival ) const ; void sync (); void arrive_and_drop (); private : CompletionFunction completion ; // exposition only }; } Ais a barrier type with a completion step controlled by a function object. The completion step calls
barrier . Threads may block at the barrier’s synchronization point for a phase, waiting for the expected sum contributions by threads that arrive in that phase.
completion shall be
CompletionFunction ,
CopyConstructible shall be
is_invocable_r_v < void , CompletionFunction > true
, andshall be
noexcept ( declval < CompletionFunction > ()()) true
.is an implementation-defined type.
barrier :: arrival_token shall be
is_nothrow_move_constructible_v < barrier :: arrival_token > true
andshall be
is_nothrow_move_assignable_v < barrier :: arrival_token > true
.explicit barrier ( ptrdiff_t expected , CompletionFunction f ); Requires:, and
expected >= 0 shall be
noexcept ( f ()) true
.Effects: Initializes the barrier fornumber of threads in the first phase, and initializes
expected with
completion . [ Note: If
move ( f ) is
expected this object may only be destroyed. — end note ]
0 Throws:if unable to create the barrier.
system_error ~ barrier (); Requires: No threads are blocked at a synchronization point for any phase.Effects: Destroys the barrier.Throws: Nothing.Remarks: May be called even if some threads have not yet returned from functions that block at a synchronization point, provided that they have unblocked. [ Note: The destructor may block until all threads have exited invocations of wait() on this object. — end note ][[ nodiscard ]] arrival_token arrive ( ptrdiff_t update = 1 ); Requires: The expected count is not less than.
update Effects: Constructs an object of typethat is associated with the
arrival_token 's synchronization point for the current phase, then arrives
barrier times at the synchronization point for the current phase.
update Synchronization: The call tostrongly happens before the start of the completion step for the current phase.
arrive Returns: The constructed object.Remarks: This may cause the completion step to start.void wait ( arrival_token && arrival ) const ; Requires:is associated with a synchronization point for the current or the immediately preceding phases of the
arrival .
barrier Effects: Blocks at the synchronization point associated withuntil the condition is satisfied.
std :: move ( arrival ) Throws: Nothing.void sync (); Effects: Equivalent to.
wait ( arrive ()) Throws: Nothing.void arrive_and_drop (); Requires: The expected number of threads for the current phase is not.
0 Effects: Decrements the expected number of threads for subsequent phases by, then arrives at the synchronization point for the current phase.
1 Throws: Nothing.Synchronization: The call tostrongly happens before the start of the completion step for the current phase.
arrive_and_drop Remarks: This may cause the completion step to start.
Create the following feature test macros:
-
, which implies that__cpp_lib_atomic_lock_free_type_aliases
andatomic_signed_lock_free
types are available.atomic_unsigned_lock_free -
, which implies the__cpp_lib_atomic_flag_test
methods and free functions fortest
are available.atomic_flag -
, which implies the__cpp_lib_atomic_wait
andnotify_ *
methods and free functions forwait
andatomic
and theatomic_flag
andatomic_int_fast_wait_t
types are available.atomic_uint_fast_wait_t -
, which implies that__cpp_lib_semaphore
andcounting_semaphore
are available.binary_semaphore -
, which implies that__cpp_lib_latch
is available.latch -
, which implies that__cpp_lib_barrier
is available.barrier