I was looking through Qt 4.8.0's implementation of QString
's implicit sharing, specifically how it's done it in a thread-safe manner. It seems that the key idea is to hold a reference count in an integer d->ref
of type QBasicAtomicInt
that can be incremented and decremented atomically. For referece:
inline QString::QString(const QString &other) : d(other.d)
{
Q_ASSERT(&other != this);
d->ref.ref();
}
inline QString::~QString()
{
if (!d->ref.deref())
free(d);
}
inline bool QBasicAtomicInt::deref()
{
unsigned char ret;
asm volatile("lock\n"
"decl %0\n"
"setne %1"
: "=m" (_q_value), "=qm" (ret)
: "m" (_q_value)
: "memory");
return ret != 0;
}
Suppose the current value of d->ref
in a QString
object A is 1 and A.deref()
is called. Isn't it possible that once the value of the ret != 0
expression (i.e. false
) has been decided a different thread of execution could copy the QString
, therefore incrementing its internal reference to 1? For example, this could happen if the second thread had a pointer to A and then did something like QString otherString = *Aptr;
, which would call the copy constructor. In that scenario, the thread that saw deref()
return false
would free the shared memory, but the second thread would think it's still valid.
What am I missing? Is it that once you go multi-threaded taking pointers to these types of objects is inherently error-prone and should be avoided? Is the class thread safe as long as you only use its interface and refrain from taking pointers to them?