I have two threads, Producer and Consumer. Data exchange is controlled by two pointers inside std::atomics:
std::atomic<TNode*> next = nullptr;
std::atomic<TNode*> waiting = nullptr;
Thread Producer publishs the prepared data and afterwards checks the value of waiting:
TNode* newNext = new TNode( );
// ... fill *newNext ...
next.store( newNext, std::memory_order_release );
TNode* oldWaiting = waiting.load( std::memory_order_seq_cst );
if( oldWaiting == nullptr )
{
/* wake up Consumer */
}
It is crucical that the load on waiting
comes after the store on next
, but std::memory_order_seq_cst
has much stronger guarantees than I really need since I really only need the order of those two accesses fixed. Is it possible to get the memory order I need without the need for memory_order_seq_cst
?
Here is the rest of the picture:
Thread Consumer checks next
. If it finds it empty it sets waiting
to signal Producer before blocking itself.
TNode* newCurrent = next.load( std::memory_order_consume );
if( newCurrent == nullptr )
{
waiting.store( current, std::memory_order_relaxed );
/* wait, blocking, for next != nullptr */
}
current = newCurrent;
The whole thing is a producer-consumer queue that keeps the need for locking low without the need for all that complicated mechanisms. next
is actually inside the current node of a singly linked list. Data typically comes in bursts, so that in most cases Consumer finds a whole bunch of nodes ready for consuming; apart from rare cases, both threads and only go through the locking and blocking/wakeup once between bursts.