Fix rare double-free in QObject machinery
As exposed by tst_QObjectRace::destroyRace we would sometimes end up with a double-free when destroying a QSlotObject in multi-threaded scenarios. One free would be done in ~QObject as the receiver was being destroyed while the other free was done when deleting a QMetaCallEvent object after we realized it was not needed because the receiver was destroyed. Since we can be in a separate thread from the receiver we should lock before referencing the connection object. Amends b7d073e9905bf9812ba96cecdcf6871a95517d30. Change-Id: Icb53862dc880ae9a4e5581a1a9ee693573f7d9c7 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
77160d2923
commit
e9eddfd856
@ -3728,6 +3728,15 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
|
|||||||
while (argumentTypes[nargs-1])
|
while (argumentTypes[nargs-1])
|
||||||
++nargs;
|
++nargs;
|
||||||
|
|
||||||
|
QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
|
||||||
|
if (!c->receiver.loadRelaxed()) {
|
||||||
|
// the connection has been disconnected before we got the lock
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c->isSlotObject)
|
||||||
|
c->slotObj->ref();
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
QMetaCallEvent *ev = c->isSlotObject ?
|
QMetaCallEvent *ev = c->isSlotObject ?
|
||||||
new QMetaCallEvent(c->slotObj, sender, signal, nargs) :
|
new QMetaCallEvent(c->slotObj, sender, signal, nargs) :
|
||||||
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs);
|
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs);
|
||||||
@ -3746,9 +3755,11 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
|
|||||||
args[n] = QMetaType::create(types[n], argv[n]);
|
args[n] = QMetaType::create(types[n], argv[n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
|
locker.relock();
|
||||||
|
if (c->isSlotObject)
|
||||||
|
c->slotObj->destroyIfLastRef();
|
||||||
if (!c->receiver.loadRelaxed()) {
|
if (!c->receiver.loadRelaxed()) {
|
||||||
// the connection has been disconnected before we got the lock
|
// the connection has been disconnected while we were unlocked
|
||||||
locker.unlock();
|
locker.unlock();
|
||||||
delete ev;
|
delete ev;
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user