diff options
-rw-r--r-- | src/corelib/global/qnamespace.h | 3 | ||||
-rw-r--r-- | src/corelib/global/qnamespace.qdoc | 7 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 115 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_p.h | 6 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 591 |
5 files changed, 680 insertions, 42 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 9b5af17f7c..a531bc627b 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1301,7 +1301,8 @@ namespace Qt { DirectConnection, QueuedConnection, BlockingQueuedConnection, - UniqueConnection = 0x80 + UniqueConnection = 0x80, + SingleShotConnection = 0x100, }; enum ShortcutContext { diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index b5830173f8..95a73c4439 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -638,6 +638,13 @@ (i.e. if the same signal is already connected to the same slot for the same pair of objects). This flag was introduced in Qt 4.6. + \value SingleShotConnection + This is a flag that can be combined with any one of the above + connection types, using a bitwise OR. When Qt::SingleShotConnection + is set, the slot is going to be called only once; the connection + will be automatically broken when the signal is emitted. + This flag was introduced in Qt 6.0. + With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes. If you try diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 6cc0a9a4e7..4d0523c898 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -3325,8 +3325,14 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, c2 = c2->nextConnectionList.loadRelaxed(); } } - type &= Qt::UniqueConnection - 1; } + type &= ~Qt::UniqueConnection; + + const bool isSingleShot = type & Qt::SingleShotConnection; + type &= ~Qt::SingleShotConnection; + + Q_ASSERT(type >= 0); + Q_ASSERT(type <= 3); std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection}; c->sender = s; @@ -3341,6 +3347,7 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, c->isSlotObject = false; c->argumentTypes.storeRelaxed(types); c->callFunction = callFunction; + c->isSingleShot = isSingleShot; QObjectPrivate::get(s)->addConnection(signal_index, c.get()); @@ -3632,7 +3639,8 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect ++nargs; QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed())); - if (!c->receiver.loadRelaxed()) { + QObject *receiver = c->receiver.loadRelaxed(); + if (!receiver) { // the connection has been disconnected before we got the lock return; } @@ -3658,17 +3666,22 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect args[n] = types[n].create(argv[n]); } + if (c->isSingleShot && !QObjectPrivate::disconnect(c)) { + delete ev; + return; + } + locker.relock(); if (c->isSlotObject) c->slotObj->destroyIfLastRef(); - if (!c->receiver.loadRelaxed()) { + if (!c->isSingleShot && !c->receiver.loadRelaxed()) { // the connection has been disconnected while we were unlocked locker.unlock(); delete ev; return; } - QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev); + QCoreApplication::postEvent(receiver, ev); } template <bool callbacks_enabled> @@ -3762,10 +3775,14 @@ void doActivate(QObject *sender, int signal_index, void **argv) sender->metaObject()->className(), sender, receiver->metaObject()->className(), receiver); } + + if (c->isSingleShot && !QObjectPrivate::disconnect(c)) + continue; + QSemaphore semaphore; { QBasicMutexLocker locker(signalSlotLock(receiver)); - if (!c->receiver.loadAcquire()) + if (!c->isSingleShot && !c->receiver.loadAcquire()) continue; QMetaCallEvent *ev = c->isSlotObject ? new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) : @@ -3778,6 +3795,9 @@ void doActivate(QObject *sender, int signal_index, void **argv) #endif } + if (c->isSingleShot && !QObjectPrivate::disconnect(c)) + continue; + QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index); if (c->isSlotObject) { @@ -4855,7 +4875,7 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa */ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index, const QObject *receiver, void **slot, - QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, + QtPrivate::QSlotObjectBase *slotObj, int type, const int *types, const QMetaObject *senderMetaObject) { if (!sender || !receiver || !slotObj || !senderMetaObject) { @@ -4889,8 +4909,14 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s c2 = c2->nextConnectionList.loadRelaxed(); } } - type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection); } + type &= ~Qt::UniqueConnection; + + const bool isSingleShot = type & Qt::SingleShotConnection; + type &= ~Qt::SingleShotConnection; + + Q_ASSERT(type >= 0); + Q_ASSERT(type <= 3); std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection}; c->sender = s; @@ -4906,6 +4932,7 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s c->argumentTypes.storeRelaxed(types); c->ownArgumentTypes = false; } + c->isSingleShot = isSingleShot; QObjectPrivate::get(s)->addConnection(signal_index, c.get()); QMetaObject::Connection ret(c.release()); @@ -4929,39 +4956,12 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s bool QObject::disconnect(const QMetaObject::Connection &connection) { QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr); - - if (!c) - return false; - QObject *receiver = c->receiver.loadRelaxed(); - if (!receiver) - return false; - - QBasicMutex *senderMutex = signalSlotLock(c->sender); - QBasicMutex *receiverMutex = signalSlotLock(receiver); - - QObjectPrivate::ConnectionData *connections; - { - QOrderedMutexLocker locker(senderMutex, receiverMutex); - - // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime - receiver = c->receiver.loadRelaxed(); - if (!receiver) - return false; - - connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed(); - Q_ASSERT(connections); - connections->removeConnection(c); + const bool disconnected = QObjectPrivate::disconnect(c); + if (disconnected) { + const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr; + c->deref(); // has been removed from the QMetaObject::Connection object } - - connections->cleanOrphanedConnections(c->sender); - - c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), - c->signal_index)); - - const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr; - c->deref(); // has been removed from the QMetaObject::Connection object - - return true; + return disconnected; } /*! \fn template<typename PointerToMemberFunction> bool QObject::disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method) @@ -5084,6 +5084,43 @@ bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void ** return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, sender, -1, slot); } +/*! + \internal + \threadsafe +*/ +bool QObjectPrivate::disconnect(QObjectPrivate::Connection *c) +{ + if (!c) + return false; + QObject *receiver = c->receiver.loadRelaxed(); + if (!receiver) + return false; + + QBasicMutex *senderMutex = signalSlotLock(c->sender); + QBasicMutex *receiverMutex = signalSlotLock(receiver); + + QObjectPrivate::ConnectionData *connections; + { + QOrderedMutexLocker locker(senderMutex, receiverMutex); + + // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime + receiver = c->receiver.loadRelaxed(); + if (!receiver) + return false; + + connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed(); + Q_ASSERT(connections); + connections->removeConnection(c); + } + + connections->cleanOrphanedConnections(c->sender); + + c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), + c->signal_index)); + + return true; +} + /*! \class QMetaObject::Connection \inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection. diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 4a60d37191..1f13274945 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -153,9 +153,10 @@ public: ushort method_offset; ushort method_relative; signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex()) - ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking + ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking ushort isSlotObject : 1; ushort ownArgumentTypes : 1; + ushort isSingleShot : 1; Connection() : ref_(2), ownArgumentTypes(true) { //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection } @@ -348,10 +349,11 @@ public: static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index, const QObject *receiver, void **slot, - QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, + QtPrivate::QSlotObjectBase *slotObj, int type, const int *types, const QMetaObject *senderMetaObject); static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type); static bool disconnect(const QObject *sender, int signal_index, void **slot); + static bool disconnect(Connection *c); void ensureConnectionData() { diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 1d274d8c95..01a847596e 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -49,6 +49,8 @@ #include <private/qobject_p.h> #endif +#include <functional> + #include <math.h> class tst_QObject : public QObject @@ -154,6 +156,7 @@ private slots: void nullReceiver(); void functorReferencesConnection(); void disconnectDisconnects(); + void singleShotConnection(); }; struct QObjectCreatedOnShutdown @@ -7495,6 +7498,594 @@ void tst_QObject::disconnectDisconnects() QCOMPARE(count, 3); // + δ } +class ReceiverDisconnecting : public QObject +{ + Q_OBJECT + +public: + SenderObject *sender; + int slotCalledCount = 0; + +public slots: + void aSlotByName() + { + ++slotCalledCount; + QVERIFY(!disconnect(sender, SIGNAL(signal1()), this, SLOT(aSlotByName()))); + } + + void aSlotByPtr() + { + ++slotCalledCount; + QVERIFY(!disconnect(sender, &SenderObject::signal1, this, &ReceiverDisconnecting::aSlotByPtr)); + } +}; + +class DeleteThisReceiver : public QObject +{ + Q_OBJECT + +public: + static int counter; + +public slots: + void deleteThis() + { + ++counter; + delete this; + } +}; + +int DeleteThisReceiver::counter = 0; + +void tst_QObject::singleShotConnection() +{ + { + // Non single shot behavior: slot called every time the signal is emitted + SenderObject sender; + QMetaObject::Connection c = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 3); + } + + { + // Non single shot behavior: multiple connections cause multiple invocations + SenderObject sender; + QMetaObject::Connection c = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 2); + + QMetaObject::Connection c2 = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot); + QVERIFY(c); + QVERIFY(c2); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QVERIFY(c); + QVERIFY(c2); + QCOMPARE(sender.aPublicSlotCalled, 4); + + sender.emitSignal1(); + QVERIFY(c); + QVERIFY(c2); + QCOMPARE(sender.aPublicSlotCalled, 6); + } + + { + // Single shot behavior: slot called only once + SenderObject sender; + QMetaObject::Connection c = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 1); + } + + { + // Same, without holding a Connection object + SenderObject sender; + bool ok = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(ok); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + } + + { + // Single shot, disconnect before emitting + SenderObject sender; + QMetaObject::Connection c = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QVERIFY(QObject::disconnect(c)); + QVERIFY(!c); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + } + + { + // Single shot together with another connection + SenderObject sender; + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot)); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 4); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 5); + } + + { + // Two single shot, from the same signal, to the same slot + SenderObject sender; + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + } + + { + // Two single shot, from different signals, to the same slot + SenderObject sender; + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + QVERIFY(connect(&sender, &SenderObject::signal2, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal2(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal2(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + } + + { + // Same signal, different connections + SenderObject sender; + ReceiverObject receiver1, receiver2; + receiver1.reset(); + receiver2.reset(); + + QVERIFY(connect(&sender, &SenderObject::signal1, + &receiver1, &ReceiverObject::slot1)); + QVERIFY(connect(&sender, &SenderObject::signal1, + &receiver2, &ReceiverObject::slot1, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + QCOMPARE(receiver1.count_slot1, 0); + QCOMPARE(receiver2.count_slot1, 0); + + sender.emitSignal1(); + QCOMPARE(receiver1.count_slot1, 1); + QCOMPARE(receiver2.count_slot1, 1); + + sender.emitSignal1(); + QCOMPARE(receiver1.count_slot1, 2); + QCOMPARE(receiver2.count_slot1, 1); + + sender.emitSignal1(); + QCOMPARE(receiver1.count_slot1, 3); + QCOMPARE(receiver2.count_slot1, 1); + + // Reestablish a single shot + QVERIFY(connect(&sender, &SenderObject::signal1, + &receiver2, &ReceiverObject::slot1, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + QCOMPARE(receiver1.count_slot1, 3); + QCOMPARE(receiver2.count_slot1, 1); + + sender.emitSignal1(); + QCOMPARE(receiver1.count_slot1, 4); + QCOMPARE(receiver2.count_slot1, 2); + + sender.emitSignal1(); + QCOMPARE(receiver1.count_slot1, 5); + QCOMPARE(receiver2.count_slot1, 2); + } + + { + // Check that the slot is invoked with the connection already disconnected + SenderObject sender; + QMetaObject::Connection c; + auto breakSlot = [&]() { + QVERIFY(!c); + ++sender.aPublicSlotCalled; + }; + + c = connect(&sender, &SenderObject::signal1, + &sender, breakSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + QVERIFY(!c); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + QVERIFY(!c); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + QVERIFY(!c); + } + + { + // Same + SenderObject sender; + ReceiverDisconnecting receiver; + receiver.sender = &sender; + bool ok = connect(&sender, SIGNAL(signal1()), + &receiver, SLOT(aSlotByName()), + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(ok); + QCOMPARE(receiver.slotCalledCount, 0); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 1); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 1); + + // reconnect + ok = connect(&sender, SIGNAL(signal1()), + &receiver, SLOT(aSlotByName()), + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(ok); + QCOMPARE(receiver.slotCalledCount, 1); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 2); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 2); + } + + { + // Same + SenderObject sender; + ReceiverDisconnecting receiver; + receiver.sender = &sender; + bool ok = connect(&sender, &SenderObject::signal1, + &receiver, &ReceiverDisconnecting::aSlotByPtr, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + + QVERIFY(ok); + QCOMPARE(receiver.slotCalledCount, 0); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 1); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 1); + + // reconnect + ok = connect(&sender, &SenderObject::signal1, + &receiver, &ReceiverDisconnecting::aSlotByPtr, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(ok); + QCOMPARE(receiver.slotCalledCount, 1); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 2); + + sender.emitSignal1(); + QCOMPARE(receiver.slotCalledCount, 2); + } + + { + // Reconnect from inside the slot + SenderObject sender; + std::function<void()> reconnectingSlot; + bool reconnect = false; + reconnectingSlot = [&]() { + ++sender.aPublicSlotCalled; + if (reconnect) { + QObject::connect(&sender, &SenderObject::signal1, + &sender, reconnectingSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + } + }; + + bool ok = connect(&sender, &SenderObject::signal1, + &sender, reconnectingSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(ok); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 1); + + reconnect = true; + ok = connect(&sender, &SenderObject::signal1, + &sender, reconnectingSlot, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + QVERIFY(ok); + QCOMPARE(sender.aPublicSlotCalled, 1); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 2); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 3); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 4); + + reconnect = false; + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 5); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 5); + } + + { + // Delete the receiver from inside the slot + SenderObject sender; + QPointer<DeleteThisReceiver> p = new DeleteThisReceiver; + DeleteThisReceiver::counter = 0; + + QVERIFY(connect(&sender, &SenderObject::signal1, + p.get(), &DeleteThisReceiver::deleteThis, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection))); + + QVERIFY(p); + QCOMPARE(DeleteThisReceiver::counter, 0); + + sender.emitSignal1(); + QCOMPARE(DeleteThisReceiver::counter, 1); + QVERIFY(!p); + + sender.emitSignal1(); + QCOMPARE(DeleteThisReceiver::counter, 1); + QVERIFY(!p); + } + + { + // Queued, non single shot + SenderObject sender; + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::QueuedConnection))); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QTRY_COMPARE(sender.aPublicSlotCalled, 3); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 3); + + QTRY_COMPARE(sender.aPublicSlotCalled, 4); + } + + { + // Queued, single shot + SenderObject sender; + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection))); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QTRY_COMPARE(sender.aPublicSlotCalled, 1); + QTest::qWait(0); + QCOMPARE(sender.aPublicSlotCalled, 1); + } + + { + // Queued, single shot, checking the connection handle + SenderObject sender; + QMetaObject::Connection c = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection)); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QTRY_COMPARE(sender.aPublicSlotCalled, 1); + QVERIFY(!c); + QTest::qWait(0); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 1); + } + + { + // Queued, single shot, disconnect before emitting + SenderObject sender; + QVERIFY(connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection))); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QVERIFY(QObject::disconnect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot)); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QTest::qWait(0); + QCOMPARE(sender.aPublicSlotCalled, 0); + } + + { + // Queued, single shot, disconnect before emitting by using the connection handle + SenderObject sender; + QMetaObject::Connection c = connect(&sender, &SenderObject::signal1, + &sender, &SenderObject::aPublicSlot, + static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection)); + QVERIFY(c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QVERIFY(QObject::disconnect(c)); + QVERIFY(!c); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + sender.emitSignal1(); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + + QTest::qWait(0); + QVERIFY(!c); + QCOMPARE(sender.aPublicSlotCalled, 0); + } + + { + // Queued, single shot, delete the receiver from inside the slot + SenderObject sender; + QPointer<DeleteThisReceiver> p = new DeleteThisReceiver; + DeleteThisReceiver::counter = 0; + + QVERIFY(connect(&sender, &SenderObject::signal1, + p.get(), &DeleteThisReceiver::deleteThis, + static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection))); + QCOMPARE(DeleteThisReceiver::counter, 0); + + sender.emitSignal1(); + QVERIFY(p); + QCOMPARE(DeleteThisReceiver::counter, 0); + + sender.emitSignal1(); + QVERIFY(p); + QCOMPARE(DeleteThisReceiver::counter, 0); + + sender.emitSignal1(); + QVERIFY(p); + QCOMPARE(DeleteThisReceiver::counter, 0); + + QTRY_COMPARE(DeleteThisReceiver::counter, 1); + QVERIFY(!p); + QTest::qWait(0); + QCOMPARE(DeleteThisReceiver::counter, 1); + QVERIFY(!p); + } +} + // Test for QtPrivate::HasQ_OBJECT_Macro static_assert(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value); static_assert(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value); |