Intrusive shared pointers have some advantages over classical control block shared pointers, as they allow, as far as I understand, atomic, lock-free concurrent operations with single writer and multiple readers, with one major exception : when a pointer is being reset and RefCount reaches zero, deleting the buffer, it cannot be concurrently copied, with a defined return value (either the old valid pointer with proper refcount, or a valid null pointer)
Is there any way to remove that restriction?
Here is a proposed, invalid implementation.
template<typename T>
class TIntrusiveSharedPointer
{
public:
TIntrusiveSharedPointer() { AP.store(nullptr); }
~TIntrusiveSharedPointer() { Reset(); }
// Resets the reference
void Reset() noexcept
{
T* OldP = AP.exchange(nullptr, std::memory_order_acq_rel)
if(OldP && OldP->RefCount.fetch_sub(1, std::memory_order_acq_rel)==1)
{
delete OldP;
}
}
/* Copy constructor */
TIntrusiveSharedPointer(const TIntrusiveSharedPointer& Other)
{
while(true)
{
T* NewP = Other.AP.load(std::memory_order_consume);
if(!NewP)
{
AP.store(nullptr, std::memory_order_release);
return;
}
//We cannot safely read RefCount here, because even if we loaded
//A non zero NewP, RefCount might already have been decremented to 0
//And pointer destroyed
if(uint64 Count = NewP->RefCount.load(std::memory_order_acquire))
{
//Current RefCount is >=1, we can increment
if(NewP->RefCountpare_exchange_weak (
Count, Count+1, std::memory_order_acquire))
{
AP.store(NewP, std::memory_order_release);
return;
}
}
else
{
//Current RefCount is zero, buffer about to be destroyed
AP.store(nullptr, std::memory_order_release);
return;
}
}
}
private:
std::atomic<T*> AP;
};
TIntrusiveSharedPointer<SomeClass> A;
void Thread1()
{
A.Reset();
}
void Thread2()
{
//Undefined behavior if run concurrently and A buffer is destroyed
TIntrusiveSharedPointer<SomeClass> B(A);
}