492 31.Producer‐ConsumerQueues
ically swapped. Unfortunately, due to the absence of a hardware CASn instruc-
tion, this limits an item size to 32 bits (further limited to 20 bits since we share
the same index value in the stack algorithm). Therefore, the algorithm is extend-
ed to use a circular queue of index elements that are offsets to the specific item in
an array. When a producer wants to insert an item, the index is atomically re-
trieved from the free list, and it is not inserted into the queue until the data is fin-
ished being copied into the array. Finally, the index can’t be reused until a
consumer retrieves the index from the queue, copies the data locally, and returns
the index back to the free list.
/* Lock-free Array-based Producer-Consumer (FIFO) Queue
Producer: Inserting an item to the tail of the queue.
- The allocator atomically retrieves a free index from the free pool.
- This index points to an unused entry in the item array.
- The item is copied into the array at the index.
- The index is inserted atomically at the queue's tail and is now visible.
- The index will remain unavailable until consumer atomically removes it.
- When that happens, the index will be placed back on the free pool.
Consumer: Removing an item from the head of the queue.
- The index is atomically removed from the head of the queue.
- If successfully removed, the head is atomically incremented.
- The item is copied from the array to a local copy.
- The index is placed back on the free pool and is now available for reuse.
*/
template <typename T> class Queue
{
public:
Queue(UINT maxItemsInQueue) : m_maxQueueSize(maxItemsInQueue),
m_allocator(maxItemsInQueue),
m_head(0), m_tail(0)
{
// Each value references a unique array element.
m_pQueue = new AllocRef[maxItemsInQueue];
assert(m_pQueue != NULL);