Consider the following code snippet taken from Herb Sutter's talk about Atomics:
The smart_ptr class contains a pimpl object named control_block_ptr that contains the reference count of refs .
// Thread A: // smart_ptr copy ctor smart_ptr(const smart_ptr& other) { ... control_block_ptr = other->control_block_ptr; control_block_ptr->refs.fetch_add(1, memory_order_relaxed); ... } // Thread D: // smart_ptr destructor ~smart_ptr() { if (control_block_ptr->refs.fetch_sub(1, memory_order_acq_rel) == 0) { delete control_block_ptr; } }
Herb Sutter says that incrementing refs in Thread A can use memory_order_relaxed because "no one does anything based on the action." Now that I understand memory_order_relaxed, if refs is N at some point, and two threads A and B execute the following code:
control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
then it can happen that both threads see the value of refs as N, and both return N + 1. It obviously does not work, and memory_order_acq_rel should be used in the same way as with the destructor. Where am I mistaken?
EDIT1: consider the following code.
atomic_int refs = N; // at time t0. // [Thread 1] refs.fetch_add(1, memory_order_relaxed); // at time t1. // [Thread 2] n = refs.load(memory_order_relaxed); // starting at time t2 > t1 refs.fetch_add(1, memory_order_relaxed); n = refs.load(memory_order_relaxed);
What is the value of refs observed in Thread 2 before calling fetch_add? Could it be N or N + 1? What is the value of refs observed in Thread 2 after calling fetch_add? Should it be at least N + 2?
[URL talk: C ++ and Beyond 2012 - http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2 (@ 1:20:00)]
c ++ multithreading c ++ 11 memory-model
Cppnoob
source share