Why isn't the reference counter in boost::shared_ptr volatile?
-
22-09-2019 - |
문제
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
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.
다른 팁
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