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>
bb10
Jesus Fernandez 2017-12-06 19:46:31 +01:00 committed by Jesus Fernandez
parent d48c502ce5
commit 345be58100
2 changed files with 36 additions and 2 deletions

View File

@ -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)

View File

@ -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"