简体   繁体   中英

Qt QStateMachine Sync Problems: Initial State not set on Started Signal

So I am trying to understand a problem with Qt's QStateMachine and I'm hoping someone can help explain why this is happening. I'm very interested in the fundamental understanding of QStateMachine rather than just the fix.

First consider the state machine with states A, and B and event 1. Event 1 brings you from A to B. A is the initial state.

Specifically this is for maintaining neighbors. In device XI get a message where a neighbor Y says hello. This causes neighbor X to malloc a neighbor state machine for this new neighbor Y. This makes the neighbor state machine and then calls QStateMachine::start( );

Now after this state machine is started I need to continue processing this hello message. So at first I was doing:

QStateMachine::start( ) ;
emit event 1 ;

My understanding is this won't work because start is an async call and so the state machine is not in its initial start until after start is done. That leads me to my first question.

1) So the state machine start gets placed in the qapp event queue but isn't emit an async call as well? Won't the event 1 be placed in the event queue after the start and so wouldn't that mean we will be in the initial state? Or is the emit not an async call?

Thinking this was the problem I changed my code a little by connecting a function to the state machine started signal. I then changed my code to queue up the events if the state machine is not started and I process this queue of pending events (and emit them to the state machine) after the started signal is called.

Well it turns out the initial state is STILL not set when I get started signal. eg QStateMachine::configuration( ).contains( initialstate ) == false. This leads me to my second and bigger question.

2) Why when the started signal is emitted am I not in the initial state.

The sequence of events here is:

  1. Create state machine
  2. Set initial state
  3. Start state machine
  4. Receive event 1
  5. Since not started queue event 1
  6. Started signal rxed
  7. Process event 1
  8. Since is unknown state do nothing
  9. Receive event 1
  10. Process event 1
  11. Now in state A. transition to state B.

The sequence should be:

  1. Create state machine
  2. Set initial state
  3. Start state machine
  4. Receive event 1
  5. Since not started queue event 1
  6. Started signal rxed
  7. Process event 1
  8. Now is state A. transition to state B.
  9. Receive event 1
  10. Process event 1
  11. Now is state B. Do nothing.

Or event better I wish I didn't have to queue the event. I wish I could do this:

  1. Create state machine
  2. Set initial state
  3. Start state machine
  4. Receive event 1
  5. Process event 1
  6. Now is state A. transition to state B.
  7. Receive event 1
  8. Process event 1
  9. Now is state B. Do nothing.

Transition signals are only connected after the state changes (in QStateMachinePrivate::registerSignalTransition ) and the connection is not a queued connection:

bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator,
                                   signalEventGenerator->metaObject()->methodOffset());

For the "event 1" to be received, the machine must already be in a state that react to that signal. Even it was a queued connection, the slot would be queued but only after the signal was received, which it isn't since there is no connection yet at that point.

To solve your problem, you can wait for the machine is in "state A" before emitting the signal:

machine->start();
qApp->processEvents();
emit event1();

Or you can delay the signal emission and queue it after the other already queued operations:

machine->start();
QTimer::singleShot(0, emitter, SIGNAL(event1()));
// or
QMetaObject::invokeMethod(emitter, "event1", Qt::QueuedConnection);

The started signal is emitted before the initial state is set (according to source code), which can be helpful if you have initialization to do before any state is set. If you need to wait for the initial state, you can use the signal QState::entered .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM