I\'m playing with the std::atomic structures and wrote this lock-free multi-producer multi-consumer queue, which I\'m attaching here. The idea for the queue is based on two
I believe I was able to crack this one. No livelock at 1000000 writes/reads for queues from size 2 to 1024 and from 1 producer and 1 consumer to 100 producers / 100 consumers.
Here's the solution. The trick is not to use cell->m_next directly in the compare and swap (the same applies for the producer code by the way) and to require strict memory order rules:
This seems to confirm my suspicion that it was compiler reordering of the reads writes. Here's the code:
bool push(const TData& data)
{
CellNode* cell = m_produceHead.load(std::memory_order_acquire);
if(cell == NULL)
return false;
while(!std::atomic_compare_exchange_strong_explicit(&m_produceHead,
&cell,
cell->m_next,
std::memory_order_acquire,
std::memory_order_release))
{
if(!cell)
return false;
}
m_data[cell->m_idx] = data;
CellNode* curHead = m_consumeHead;
cell->m_next = curHead;
while (!std::atomic_compare_exchange_strong_explicit(&m_consumeHead,
&curHead,
cell,
std::memory_order_acquire,
std::memory_order_release))
{
cell->m_next = curHead;
}
return true;
}
bool pop(TData& data)
{
CellNode* cell = m_consumeHead.load(std::memory_order_acquire);
if(cell == NULL)
return false;
while(!std::atomic_compare_exchange_strong_explicit(&m_consumeHead,
&cell,
cell->m_next,
std::memory_order_acquire,
std::memory_order_release))
{
if(!cell)
return false;
}
data = m_data[cell->m_idx];
CellNode* curHead = m_produceHead;
cell->m_next = curHead;
while(!std::atomic_compare_exchange_strong_explicit(&m_produceHead,
&curHead,
cell,
std::memory_order_acquire,
std::memory_order_release))
{
cell->m_next = curHead;
}
return true;
};