Hi Andreas, Thanks for your response!
As far as I can tell, the phenomenon you're observing would maifest itself in the same way in synchronous machines as it does in your async machine. What follows therefore only considers the properties that are common to both types of machines.
Yes, of course, async machine is stuck in the topic name and in my repro just for "historical reasons" :).
1. Each state machine has a general event queue. 2. Logically, there is a *per-state* deferred event queue, which stores incoming events that are deferred while the machine resides in a given state. Whenever a state is left, all deferred events for that state are moved into the general event queue. 3. As the last step of state_machine::process_event, all events in the general event queue are processed.
IIUC, what you're observing can be explained with the 3 points above, right?
Right. To be more exact, it's because in (2) "all deferred events for that state" are *enqueued* into the general event queue, i.e. pushed in fifo manner. Although, intuitively I'd say that these deferred events have "higher priority" than those in the queue, because they were posted earlier for sure. So this is exactly the question: wasn't it more appropriate to push them to front of the "queue" (well, not real queue then)?
In your example, When s1 is left, instances of ev3to4_1 and ev3to4_2 are moved from the deferral queue to the general event queue, although the next state does nothing else but defer them also. IMO, the root of the problem lies here. A better solution would be to put both s1 and s2 into an outer state (e.g. s) and have that defer ev3to4_1 and ev3to4_2. s1 would then only defer ev2to3 and transition to s2. Similarly, s2 would then only transition to s3.
It's a nice workaround for this specific case (which is merely a minimal reproduction), but if the main idea behind it is to avoid multiple deferrals of the same event, then I'm afraid it would be quite complicated to apply it to my real FSM, because it has rather long "pipeline" of states, some of them are already nested. It looks like this (indentations mean substates): struct Disconnected; struct Connecting; struct Connected; --struct Unauthenticated; --struct Authenticating; --struct Authenticated; ----struct Stopped; ----struct Starting; ----struct Started; ...and so on... The main events (are queued in this order): struct EvConnect; struct EvAuthenticate; struct EvStart; struct EvGetData; EvConnect moves Disconnected-->Connecting, EvAuthenticate moves Unauthenticated-->Authenticating, and so on. These transitions generate some actions, and later "acknowledging" events come: EvConnect::Ok advances Connecting-->Connected, EvConnect::Fail rolls back Connecting-->Disconnected, and so on. So if everything goes well, EvGetData should be deferred until Started state, where it's processed. OTO, if some failure occurs, EvGetData would be processed as "error" in one of "stable" states (Disconnected, Unauthenticated, etc) - it cannot be silently ignored for some reasons. This means that EvGetData should never appear in the event queue before the events, which must precede it, because then it would be processed immediately as "error". Unfortunately, this is exactly what happens now: an "acknowledging" event enters between EvStart and EvGetData (which is legitimate), causes transition Authenticating-->Authenticated, and the 2 events come reversed to Stopped state. I'm really sorry to bother you with my FSM details, but maybe you can see some obvious workaround that I miss:). Thank you!