From 302e6968f1152d5dee8d5debafb313bd53fa55ff Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Mon, 4 Jun 2012 21:51:04 +0200 Subject: statemachine: Make delayed event posting work from secondary thread postDelayedEvent() and cancelDelayedEvent() are marked as thread-safe in the documentation. Unfortunately, they didn't actually work when called from another thread; they just produced some warnings: QObject::startTimer: timers cannot be started from another thread QObject::killTimer: timers cannot be stopped from another thread As the warnings indicate, the issue was that postDelayedEvent() (cancelDelayedEvent()) unconditionally called QObject::startTimer() (stopTimer()), i.e. without considering which thread the function was called from. If the function is called from a different thread, the actual starting/stopping of the associated timer is now done from the correct thread, by asynchronously calling a private slot on the state machine. This also means that the raw timer id can no longer be used as the id of the delayed event, since a valid event id must be returned before the timer has started. The state machine now manages those ids itself (using a QFreeList, just like startTimer() and killTimer() do), and also keeps a mapping from timer id to event id once the timer has been started. This is inherently more complex than before, but at least the API should work as advertised/intended now. Task-number: QTBUG-17975 Change-Id: I3a866d01dca23174c8841112af50b87141df0943 Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../qstatemachine/tst_qstatemachine.cpp | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'tests/auto') diff --git a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp index c5d33e7437..14a0bed1b4 100644 --- a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp @@ -101,6 +101,7 @@ private slots: void postEvent(); void cancelDelayedEvent(); void postDelayedEventAndStop(); + void postDelayedEventFromThread(); void stopAndPostEvent(); void stateFinished(); void parallelStates(); @@ -1731,6 +1732,55 @@ void tst_QStateMachine::postDelayedEventAndStop() QVERIFY(machine.configuration().contains(s1)); } +class DelayedEventPosterThread : public QThread +{ + Q_OBJECT +public: + DelayedEventPosterThread(QStateMachine *machine, QObject *parent = 0) + : QThread(parent), firstEventWasCancelled(false), + m_machine(machine), m_count(0) + { + moveToThread(this); + QObject::connect(m_machine, SIGNAL(started()), + this, SLOT(postEvent())); + } + + mutable bool firstEventWasCancelled; + +private Q_SLOTS: + void postEvent() + { + int id = m_machine->postDelayedEvent(new QEvent(QEvent::User), 1000); + firstEventWasCancelled = m_machine->cancelDelayedEvent(id); + + m_machine->postDelayedEvent(new QEvent(QEvent::User), 1); + + quit(); + } +private: + QStateMachine *m_machine; + int m_count; +}; + +void tst_QStateMachine::postDelayedEventFromThread() +{ + QStateMachine machine; + QState *s1 = new QState(&machine); + QFinalState *f = new QFinalState(&machine); + s1->addTransition(new EventTransition(QEvent::User, f)); + machine.setInitialState(s1); + + DelayedEventPosterThread poster(&machine); + poster.start(); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + QVERIFY(finishedSpy.isValid()); + machine.start(); + QTRY_COMPARE(finishedSpy.count(), 1); + + QVERIFY(poster.firstEventWasCancelled); +} + void tst_QStateMachine::stopAndPostEvent() { QStateMachine machine; -- cgit v1.2.3