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:
Giuseppe D'Angelo 2012-12-16 23:06:45 +01:00 committed by The Qt Project
parent 058c029b1e
commit c4f433d581
2 changed files with 60 additions and 0 deletions

View File

@ -4293,6 +4293,10 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
if (c->next)
c->next->prev = c->prev;
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).
return true;

View File

@ -141,6 +141,7 @@ private slots:
void returnValue2();
void connectVirtualSlots();
void connectFunctorArgDifference();
void disconnectDoesNotLeakFunctor();
};
class SenderObject : public QObject
@ -5569,5 +5570,60 @@ void tst_QObject::connectFunctorArgDifference()
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)
#include "tst_qobject.moc"