Replies: 1 comment 3 replies
-
Good question! The reason mutual exlusion is needed even in the SPSC scenario is to keep operation ordering and prevent data races. Both compilers and out of order CPUs are allowed to reorder code they deem independent, and while doing this they cannot take into account the fact that multiple threads could be operating on the same data, leading to data races where two threads can see events in different orders. Consider the following example using the Queue: template <typename T, size_t size> bool Queue<T, size>::Push(const T &element) {
const size_t w = _w.load(std::memory_order_relaxed);
size_t w_next = w + 1;
if (w_next == size) {
w_next = 0U;
}
const size_t r = _r.load(std::memory_order_acquire);
if (w_next == r) {
return false;
}
_data[w] = element;
_w.store(w_next, std::memory_order_release);
return true;
} Without atomic operations here, the compiler or the CPU might decide that placing the element and bumping the write index are independent operations as there is no data dependency from their viewpoint. However, we know that if the write index is stored first and the operation is interrupted, the reading thread will believe there is data to be read. While mutual exclusion just prevents the entire object from being accessed from different threads at the same time, bypassing this problem, atomic operations are about preventing the compiler and the CPU from reordering operations by generating code in the order we intended and hardware barriers that prevent operation reordering on the CPU side. |
Beta Was this translation helpful? Give feedback.
-
I'm curious, why do we need mutual exclusión in the ring buffer if we are in the spsc scenario?
Beta Was this translation helpful? Give feedback.
All reactions