Regardless if an object's reference counter is atomic, there is one major problem when a single RefPtr
holding it is being re-assigned and read concurrently.
Here I'll explain on a simple example. Note that all the time we are in a method of a single object, Type
has ThreadSafeAutoRefCnt
reference counter, when talking Mozilla code-base terms:
RefPtr<Type>; local = mMember; // mMember is RefPtr<mType>, holding an object
And another piece of code then, on a different thread:
mMember = new Type(); // mMember's value is rewritten with a new object
Usually, people believe this is perfectly safe. But it's far from it. Just break this to actual atomic operations and put the two threads side by side:
Thread 1
local.value = mMemeber.value; /* context switch */ . . . . . . local.value->AddRef();
Thread 2
. . Type* temporary = new Type(); temporary->AddRef(); Type* old = mMember.value; mMember.value = temporary; old->Release(); /* context switch */ .
Similar for clearing a member (or a global, when we are here) while some other thread may try to grab a reference to it:
RefPtr<type>; service = sService; // sService is a RefPtr if (!service) return; // service being null is our 'after shutdown' flag
And another thread doing, usually during shutdown:
sService = nullptr; // while sService was holding an object
And here is what actually happens:
Thread 1
local.value = sService.value; /* context switch */ . . . . local.value->AddRef();
Thread 2
. . Type* old = sService.value; sService.value = nullptr; old->Release(); /* context switch */ .
And where is the problem? Clearly, if the Release()
call on the second thread is the last one on the object, the AddRef()
on the first thread will do its job on a dying or already dead object, not talking about further access to a bad pointer.
The only correct way is to have both in and out assignments protected by a mutex or ensure that anyone cannot be trying to grab a reference from a globally accessed RefPtr
when it's being finally released or just being re-assigned. The latter may not always be easy or even possible.
Anyway, if somebody knows how to solve this universally without using an additional lock, I would be interested!