[Statechart] Question regarding state_machine event handling
Hi, I'm currently working with the the statechart library in version 1.42.0 and have some difficulties with the way the simple state_machine processes its events. For my work I need several independent state machines, which communicate with each other using process_event/post_event. For various reasons I do not want to use the asynchroneous_state_machine - mostly because my application is built around a global event/message handler and I do not want to use multiple threads with their necessary synchronization. It basically works because in my hierarchy state_machine<>::process_event() is called only in one direction, while in the "opposite" direction (or if unsure) state_machine<>::post_event() is being used. E.g. A calls B->process_event(), B calls C->process_event(), but C calls B->post_event() and B calls A->post_event(). This works so far, and since the process_event() function also processes events posted from within its execution context I do not see any problems with this. However, what I could really make use of are two functions: a) a query function to check if there are any events in the state machine queue (if additional post_event() calls have been made outside the process_event() context); and b) a process_event() function that does not need an event argument, but only processes the queue. Currently I use a "dummy" event for this, but this is rather ugly. Does anybody know why such functions are not available? For b) there is a function named "process_queued_events()" that does exactly what I need, but unfortunately this function is declared private.For a), a simple "eventQueue_.empty()" wrapper would suffice. An even bigger problem for me is that process_event() executes the event argument _first_. I'm rather used to state machines that process their events in-order regardless of the way they got queued up, so I had expected that process_event() first executes the events already in the queue before processing the given event (in other words, the given event argument is only put to the end of the queue initially, and then the queue is processed). Any ideas why the state machine works this way? What is the rationale for this behaviour? Thanks, Arne
For various reasons I do not want to use the asynchroneous_state_machine - mostly because my application is built around a global event/message handler and I do not want to use multiple threads with their necessary synchronization.
Just a little remark: all asynchroneous_state_machine's can use the same fifo_scheduler running in a single thread. The "outer world" would communicate to them by posting events, thus no synchronization is needed inside the async.machines. (And according to your description, it really seems that async.machines would better fit you requirements.)
Hi Arne
For various reasons I do not want to use the asynchroneous_state_machine - mostly because my application is built around a global event/message handler and I do not want to use multiple threads with their necessary synchronization.
As Igor has already pointed out, you don't have to use multiple threads. In fact, it's perfectly sensible to use multiple asynchronous_state_machine<> subclass objects hosted in one fifo_scheduler in a program that only has exactly one thread. Moreover, even under circumstances where you have very limited control over a global message queue and its handlers, you should still be able to use fifo_scheduler, as you can instruct it to simply return from its operator() as soon as its internal queue runs empty. So, in your scenario, if you can ensure that fifo_scheduler::operator() is called periodically, you *can* use async machines.
It basically works because in my hierarchy state_machine<>::process_event() is called only in one direction, while in the "opposite" direction (or if unsure) state_machine<>::post_event() is being used. E.g. A calls B->process_event(), B calls C->process_event(), but C calls B->post_event() and B calls A->post_event().
state_machine<>::post_event() is not intended to be called from code "outside" of the machine, but only transition actions, which is why post_event is documented as being protected. Unfortunately, it is public in the code because it has to be called from other class templates and template friend support was very spotty at the time this was implemented. Anyway, I certainly could have done more to prevent calls from outside (e.g. with an assert and/or better documentation). May I ask you to enter a ticket for this? Thanks & Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Hi Andreas,
Andreas Huber
For various reasons I do not want to use the asynchroneous_state_machine - mostly because my application is built around a global event/message handler and I do not want to use multiple threads with their necessary synchronization.
As Igor has already pointed out, you don't have to use multiple threads. In fact, it's perfectly sensible to use multiple asynchronous_state_machine<> subclass objects hosted in one fifo_scheduler in a program that only has exactly one thread. Moreover, even under circumstances where you have very limited control over a global message queue and its handlers, you should still be able to use fifo_scheduler, as you can instruct it to simply return from its operator() as soon as its internal queue runs empty. So, in your scenario, if you can ensure that fifo_scheduler::operator() is called periodically, you *can* use async machines.
That's good to know - I did not realize that. From the tutorial and the reference I had the impression that a separate thread would be needed for every other state machine, and I also wanted to go the easy way first ;-) I'll give it a try; according to the reference all functionality I need seem to be available.
It basically works because in my hierarchy state_machine<>::process_event() is called only in one direction, while in the "opposite" direction (or if unsure) state_machine<>::post_event() is being used. E.g. A calls B->process_event(), B calls C->process_event(), but C calls B->post_event() and B calls A->post_event().
state_machine<>::post_event() is not intended to be called from code "outside" of the machine, but only transition actions, which is why post_event is documented as being protected. Unfortunately, it is public in the code because it has to be called from other class templates and template friend support was very spotty at the time this was implemented. Anyway, I certainly could have done more to prevent calls from outside (e.g. with an assert and/or better documentation). May I ask you to enter a ticket for this? Sure, the ticket is opened. Now that I read this I see that this function is indeed declared protected in the reference, but not in the source.
Regarding my other questions about the synchroneous state machine: is it really intended that a new event is processed before other events already queued? Of course this might be related to the post_event() issue above - if events cannot be queued up from outside without being processed immediately, the queue cannot contain any pending events. Right? Regards, Arne
That's good to know - I did not realize that. From the tutorial and the reference I had the impression that a separate thread would be needed for every other state machine, and I also wanted to go the easy way first ;-) I'll give it a try; according to the reference all functionality I need seem to be available.
Ok, let me know if you have any trouble. Async machines differ quite a bit from sync ones.
state_machine<>::post_event() is not intended to be called from code "outside" of the machine, but only transition actions, which is why post_event is documented as being protected. Unfortunately, it is public in the code because it has to be called from other class templates and template friend support was very spotty at the time this was implemented. Anyway, I certainly could have done more to prevent calls from outside (e.g. with an assert and/or better documentation). May I ask you to enter a ticket for this? Sure, the ticket is opened.
Thanks.
Regarding my other questions about the synchroneous state machine: is it really intended that a new event is processed before other events already queued? Of course this might be related to the post_event() issue above - if events cannot be queued up from outside without being processed immediately, the queue cannot contain any pending events. Right?
Correct, if post_event is only ever called from transition actions then the posted events are processed as the last step of process_event, so there cannot be any event ordering issues. Regards, Andreas
participants (3)
-
Andreas Huber
-
Arne Babnik
-
Igor R