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,
ThreadSafeAutoRefCnt reference counter, when talking Mozilla code-base terms:
RefPtr<Type> local = mMember; // mMember is RefPtr<Type>, holding an object
And other 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:
local.value = mMemeber.value; /* context switch */ . . . . . . local.value->AddRef();
. . 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 what actually happens:
local.value = sService.value; /* context switch */ . . . . local.value->AddRef();
. . 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 there cannot be anyone 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 a way how to solve this universally without using an additional lock, I would be really interested!