Boost.Atomic documentation, Reference Counting
Hello. Your documentation to Boost.Atomic ([1]) has been given me as an answer to my StackOverflow ([2]) question. Here is a snippet from the documentation: friend void intrusive_ptr_release(const X * x) { if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) { boost::atomic_thread_fence(boost::memory_order_acquire); delete x; } } And your rationale: "It is important to enforce any possible access to the object in one thread (through an existing reference) to happen before deleting the object in a different thread. This is achieved by a "release" operation after dropping a reference (any access to the object through this reference must obviously happened before), and an "acquire" operation before deleting the object." I wonder why this would not work well if it looked like this instead: friend void intrusive_ptr_release(const X * x) { if (x->refcount_.fetch_sub(1, boost::memory_order_relaxed) == 1) { boost::atomic_thread_fence(boost::memory_order_acq_rel); delete x; } } [1] http://www.chaoticmind.net/~hcb/projects/boost.atomic/doc/atomic/usage_examp... [2] http://stackoverflow.com/questions/10268737/c11-atomics-and-intrusive-shared... -- VZ
On 23/04/12 08:26, Václav Zeman wrote:
Your documentation to Boost.Atomic ([1]) has been given me as an answer to my StackOverflow ([2]) question. Here is a snippet from the documentation:
friend void intrusive_ptr_release(const X * x) { if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) { boost::atomic_thread_fence(boost::memory_order_acquire); delete x; } }
And your rationale: "It is important to enforce any possible access to the object in one thread (through an existing reference) to happen before deleting the object in a different thread. This is achieved by a "release" operation after dropping a reference (any access to the object through this reference must obviously happened before), and an "acquire" operation before deleting the object."
I wonder why this would not work well if it looked like this instead:
friend void intrusive_ptr_release(const X * x) { if (x->refcount_.fetch_sub(1, boost::memory_order_relaxed) == 1) { boost::atomic_thread_fence(boost::memory_order_acq_rel); delete x; } }
If 2 threads A and B have a reference to the object, and both drop their references then one thread will delete the object. If thread A is the one that deletes the object, then the destructor will access the object. If thread B had modified the object prior to dropping its reference, then unless there is some form of ordering constraint that requires the modifications on thread B to be visible to thread A prior to the destructor call then you have a data race and undefined behaviour. The easiest way to achieve that is to make the fetch_sub use memory_order_release. This then synchronizes-with the fence with memory_order_acquire in thread A, and all is well. Making the fence use memory_order_acq_rel doesn't achieve anything, since this is only executed in the thread that will call delete (thread A). Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++11 thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
participants (2)
-
Anthony Williams
-
Václav Zeman