I would like to use a std::atomic<std::shared_ptr>
in my code so that the shared_ptr can be atomicaly updated, but I have a problem when accessing the shared_ptr. The load() method on the atomic seems to reduce the ref-count on the shared_ptr, so that I can't actually use the object without it being deallocated.
Here is a simplified piece of code that shows the problem...
typedef shared_ptr<MyClass> MyClassPtr;
typedef atomic<MyClassPtr> MyClassAtomicPtr;
// 1.
MyClassPtr ptr( new MyClass() );
printf("1. use_count=%d\n", ptr.use_count());
// 2.
MyClassAtomicPtr atomicPointer(ptr);
printf("2. use_count=%d\n", ptr.use_count());
// 3.
{
MyClassPtr p = atomicPointer.load();
printf("3a. use_count=%d\n", ptr.use_count());
}
printf("3b. use_count=%d\n", ptr.use_count());
// 4.
{
MyClassPtr p = atomicPointer.load();
printf("4a. use_count=%d\n", ptr.use_count());
}
printf("4b. use_count=%d\n", ptr.use_count());
The output of this is:
1. use_count=1
2. use_count=2
3a. use_count=2
3b. use_count=1
4a. use_count=1
4b. use_count=-572662307
I understand steps 1 and 2. But at step 3, I would expect the assignment to the shared_ptr to increase the ref-count to 3, and then when it goes out of scope for the ref-count to go back down to 2. But in fact it stays at 2 when assigned and then decreases to 1 when the shared_ptr goes out of scope. Similarly in step 4, where the ref-count goes to zero and the object is deleted.
So my question is: how can I access and use the shared_ptr managed by the atomic without destroying it?
(I was compiling with Visual Studio 2012 Version 11.0.50727.1 RTMREL)
I believe the standard way to atomically load and store shared pointers are to use the functions in §20.7.2.5[util.smartptr.shared.atomic]. It seems only libc++ of clang support them:
template<class T> bool atomic_is_lock_free(const shared_ptr<T>* p);
template<class T> shared_ptr<T> atomic_load(const shared_ptr<T>* p);
template<class T> shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo);
template<class T> void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);
template<class T> void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
template<class T> shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r);
template<class T> shared_ptr<T> atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
template<class T> bool atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
template<class T> bool atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
template<class T> bool atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w, memory_order success, memory_order failure);
template<class T> bool atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w, memory_order success, memory_order failure);
So you code could be written as:
auto ptr = std::make_shared<MyClass>();
printf("1. use_count=%d\n", ptr.use_count());
{
auto p = std::atomic_load(&ptr);
printf("3a. use_count=%d\n", ptr.use_count());
}
printf("3b. use_count=%d\n", ptr.use_count());
{
auto p = std::atomic_load(&ptr);
printf("3a. use_count=%d\n", ptr.use_count());
}
printf("4b. use_count=%d\n", ptr.use_count());
But I can't find such supports listed on MSDN, so the best you could do is to use a mutex. (Actually, the implementation of these functions in libc++ uses a mutex too.)