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 |
|