Reset the QMetaObject::Connection dptr when disconnect()ing
The QObjectPrivate::Connection refcount was not decreased when disconnect()ing, therefore it was kept alive by the owning QMetaObject::Connection object. This removes a leak in case the QMetaObject::Connection survives the sender object, after a successful disconnect(). Change-Id: Ie2ea59b269a0e589ae23c1457df7533be77c0797 Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
parent
058c029b1e
commit
c4f433d581
@ -4293,6 +4293,10 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
|
|||||||
if (c->next)
|
if (c->next)
|
||||||
c->next->prev = c->prev;
|
c->next->prev = c->prev;
|
||||||
c->receiver = 0;
|
c->receiver = 0;
|
||||||
|
|
||||||
|
const_cast<QMetaObject::Connection &>(connection).d_ptr = 0;
|
||||||
|
c->deref(); // has been removed from the QMetaObject::Connection object
|
||||||
|
|
||||||
// disconnectNotify() not called (the signal index is unknown).
|
// disconnectNotify() not called (the signal index is unknown).
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -141,6 +141,7 @@ private slots:
|
|||||||
void returnValue2();
|
void returnValue2();
|
||||||
void connectVirtualSlots();
|
void connectVirtualSlots();
|
||||||
void connectFunctorArgDifference();
|
void connectFunctorArgDifference();
|
||||||
|
void disconnectDoesNotLeakFunctor();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SenderObject : public QObject
|
class SenderObject : public QObject
|
||||||
@ -5569,5 +5570,60 @@ void tst_QObject::connectFunctorArgDifference()
|
|||||||
QVERIFY(true);
|
QVERIFY(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int countedStructObjectsCount = 0;
|
||||||
|
struct CountedStruct
|
||||||
|
{
|
||||||
|
CountedStruct() { ++countedStructObjectsCount; }
|
||||||
|
CountedStruct(const CountedStruct &) { ++countedStructObjectsCount; }
|
||||||
|
CountedStruct &operator=(const CountedStruct &) { return *this; }
|
||||||
|
~CountedStruct() { --countedStructObjectsCount; }
|
||||||
|
void operator()() const {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QObject::disconnectDoesNotLeakFunctor()
|
||||||
|
{
|
||||||
|
QCOMPARE(countedStructObjectsCount, 0);
|
||||||
|
{
|
||||||
|
QMetaObject::Connection c;
|
||||||
|
{
|
||||||
|
CountedStruct s;
|
||||||
|
QCOMPARE(countedStructObjectsCount, 1);
|
||||||
|
QTimer timer;
|
||||||
|
|
||||||
|
c = connect(&timer, &QTimer::timeout, s);
|
||||||
|
QVERIFY(c);
|
||||||
|
QCOMPARE(countedStructObjectsCount, 2);
|
||||||
|
QVERIFY(QObject::disconnect(c));
|
||||||
|
// the connection list has been marked dirty, but not cleaned yet.
|
||||||
|
QCOMPARE(countedStructObjectsCount, 2);
|
||||||
|
}
|
||||||
|
// disconnect() dropped the reference that c had to the functor;
|
||||||
|
// the sender has been destroyed. Therefore, the QObjectPrivate::Connection
|
||||||
|
// refcount dropped to zero and destroyed the functor.
|
||||||
|
QCOMPARE(countedStructObjectsCount, 0);
|
||||||
|
}
|
||||||
|
QCOMPARE(countedStructObjectsCount, 0);
|
||||||
|
{
|
||||||
|
QMetaObject::Connection c1, c2;
|
||||||
|
{
|
||||||
|
CountedStruct s;
|
||||||
|
QCOMPARE(countedStructObjectsCount, 1);
|
||||||
|
QTimer timer;
|
||||||
|
|
||||||
|
c1 = connect(&timer, &QTimer::timeout, s);
|
||||||
|
QVERIFY(c1);
|
||||||
|
c2 = c1;
|
||||||
|
QVERIFY(c2);
|
||||||
|
QCOMPARE(countedStructObjectsCount, 2);
|
||||||
|
QVERIFY(QObject::disconnect(c1));
|
||||||
|
// the connection list has been marked dirty, but not cleaned yet.
|
||||||
|
QCOMPARE(countedStructObjectsCount, 2);
|
||||||
|
}
|
||||||
|
// c2 still holds a reference; c1 has been cleaned up
|
||||||
|
QCOMPARE(countedStructObjectsCount, 1);
|
||||||
|
}
|
||||||
|
QCOMPARE(countedStructObjectsCount, 0);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QObject)
|
QTEST_MAIN(tst_QObject)
|
||||||
#include "tst_qobject.moc"
|
#include "tst_qobject.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user