Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

atomic_load/atomic_store on std::shared_ptr in VC11 - why the global spinlock?

I'm trying to understand exactly how to manage shared pointers safely with atomic operations. Turns out VC11 (Visual studio 2012) has support for C++11 and thereby can permit read/write races on std::shared_ptr. I want to check I understood the basics, then ask something about the implementation detail of the atomic ops on std::shared_ptr in VC11.

std::shared_ptr<A> x, y, z;
x = std::make_shared<A>(args1);
y = std::make_shared<A>(args2);

Thread 1

std::shared_ptr<A> temp = std::atomic_load(y);

Thread 2

std::atomic_store(&y, z);

Without the atomics, the race would potentially cause temp to end up having corrupted state, or Thread 2 could delete the A instance pointed to by the original y just as Thread 1 was attempting to copy and addref the shared_ptr, which would make it point to a "zombie" object.

My question regarding atomic_load and atomic_store in VC11:

I've noticed they use a spinlock that performs test-and-set on a global variable. So I wondered: why not do the test-and-set on the topmost bit of the reference counter of the shared_ptr itself? that way locks on different shared_ptr's won't contend with each other. Is there a reason this was not done?

EDIT: VS implementation of atomic_is_lock_free. Not surprising, since it IS using a spinlock for everything. Still wondering why they couldn't make it use a shared_ptr-instance-specific lock instead of a global lock.

template <class _Ty> inline
bool atomic_is_lock_free(const shared_ptr<_Ty> *)
{   // return true if atomic operations on shared_ptr<_Ty> are lock-free
    return (false);
}
like image 965
yonil Avatar asked Oct 22 '22 15:10

yonil


2 Answers

You can't do an atomic test-and-set on the shared_ptr's ref count because the ref count is stored in the shared_ptr's control block. By the time you got around to attempting your test-and-set, another thread could have released the last shared_ptr reference and deleted the control block from under you.

Thread 1                                  Thread 2
Read control block address

                                          Decrement ref count (now 0)
                                          Delete control block

Test-and-set ref count (undefined behaviour)

Remember that the premise here is that multiple threads are manipulating the same shared_ptr instance. If each thread has its own instance (pointing to the same controlled object), then we have no issues and no need for atomic shared_ptr operations.

like image 192
Peter Ruderman Avatar answered Oct 27 '22 09:10

Peter Ruderman


Munging the top bit of the reference count would require code that deals with the reference count as a counter to ignore that top bit. That is, it would make the most common uses slower in order to provide a minor speed increase in less common cases.

like image 25
Pete Becker Avatar answered Oct 27 '22 10:10

Pete Becker