Has counting_semaphore<X>::acquire()
a barrier so that I won't need to have an atomic_thread_fence( memory_order_acquire )
. The same would be nice for counting_semaphore<X>::relese( n )
, which should include an atomic_thread_fence( memory_order_release )
.
Has counting_semaphore<X>::acquire()
a barrier so that I won't need to have an atomic_thread_fence( memory_order_acquire )
. The same would be nice for counting_semaphore<X>::relese( n )
, which should include an atomic_thread_fence( memory_order_release )
.
1 Answer
Reset to default 5These functions are acquire and release operations. They contain acquire and release barriers in the sense that acquire()
will not be reordered with loads and stores that appear later in program order, and release()
will not be reordered with loads and stores that appear earlier.
So for instance, if you do
int x;
std::counting_semaphore sem{0};
int not_atomic;
// thread 1
not_atomic = 47;
sem.release();
// thread 2
sem.acquire();
cout << non_atomic;
then this program does not have a data race, and it is guaranteed to print 47
.
This is implied by [thread.sema.cnt p10] (N4860), which says about release()
:
Synchronization: Strongly happens before invocations of
try_acquire
that observe the result of the effects.
This also carries over to acquire
and try_acquire_for
, whose operations are defined as if by calling try_acquire
.
However, this is not equivalent to placing a fence. For instance, a release fence would also prevent earlier loads and stores from being reordered with any later stores, not just with the sem.release()
itself. Consider for instance
std::atomic<bool> flag{false};
// thread 1
not_atomic = 47;
std::atomic_thread_fence(std::memory_order_release);
flag.store(true, std::memory_order_relaxed);
// thread 2
if (flag.load(std::memory_order_acquire))
cout << not_atomic;
This program is also free of data races and must print 47. But if you replace it with
// thread 1
not_atomic = 47;
sem.release();
flag.store(true, std::memory_order_relaxed);
// thread 2
if (flag.load(std::memory_order_acquire))
cout << not_atomic;
then you have a data race. The relaxed store of flag
can be reordered above sem.release()
, at which point it can then be reordered above not_atomic = 47
.