In the boost::shared_ptr
destructor, this is done:
if(--*pn == 0)
{
boost::checked_delete(px);
delete pn;
}
where pn
is a pointer to the reference counter, which is typedefed as
shared_ptr::count_type -> detail::atomic_count -> long
I would have expected the long
to be volatile long
, given threaded usage and the non-atomic 0-check-and-deletion in the shared_ptr
destructor above. Why isn't it volatile?
EDIT:
It turns out I looked at the header used when multi-threaded usage is not specified (atomic_count.hpp). In atomic_count_win32.hpp, the decrement is properly implemented for multithreaded usage.
Because volatile
is not necessary for multithreading, and does nothing beneficial, but potentially destroys a number of optimizations.
In order to ensure safe multithreaded access to a variable, the primitive we need is a memory barrier, which provides both the guarantee of volatile
and a few others (it prevents memory access reordering across the barrier, which volatile doesn't do)
I believe shared_ptr
uses atomic operations when possible, which implicitly provide a memory barrier. Otherwise it falls back to a mutex, which also provides a memory barrier.
See Why is volatile not considered useful in multithreaded C or C++ programming? or http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/ for more details
Editcount_type
is not a long
in the general case. It is convertible to a long
. If you look in atomic_count.hpp
, the typedef to long is only applied if no threading is available (in which case of course, no synchronization is necessary). Otherwise it uses the implementation defined in boost/smart_ptr/detail/atomic_count_pthreads.hpp
or boost/smart_ptr/detail/atomic_count_win32.hpp
or one of the other files listed. And those are synchronized wrapper classes that ensures all operations are done atomically.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With