ARTICLE AD BOX
Consider this example:
#include <atomic> #include <thread> int main(){ std::atomic<int> epoch = {0}; std::atomic<int> value = {1}; std::thread t1([&](){ epoch.store(1,std::memory_order::seq_cst); // #1 value.load(std::memory_order::seq_cst); // #2 }); std::thread t2([&](){ value.exchange(2,std::memory_order::seq_cst); // #3 epoch.load(std::memory_order::seq_cst); // #4 }); }In this example, if #2 read 1, then #4 must read #1, because it's guaranteed by the single total order #1 < #2 < #3 < #4. For other cases, #4 can either read the initial value or #1, they correspond to a valid single total order #3 < #4 < #1 < #2, or #3 < #1 < #2 < #4(or, #3 < #1 < #4 < #2), respectively.
If change the example to the following:
#include <atomic> #include <thread> int main(){ std::atomic<int> epoch = {0}; std::atomic<int> value = {1}; std::thread t1([&](){ epoch.store(1,std::memory_order::relaxed); // #1 value.fetch_add(0,std::memory_order::acq_rel); // #2 }); std::thread t2([&](){ value.exchange(2,std::memory_order::acq_rel); // #3 epoch.load(std::memory_order::relaxed); // #4 }); }In this example, if #2 read 1, according to [atomics.order] p10
Atomic read-modify-write operations shall always read the last value (in the modification order) written before the write associated with the read-modify-write operation.
#3 must follow #2 in the modification order of value, because their memory orders are acq_rel, #2 must happens-before #3, so the visibility of #1 to #4 is now guaranteed by the happens-before. Similarly, for other cases, #4 can either read 0 or #1. IIUC, these two programs are functionally equivalent in observable behaviors.
This first snippet of code is simplified from the EBR algorithm(i.e., Epoch-Based Reclamation). #3 emulates the operation that updates the pointer, the core idea is that, if #2 reads the old pointer value, it must precede #3 in the single total order, so #1 also precedes #4 in the single total order; therefore, #4 must see #1 to check whether the reader's epoch is the current one.
If we only consider the correctness of implementing the core idea, can I change the memory order seq_cst to acq_rel by changing the corresponding operations to those in the second example? IIUC, they should have the same correctness; merely, the visibility of #1 is guaranteed by the single total order in the first way, and is guaranteed by happens-before in the second way.
