From 8efcfda41f98af51c5d7ab72b5bc38926e9c3f11 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 1 Aug 2012 12:59:28 +0200 Subject: statemachine: Really fix signal transition handling in multi-threaded setup Commit f9a17d7f0f02f7af849afdf653a763ffdaf78a1b fixed it for the case where the sender object is in a different thread at transition setup time. However, it still didn't work if either the sender object or the state machine was moved to a different thread at some later time, before the machine was started. Therefore: Bite the sour grape and traverse all the machine's transitions when the machine is being started, registering those signal transitions whose sender objects are in other threads. This will increase the machine's startup time (proportional to the number of transitions), but at least it works in all known scenarios, meaning we don't have to document weird restrictions regarding the order in which the user's operations have to be done. Task-number: QTBUG-19789 Change-Id: I5f1dd1321994e49635f52be65cf56d2678ed1253 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/corelib/statemachine/qstatemachine.cpp | 32 ++++++++++++++++++++++++------ src/corelib/statemachine/qstatemachine_p.h | 1 + 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'src/corelib/statemachine') diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 1fe7f96b3c..b3e91c1ac0 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1335,6 +1335,28 @@ void QStateMachinePrivate::clearHistory() } } +/*! + \internal + + Registers all signal transitions whose sender object lives in another thread. + + Normally, signal transitions are lazily registered (when a state becomes + active). But if the sender is in a different thread, the transition must be + registered early to keep the state machine from "dropping" signals; e.g., + a second (transition-bound) signal could be emitted on the sender thread + before the state machine gets to process the first signal. +*/ +void QStateMachinePrivate::registerMultiThreadedSignalTransitions() +{ + Q_Q(QStateMachine); + QList transitions = rootState()->findChildren(); + for (int i = 0; i < transitions.size(); ++i) { + QSignalTransition *t = transitions.at(i); + if ((t->machine() == q) && t->senderObject() && (t->senderObject()->thread() != q->thread())) + registerSignalTransition(t); + } +} + void QStateMachinePrivate::_q_start() { Q_Q(QStateMachine); @@ -1346,6 +1368,8 @@ void QStateMachinePrivate::_q_start() externalEventQueue.clear(); clearHistory(); + registerMultiThreadedSignalTransitions(); + #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": starting"; #endif @@ -1703,8 +1727,8 @@ void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition) void QStateMachinePrivate::maybeRegisterSignalTransition(QSignalTransition *transition) { Q_Q(QStateMachine); - if (((state == Running) && configuration.contains(transition->sourceState())) - || (transition->senderObject() && (transition->senderObject()->thread() != q->thread()))) { + if ((state == Running) && (configuration.contains(transition->sourceState()) + || (transition->senderObject() && (transition->senderObject()->thread() != q->thread())))) { registerSignalTransition(transition); } } @@ -1771,14 +1795,10 @@ 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 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; connectionsMutex.lock(); diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index cf20ee31ca..c8d09586b4 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -175,6 +175,7 @@ public: void maybeRegisterSignalTransition(QSignalTransition *transition); void registerSignalTransition(QSignalTransition *transition); void unregisterSignalTransition(QSignalTransition *transition); + void registerMultiThreadedSignalTransitions(); #ifndef QT_NO_STATEMACHINE_EVENTFILTER void maybeRegisterEventTransition(QEventTransition *transition); void registerEventTransition(QEventTransition *transition); -- cgit v1.2.3