std:: atomic_thread_fence
|
Defined in header
<atomic>
|
||
|
extern
"C"
void
atomic_thread_fence
(
std::
memory_order
order
)
noexcept
;
|
(since C++11) | |
Establishes memory synchronization ordering of non-atomic and relaxed atomic accesses, as instructed by order , without an associated atomic operation. Note however, that at least one atomic operation is required to set up the synchronization, as described below.
Contents |
Fence-atomic synchronization
A release fence
F
in thread
A
synchronizes-with atomic
acquire operation
Y
in thread
B
, if
-
there exists an atomic store
X(with any memory order), -
Yreads the value written byX(or the value would be written by release sequence headed byXifXwere a release operation), -
Fis sequenced-beforeXin threadA.
In this case, all non-atomic and relaxed atomic stores that are
sequenced-before
F
in thread
A
will
happen-before
all non-atomic and relaxed atomic loads from the same locations made in thread
B
after
Y
.
Atomic-fence synchronization
An atomic
release operation
X
in thread
A
synchronizes-with an acquire fence
F
in thread
B
, if
-
there exists an atomic read
Y(with any memory order), -
Yreads the value written byX(or by the release sequence headed by X ), -
Yis sequenced-beforeFin threadB.
In this case, all non-atomic and relaxed atomic stores that are
sequenced-before
X
in thread
A
will
happen-before
all non-atomic and relaxed atomic loads from the same locations made in thread
B
after
F
.
Fence-fence synchronization
A release fence
FA
in thread
A
synchronizes-with an acquire fence
FB
in thread
B
, if
-
there exists an atomic object
M, -
there exists an atomic write
X(with any memory order) that modifiesMin threadA, -
FAis sequenced-beforeXin threadA, -
there exists an atomic read
Y(with any memory order) in threadB, -
Yreads the value written byX(or the value would be written by release sequence headed byXifXwere a release operation), -
Yis sequenced-beforeFBin threadB.
In this case, all non-atomic and relaxed atomic stores that are
sequenced-before
FA
in thread
A
will
happen-before
all non-atomic and relaxed atomic loads from the same locations made in thread
B
after
FB
.
Depending on the value of the order parameter, the effects of this call are:
- When order == std:: memory_order_relaxed , there are no effects.
- When order == std:: memory_order_acquire or order == std:: memory_order_consume , is an acquire fence.
- When order == std:: memory_order_release , is a release fence.
- When order == std:: memory_order_acq_rel , is both a release fence and an acquire fence.
- When order == std:: memory_order_seq_cst , is a sequentially-consistent ordering acquire fence and release fence.
Parameters
| order | - | the memory ordering executed by this fence |
Notes
On x86 (including x86-64),
atomic_thread_fence
functions issue no CPU instructions and only affect compile-time code motion, except for
std
::
atomic_thread_fence
(
std::
memory_order_seq_cst
)
.
atomic_thread_fence
imposes stronger synchronization constraints than an atomic store operation with the same
std::memory_order
. While an atomic store-release operation prevents all preceding reads and writes from moving past the store-release, an
atomic_thread_fence
with
std::
memory_order_release
ordering prevents all preceding reads and writes from moving past all subsequent stores.
Fence-fence synchronization can be used to add synchronization to a sequence of several relaxed atomic operations, for example:
// Global std::string computation(int); void print(std::string); std::atomic<int> arr[3] = {-1, -1, -1}; std::string data[1000]; //non-atomic data // Thread A, compute 3 values. void ThreadA(int v0, int v1, int v2) { // assert(0 <= v0, v1, v2 < 1000); data[v0] = computation(v0); data[v1] = computation(v1); data[v2] = computation(v2); std::atomic_thread_fence(std::memory_order_release); std::atomic_store_explicit(&arr[0], v0, std::memory_order_relaxed); std::atomic_store_explicit(&arr[1], v1, std::memory_order_relaxed); std::atomic_store_explicit(&arr[2], v2, std::memory_order_relaxed); } // Thread B, prints between 0 and 3 values already computed. void ThreadB() { int v0 = std::atomic_load_explicit(&arr[0], std::memory_order_relaxed); int v1 = std::atomic_load_explicit(&arr[1], std::memory_order_relaxed); int v2 = std::atomic_load_explicit(&arr[2], std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); // v0, v1, v2 might turn out to be -1, some or all of them. // Otherwise it is safe to read the non-atomic data because of the fences: if (v0 != -1) print(data[v0]); if (v1 != -1) print(data[v1]); if (v2 != -1) print(data[v2]); }
Example
Scan an array of mailboxes, and process only the ones intended for us, without unnecessary synchronization. This example uses atomic-fence synchronization.
const int num_mailboxes = 32; std::atomic<int> mailbox_receiver[num_mailboxes]; std::string mailbox_data[num_mailboxes]; // The writer threads update non-atomic shared data // and then update mailbox_receiver[i] as follows: mailbox_data[i] = ...; std::atomic_store_explicit(&mailbox_receiver[i], receiver_id, std::memory_order_release); // Reader thread needs to check all mailbox[i], but only needs to sync with one. for (int i = 0; i < num_mailboxes; ++i) if (std::atomic_load_explicit(&mailbox_receiver[i], std::memory_order_relaxed) == my_id) { // synchronize with just one writer std::atomic_thread_fence(std::memory_order_acquire); // guaranteed to observe everything done in the writer thread // before the atomic_store_explicit() do_work(mailbox_data[i]); }
See also
|
(C++11)
|
defines memory ordering constraints for the given atomic operation
(enum) |
|
(C++11)
|
fence between a thread and a signal handler executed in the same thread
(function) |
|
C documentation
for
atomic_thread_fence
|
|