summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesus Fernandez <jesus.fernandez@qt.io>2017-12-06 19:46:31 +0100
committerJesus Fernandez <Jesus.Fernandez@qt.io>2017-12-14 16:30:38 +0000
commit345be581007c05164052e27f90fcfaf27a41c743 (patch)
tree6165b95a586bf015b9142b21d3c65def234ba855
parentd48c502ce535c913424440557cdfe7fa6e383dea (diff)
Fix assert when emitting a signal from a different thread
If a signal is emitted more than once in a multithreaded application the QSignalEventGenerator::execute function asserts in the check for a valid signal index. It happens after abandoning the state and all the connections are disconnected. If we have pending signal to be processed the QObject::sender() won't be able to resolve the sender object. Task-number: QTBUG-61463 Change-Id: I9d4b7266c6dddc9ff2e7453b05a6989876ccb332 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--src/corelib/statemachine/qstatemachine.cpp6
-rw-r--r--tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp32
2 files changed, 36 insertions, 2 deletions
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index 2808ba2ced..c2b5afd241 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -3096,10 +3096,12 @@ int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
void QSignalEventGenerator::execute(void **_a)
{
+ auto machinePrivate = QStateMachinePrivate::get(qobject_cast<QStateMachine*>(parent()));
+ if (machinePrivate->state != QStateMachinePrivate::Running)
+ return;
int signalIndex = senderSignalIndex();
Q_ASSERT(signalIndex != -1);
- QStateMachine *machine = qobject_cast<QStateMachine*>(parent());
- QStateMachinePrivate::get(machine)->handleTransitionSignal(sender(), signalIndex, _a);
+ machinePrivate->handleTransitionSignal(sender(), signalIndex, _a);
}
QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent)
diff --git a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp
index 8f0d83ce32..7ea467b6ef 100644
--- a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp
+++ b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp
@@ -101,6 +101,7 @@ Q_OBJECT
public:
SignalEmitter(QObject *parent = 0)
: QObject(parent) {}
+public Q_SLOTS:
void emitSignalWithNoArg()
{ emit signalWithNoArg(); }
void emitSignalWithIntArg(int arg)
@@ -251,6 +252,7 @@ private slots:
void qtbug_46059();
void qtbug_46703();
void postEventFromBeginSelectTransitions();
+ void dontProcessSlotsWhenMachineIsNotRunning();
};
class TestState : public QState
@@ -6658,5 +6660,35 @@ void tst_QStateMachine::postEventFromBeginSelectTransitions()
QVERIFY(machine.isRunning());
}
+void tst_QStateMachine::dontProcessSlotsWhenMachineIsNotRunning()
+{
+ QStateMachine machine;
+ QState initialState;
+ QFinalState finalState;
+
+ struct Emitter : SignalEmitter
+ {
+ QThread thread;
+ Emitter(QObject *parent = nullptr) : SignalEmitter(parent)
+ {
+ moveToThread(&thread);
+ thread.start();
+ }
+ } emitter;
+
+ initialState.addTransition(&emitter, &Emitter::signalWithNoArg, &finalState);
+ QTimer::singleShot(0, [&]() {
+ metaObject()->invokeMethod(&emitter, "emitSignalWithNoArg");
+ metaObject()->invokeMethod(&emitter, "emitSignalWithNoArg");
+ });
+ machine.addState(&initialState);
+ machine.addState(&finalState);
+ machine.setInitialState(&initialState);
+ machine.start();
+ connect(&machine, &QStateMachine::finished, &emitter.thread, &QThread::quit);
+ QSignalSpy signalSpy(&machine, &QStateMachine::finished);
+ QTRY_COMPARE_WITH_TIMEOUT(signalSpy.count(), 1, 100);
+}
+
QTEST_MAIN(tst_QStateMachine)
#include "tst_qstatemachine.moc"