- 
                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
          ^
          |------------------ prevAssume 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