From cc89509f831111aadf869b1065ec1ae01f6759b8 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 17 Dec 2012 00:28:44 +0100 Subject: [PATCH] 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 --- src/corelib/kernel/qobject.cpp | 6 +++ .../corelib/kernel/qobject/tst_qobject.cpp | 47 +++++++++++++++---- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 20c634ad781..9091b5579e8 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -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(connection).d_ptr = 0; c->deref(); // has been removed from the QMetaObject::Connection object diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 76a97f89f4d..581644b19f5 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -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); }