Delete the QSlotObject when disconnect()ing

When disconnect()ing through a QMetaObject::Connection,  if the
QObjectPrivate::Connection contains a slot object, deref it, so
that it will be destroyed before the next run of cleanConnectionList.

Previously, a copy of the functor passed to connect() was kept until
QObjectPrivate::cleanConnectionLists was called (by adding a new signal,
or the sender was destroyed), even after a successful call to
disconnect(). That is, we were keeping that copy allocated without
any good reason.

Change-Id: Ie6074ea797df1611cb995dec07c5b5a742360833
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
Giuseppe D'Angelo 2012-12-17 00:28:44 +01:00 committed by The Qt Project
parent c4f433d581
commit cc89509f83
2 changed files with 45 additions and 8 deletions

View File

@ -4294,6 +4294,12 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
c->next->prev = c->prev;
c->receiver = 0;
// destroy the QSlotObject, if possible
if (c->isSlotObject) {
c->slotObj->destroyIfLastRef();
c->isSlotObject = false;
}
const_cast<QMetaObject::Connection &>(connection).d_ptr = 0;
c->deref(); // has been removed from the QMetaObject::Connection object

View File

@ -5594,12 +5594,8 @@ void tst_QObject::disconnectDoesNotLeakFunctor()
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
// the connection list has been marked dirty, but not cleaned yet.
QCOMPARE(countedStructObjectsCount, 2);
QCOMPARE(countedStructObjectsCount, 1);
}
// 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);
@ -5616,11 +5612,46 @@ void tst_QObject::disconnectDoesNotLeakFunctor()
QVERIFY(c2);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c1));
// the connection list has been marked dirty, but not cleaned yet.
QCOMPARE(countedStructObjectsCount, 2);
// functor object has been destroyed
QCOMPARE(countedStructObjectsCount, 1);
}
// c2 still holds a reference; c1 has been cleaned up
QCOMPARE(countedStructObjectsCount, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
{
CountedStruct s;
QCOMPARE(countedStructObjectsCount, 1);
QTimer timer;
QMetaObject::Connection c = connect(&timer, &QTimer::timeout, s);
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 1);
}
QCOMPARE(countedStructObjectsCount, 0);
{
QTimer timer;
QMetaObject::Connection c = connect(&timer, &QTimer::timeout, CountedStruct());
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 1); // only one instance, in Qt internals
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 0); // functor being destroyed
}
QCOMPARE(countedStructObjectsCount, 0);
{
#if defined(Q_COMPILER_LAMBDA)
CountedStruct s;
QCOMPARE(countedStructObjectsCount, 1);
QTimer timer;
QMetaObject::Connection c = connect(&timer, &QTimer::timeout, [s](){});
QVERIFY(c);
QCOMPARE(countedStructObjectsCount, 2);
QVERIFY(QObject::disconnect(c));
QCOMPARE(countedStructObjectsCount, 1);
#endif // Q_COMPILER_LAMBDA
}
QCOMPARE(countedStructObjectsCount, 0);
}