summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qnamespace.h3
-rw-r--r--src/corelib/global/qnamespace.qdoc7
-rw-r--r--src/corelib/kernel/qobject.cpp115
-rw-r--r--src/corelib/kernel/qobject_p.h6
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp591
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);