Memory Order Consume
§classic acq-rel pairing for inter-thread data sharing
One common pattern in concurrent programming is that a producer prepares some data, sets the signal; then, the consumer sees the signal and reads the data. The code looks like:
1 | [init] |
The recommended memory order to use here is release
for writing the flag
, and acquire
for reading the flag
.
flag
needs to be of atomic type as well, because it’s read/write concurrently. In contrast, data
needs not to be
atomic.
§dependent load
Next we look at a variant of the above scenario, where data
is shared via a pointer.
1 | [init] |
One can continue use acq-rel pair as before. However, the use of dependent-load to retrieve data offers a lighter
synchronization. The only ordering we actually care is that data
is written before data_ptr
is updated. Nonetheless,
using acq-rel means all writes in T0 before release
are visible to T1 after acquire
; an overkill for this scenario.
consume
is meant to resolve this, providing the minimal enough synchronization: only memory reads depending on the
release
can see the writes from T0.
The following is an example to illustrate the difference between acq-rel and rel-consume. x
is read through a pointer
(dependent load), while y
is read directly. Therefore, rel-consume only guarantees read_x
gets the updated value,
but not necessarily read_y
.
Using acquire
: read_x == 1
, read_y == 1
Using consume
: read_x == 1
, read_y == 0 or 1
1 |
|