Why isn't the reference counter in boost::shared_ptr volatile?
-
22-09-2019 - |
Question
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.
Solution
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
Edit
count_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.
OTHER TIPS
volatile
has virtually nothing to do with threading. See here.
You are misreading the code. atomic_count
is defined simply as a long if the code is not using multithreading:
#ifndef BOOST_HAS_THREADS
namespace boost
{
namespace detail
{
typedef long atomic_count;
}
}
#elif //... include various platform-specific headers that define atomic_count class
#endif