-
Notifications
You must be signed in to change notification settings - Fork 17
Open
Description
The following code comes from 7ab32b4
template<typename T>
void MpscQueue<T>::enqueue(const T &input) {
BufferNode *node{new BufferNode(input)}; // #1
BufferNode *prevhead{head_.exchange(node, std::memory_order_acq_rel)}; // #2
prevhead->next_.store(node, std::memory_order_release); // #3
}
template<typename T>
bool MpscQueue<T>::dequeue(T &output) {
BufferNode *tail = tail_.load(std::memory_order_relaxed);
BufferNode *next = tail->next_.load(std::memory_order_acquire);
if (next == nullptr) {
return false;
}
output = std::move(*(next->dataPtr_));
delete next->dataPtr_; // #4
tail_.store(next, std::memory_order_release); // #5
delete tail;
return true;
}
Consider that 4 threads A, B, C, D. Assume that we have the queue with 3 elements and thread A is putting a new element 4
into the queue, in which case it's running in enqueue
. After A executes #2 successfully, the current state of the queue looks like this:
|---------------------- head
1 -> 2 -> 3 --> 4
^
|------------------ prev
Assume that A is switched out by scheduler. Now thread B, C, D are trying to dequeue elements from the queue. They run to #4 and delete 1, 2, 3 respectively.
Note that current state of prev
is freed and no longer valid. However, if A resumes at #3, the prevhead
is actually deleted by other threads and it's UB now.
Metadata
Metadata
Assignees
Labels
No labels