I’m working on a single-producer, multi-consumer lock-free queue implementation in C++. The code is based on examples from the book Concurrency in Action by Anthony Williams. However, I have encountered an issue with compare_exchange_strong that I can’t quite explain. Even with only one push thread, compare_exchange_strong occasionally fails in a way that suggests it was interrupted or otherwise preempted. Here is a simplified version of my code:
void push(T new_value) {
std::unique_ptr<T> new_data(new T(new_value));
counted_node_ptr new_next;
new_next.ptr = new node;
new_next.external_count = 1;
// Create a new tail node
counted_node_ptr old_tail = tail.load();
for (;;) {
increase_external_count(tail, old_tail);
T* old_data = nullptr;
// Attempt to set the data of the current tail node
while (!old_tail.ptr->data.compare_exchange_weak(
old_data, new_data.get()) && old_data == nullptr);
if (old_data == nullptr) {
// Successfully replaced the old tail's data with new data
counted_node_ptr old_next = { 0, nullptr };
// Attempt to update the next pointer of the current tail node
while (!old_tail.ptr->next.compare_exchange_weak(
old_next, new_next) && (old_next.external_count == 0 && old_next.ptr == nullptr));
if (!(old_next.external_count == 0 && old_next.ptr == nullptr)) {
// Update the next pointer of the current tail node failed unexpectedly
assert(0);
delete new_next.ptr;
new_next.ptr = nullptr;
new_next = old_next;
}
// Set the new tail node
set_new_tail(old_tail, new_next);
new_data.release();
break;
} else {
// Another thread has already inserted data
// Retry with updated tail
}
}
}
test conditions:
Single push thread and one pop thread.
No other concurrent operations modifying tail or next pointers.
In the code, compare_exchange_weak is used in a loop to handle potential spurious failures. However, I expected compare_exchange_strong to be resilient to such issues without needing a loop. Despite this, when I replace compare_exchange_weak with compare_exchange_strong, the assertion in the else branch triggers, even though there is only one push thread and no other thread should be interfering with the push operation.
Unexpected Behavior with compare_exchange_strong in Single Producer Multi Consumer Queue Implementation
Why does compare_exchange_strong fail in this context even though there is no concurrent push operation?
Is there a fundamental difference in how compare_exchange_strong and compare_exchange_weak handle interruptions that I might be missing?
What are the best practices for using compare_exchange_strong and compare_exchange_weak in a single-producer, multi-consumer queue?
Note:
The implementation is copied from the book Concurrency in Action by Anthony Williams.
running on an x86-64 architecture.
using the GCC 9 compiler
Why does compare_exchange_strong fail in this context even though there is no concurrent push operation?
Is there a fundamental difference in how compare_exchange_strong and compare_exchange_weak handle interruptions that I might be missing?
What are the best practices for using compare_exchange_strong and compare_exchange_weak in a single-producer, multi-consumer queue?
user25444684 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.