diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 59b6b55494..ec4f3dea4c 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1703,8 +1703,10 @@ void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition) void QStateMachinePrivate::maybeRegisterSignalTransition(QSignalTransition *transition) { Q_Q(QStateMachine); - if ((state == Running) && configuration.contains(transition->sourceState())) + if (((state == Running) && configuration.contains(transition->sourceState())) + || (transition->senderObject() && (transition->senderObject()->thread() != q->thread()))) { registerSignalTransition(transition); + } } void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition) @@ -1766,11 +1768,15 @@ void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transitio void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition) { + Q_Q(QStateMachine); int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex; if (signalIndex == -1) return; // not registered - QSignalTransitionPrivate::get(transition)->signalIndex = -1; const QObject *sender = QSignalTransitionPrivate::get(transition)->sender; + // Don't unregister if the sender is on another thread + if (!sender || (sender->thread() != q->thread())) + return; + QSignalTransitionPrivate::get(transition)->signalIndex = -1; QVector &connectedSignalIndexes = connections[sender]; Q_ASSERT(connectedSignalIndexes.size() > signalIndex); Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0); diff --git a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp index b14ab7d1c5..8c67c0ec03 100644 --- a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp @@ -208,6 +208,7 @@ private slots: void signalTransitionNormalizeSignature(); void createSignalTransitionWhenRunning(); void createEventTransitionWhenRunning(); + void signalTransitionSenderInDifferentThread(); }; class TestState : public QState @@ -4827,5 +4828,61 @@ void tst_QStateMachine::createEventTransitionWhenRunning() QTRY_VERIFY(machine.configuration().contains(s4)); } +class SignalEmitterThread : public QThread +{ + Q_OBJECT +public: + SignalEmitterThread(QObject *parent = 0) + : QThread(parent) + { + moveToThread(this); + } + +Q_SIGNALS: + void signal1(); + void signal2(); + +public Q_SLOTS: + void emitSignals() + { + emit signal1(); + emit signal2(); + } +}; + +// QTBUG-19789 +void tst_QStateMachine::signalTransitionSenderInDifferentThread() +{ + QStateMachine machine; + QState *s1 = new QState(&machine); + machine.setInitialState(s1); + + SignalEmitterThread thread; + QState *s2 = new QState(&machine); + s1->addTransition(&thread, SIGNAL(signal1()), s2); + + QFinalState *s3 = new QFinalState(&machine); + s2->addTransition(&thread, SIGNAL(signal2()), s3); + + thread.start(); + QTRY_VERIFY(thread.isRunning()); + + machine.start(); + QTRY_VERIFY(machine.configuration().contains(s1)); + + QMetaObject::invokeMethod(&thread, "emitSignals"); + // thread emits both signal1() and signal2(), so we should end in s3 + QTRY_VERIFY(machine.configuration().contains(s3)); + + // Run the machine again; transitions should still be registered + machine.start(); + QTRY_VERIFY(machine.configuration().contains(s1)); + QMetaObject::invokeMethod(&thread, "emitSignals"); + QTRY_VERIFY(machine.configuration().contains(s3)); + + thread.quit(); + QTRY_VERIFY(thread.wait()); +} + QTEST_MAIN(tst_QStateMachine) #include "tst_qstatemachine.moc"