A release sequence begins with an atomic release operation and continues through read-modify-write operations, each reading the value written by the preceding operation. It can carry synchronization from the initial operation to a later load. section 5.3.4
The synchronization guarantee depends on the memory ordering:
- The initial store uses memory_order_release, memory_order_acq_rel, or memory_order_seq_cst; read-modify-write operations in the sequence may use any ordering, including relaxed. section 5.3.4
- The final load uses memory_order_acquire or memory_order_seq_cst to synchronize with the initial store; memory_order_consume makes it dependency-ordered-before the load. section 5.3.4
A shared queue count
A producer fills a shared buffer and stores the item count with release ordering. Consumers claim items using acquire fetch_sub operations. Even when a later consumer reads a value written by an earlier fetch_sub rather than by the producer’s store, the release sequence carries the synchronization needed before reading the buffer. section 5.3.4
Listing 5.11. Reading values from a queue with atomic operations
#include <atomic>
#include <thread>
std::vector<int> queue_data;
std::atomic<int> count;
void populate_queue()
{
unsigned const number_of_items=20;
queue_data.clear();
for(unsigned i=0;i<number_of_items;++i)
{
queue_data.push_back(i);
}
count.store(number_of_items,std::memory_order_release); #1
}
void consume_queue_items()
{
while(true)
{
int item_index;
if((item_index=count.fetch_sub(1,std::memory_order_acquire))<=0) #2
{
wait_for_more_items(); #3
continue;
}
process(queue_data[item_index-1]); #4
}
}
int main()
{
std::thread a(populate_queue);
std::thread b(consume_queue_items);
std::thread c(consume_queue_items);
a.join();
b.join();
c.join();
}
The listing shows the producer publishing 20 queued items and two consumers claiming them with fetch_sub; consumers wait when the count indicates no items remain. section 5.3.4
Release sequences also support synchronization in a lock-free stack: read-modify-write compare-and-exchange operations on the head can carry synchronization from a release compare-and-exchange in push to an acquire operation that reads its value. section 7.2.5
