Re: [Boost-users] [Statechart] How to decide between post_eventand process_eventatrun time ?
Yes, it is slightly more work than if there was a state_machine::is_processing() function. I'm just not sure whether your use case is sufficiently common to justify adding such a function. Maybe you could provide more details so that I understand it better?
Sure, I'll try. My state machine is coupled to a connection on which I must comply with some protocol. My layer is a middleware. Each time I have something interesting, like data or events, I call a user-supplied handler on a corresponding method from a react method of the state machine. If the user wants to close the connection I have the problem I described because: - either he decides to close the connection from a callback, which means the state machine is in the middle of processing the event which caused the call to the handler, then I must use post_event - or he decided to close the connection based on an event that didn't come from the connection, in that case I must use process_event. I simplified the problem but the idea is there. If my state machine calls a handler on which I don't have total control, and I supply a few methods that need to be processed using the state machine, then I need to decide between process and post_event. In the simple case I could also provide 2 close methods for the handler, one that must be called in callbacks and one for the other methods of handler. I don't think this is a clean way to solve this issue. Each method in my interface would have 2 versions, and the stability becomes really weak because calling the wrong method at the wrong place results in a crash. At this point, maybe my design is weak, and I shouldn't call the handlers callback directly from the react methods. Instead I could use an event queue to talk to the handler. In the react method I would post events to the queue, and make sure I dequeue it from outside the state machine. But it involves (much) more work... Hope my explanations are clear ;) What is your point of view on this ? Regards, Philippe
Hi Philippe
My state machine is coupled to a connection on which I must comply with some protocol. My layer is a middleware. Each time I have something interesting, like data or events, I call a user-supplied handler on a corresponding method from a react method of the state machine.
It's *usually* not a good idea to make *direct* callbacks to client code from within the state machine, for the reason I'll outline below. Client code can do anything, including calling process_event() again on the same state machine, which is not permitted. I say usually because there are perfectly sane ways to do it: You restrict what said client code can do, e.g. the code could act as a simple plug-in (do some calculation, the result of which is immediately returned to the state machine).
If the user wants to close the connection I have the problem I described because: - either he decides to close the connection from a callback, which means the state machine is in the middle of processing the event which caused the call to the handler, then I must use post_event - or he decided to close the connection based on an event that didn't come from the connection, in that case I must use process_event.
I assume by "user" you mean a human operator of your system. If so, then what you're doing is almost certainly bad practice. In the best case your system is totally unresponsive to any other events while it waits for the callback to return from client code (IIUC, this only happens when the human operator clicks a button or some such). Under certain circumstances you want exactly that, but be aware that in an MT environment you'd have to protect your FSM with a mutex and other threads will simply wait for said mutex to become available before they can proceed to offload their event. This results in bad scalability and takes away any chance for the processing of say emergency events from the outside world.
In the simple case I could also provide 2 close methods for the handler, one that must be called in callbacks and one for the other methods of handler. I don't think this is a clean way to solve this issue. Each method in my interface would have 2 versions, and the stability becomes really weak because calling the wrong method at the wrong place results in a crash.
Right.
At this point, maybe my design is weak, and I shouldn't call the handlers callback directly from the react methods.
Yes, I believe so.
Instead I could use an event queue to talk to the handler. In the react method I would post events to the queue, and make sure I dequeue it from outside the state machine.
This is what you usually do. The library offers support for this with asynchronous_state_machine and fifo_scheduler. While these classes are fairly minimal they might still be the perfect fit for your problem. Instead of calling the user-supplied callbacks directly you'd have fifo_scheduler call them for you with fifo_scheduler::queue_work_item().
But it involves (much) more work...
If the classes I mentioned work for you then I don't think it's really that much more work. Please let me know how that works out. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Quoting Andreas Huber
Hi Philippe
My state machine is coupled to a connection on which I must comply with some protocol. My layer is a middleware. Each time I have something interesting, like data or events, I call a user-supplied handler on a corresponding method from a react method of the state machine.
It's *usually* not a good idea to make *direct* callbacks to client code from within the state machine, for the reason I'll outline below. Client code can do anything, including calling process_event() again on the same state machine, which is not permitted. I say usually because there are perfectly sane ways to do it: You restrict what said client code can do, e.g. the code could act as a simple plug-in (do some calculation, the result of which is immediately returned to the state machine).
If the user wants to close the connection I have the problem I described because: - either he decides to close the connection from a callback, which means the state machine is in the middle of processing the event which caused the call to the handler, then I must use post_event - or he decided to close the connection based on an event that didn't come from the connection, in that case I must use process_event.
I assume by "user" you mean a human operator of your system. If so, then what you're doing is almost certainly bad practice. In the best case your system is totally unresponsive to any other events while it waits for the callback to return from client code (IIUC, this only happens when the human operator clicks a button or some such). Under certain circumstances you want exactly that, but be aware that in an MT environment you'd have to protect your FSM with a mutex and other threads will simply wait for said mutex to become available before they can proceed to offload their event. This results in bad scalability and takes away any chance for the processing of say emergency events from the outside world.
Ok, let me give you more details about what I'm doing. I am writing a library which is used by other devs in the team. Basically, this library handles the internals of a protocol and for the dev who is using it, it provides a class called Handler. This Handler class contains callbacks methods and action methods. For instance, receiveMessage() is called when a new message has been completely received and sendMessage() sends a message. The user will override the callback methods to write its own reaction code to events. My code and the FSM are in a class called Connection. For each tcp connection, there is exactly one instance of Connection linked to one instance of a user class derived from Handler. This means that the user code only interacts with the state machine through methods I wrote. As I said, I am using ACE, and the ACE Reactor (event demultiplexer based on select), which means I am called by the Reactor whenever something happens on the socket. My current design is to process an event in the state machine when I am called by the reactor (this reactor is single threaded and that's what we want here). Depending on the state of my machine I will try to read from the socket, and depending on the result i will eventually post a "MessageComplete" event. The reaction to MessageComplete calls Handler::receiveMessage(). From that point if I want the user to be able to call action methods from within the callbacks, I need to use post_event in them. My problem is that if I want to write an action method "close", and if I use post_event in it, then close can't be called from outside a callback. In this case, I think it is quite natural to call the Handler as soon as we get the information, meaning from the FSM. "natural" may not mean it is the right thing to do...
In the simple case I could also provide 2 close methods for the handler, one that must be called in callbacks and one for the other methods of handler. I don't think this is a clean way to solve this issue. Each method in my interface would have 2 versions, and the stability becomes really weak because calling the wrong method at the wrong place results in a crash.
Right.
At this point, maybe my design is weak, and I shouldn't call the handlers callback directly from the react methods.
Yes, I believe so.
Instead I could use an event queue to talk to the handler. In the react method I would post events to the queue, and make sure I dequeue it from outside the state machine.
This is what you usually do. The library offers support for this with asynchronous_state_machine and fifo_scheduler. While these classes are fairly minimal they might still be the perfect fit for your problem. Instead of calling the user-supplied callbacks directly you'd have fifo_scheduler call them for you with fifo_scheduler::queue_work_item().
Ok. So let me sum that up: I could use a fifo_scheduler to deal with the callbacks. Whenever I want to call the handler, I post a work_item on it. The question is, when do I call the operator() ? My code is single threaded, and reacting to events from the ACE reactor, so I want to use a non blocking FifoWorker, and call operator() somewhere. I could perfectly call it after each process_event. What about if the handler callback generates events for the FSM (which is generally the case actually). This time I actually need to call process_event in my action methods (sendMessage(), close(), ...). If in return, these events need to call the handler again, they will use fifo_scheduler::queue_work_item() while the fifo_scheduler is processing the queue. What will happen here ? Will the fifo_scheduler treat theses new tasks before the operator () returns ? If so, then that's a solution.
But it involves (much) more work...
If the classes I mentioned work for you then I don't think it's really that much more work.
Do I need to write a processor or the fact to directly use fifo_scheduler::queue_work_item() bypasses the processor things ? If I don't need a processor then I guess I only need to write one functor for each callback I want to make to the handler, have a fifo_scheduler, replace my callbacks by calls to fifo_scheduler::queue_work_item() and call the operator() at the right places, right ?
Please let me know how that works out.
Regards,
-- Andreas Huber
Thanks a lot for your help, best regards Philippe
"Philippe DAVID"
If the user wants to close the connection I have the problem I described because: - either he decides to close the connection from a callback, which means the state machine is in the middle of processing the event which caused the call to the handler, then I must use post_event - or he decided to close the connection based on an event that didn't come from the connection, in that case I must use process_event.
I assume by "user" you mean a human operator of your system. If so, then what you're doing is almost certainly bad practice. In the best case your system is totally unresponsive to any other events while it waits for the callback to return from client code (IIUC, this only happens when the human operator clicks a button or some such). Under certain circumstances you want exactly that, but be aware that in an MT environment you'd have to protect your FSM with a mutex and other threads will simply wait for said mutex to become available before they can proceed to offload their event. This results in bad scalability and takes away any chance for the processing of say emergency events from the outside world.
Ok, let me give you more details about what I'm doing. I am writing a library which is used by other devs in the team. Basically, this library handles the internals of a protocol and for the dev who is using it, it provides a class called Handler. This Handler class contains callbacks methods and action methods. For instance, receiveMessage() is called when a new message has been completely received and sendMessage() sends a message. The user will override the callback methods to write its own reaction code to events.
My code and the FSM are in a class called Connection. For each tcp connection, there is exactly one instance of Connection linked to one instance of a user class derived from Handler. This means that the user code only interacts with the state machine through methods I wrote.
Ok, that sounds good. So I take it there's no human operator involved anywhere, right?
As I said, I am using ACE, and the ACE Reactor (event demultiplexer based on select), which means I am called by the Reactor whenever something happens on the socket. My current design is to process an event in the state machine when I am called by the reactor (this reactor is single threaded and that's what we want here).
ST renders many of my earlier comments invalid. Wrong assumption, sorry.
Depending on the state of my machine I will try to read from the socket, and depending on the result i will eventually post a "MessageComplete" event. The reaction to MessageComplete calls Handler::receiveMessage(). From that point if I want the user to be able to call action methods from within the callbacks, I need to use post_event in them. My problem is that if I want to write an action method "close", and if I use post_event in it, then close can't be called from outside a callback.
Right. IIUC, then Handler completely insulates the user code from the state_machine? If so, then you could solve the problem quite centrally in Handler, no? Before you call Handler::receiveMessage() you set a private bool in Handler and ensure that its reset when receiveMessage returns. Moreover, you'd have a private method called e.g. Handler::sendEvent(), which calls either process_event or post_event based on whether the bool is true or not. Not exactly award-winning software engineering, I know. But it would at least solve the problem without duplicating that darn check everywhere.
In this case, I think it is quite natural to call the Handler as soon as we get the information, meaning from the FSM.
I agree as long as Handler subclass objects do not attempt to make potentially blocking calls.
"natural" may not mean it is the right thing to do...
No, provided my impression is correct this time then I'd say natural in this case equals right.
This is what you usually do. The library offers support for this with asynchronous_state_machine and fifo_scheduler. While these classes are fairly minimal they might still be the perfect fit for your problem. Instead of calling the user-supplied callbacks directly you'd have fifo_scheduler call them for you with fifo_scheduler::queue_work_item().
Ok. So let me sum that up: I could use a fifo_scheduler to deal with the callbacks. Whenever I want to call the handler, I post a work_item on it. The question is, when do I call the operator() ? My code is single threaded, and reacting to events from the ACE reactor, so I want to use a non blocking FifoWorker, and call operator() somewhere. I could perfectly call it after each process_event.
No, I was assuming an MT environment. In your situation (ST) fifo_scheduler won't help anything. So please disregard all my advice about fifo_scheduler and asynchronous_state_machine. HTH, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Quoting Andreas Huber
"Philippe DAVID"
wrote in message news:20071120173440.x0inzib4w0sssow0@mail.sogeti.com... If the user wants to close the connection I have the problem I described because: - either he decides to close the connection from a callback, which means the state machine is in the middle of processing the event which caused the call to the handler, then I must use post_event - or he decided to close the connection based on an event that didn't come from the connection, in that case I must use process_event.
I assume by "user" you mean a human operator of your system. If so, then what you're doing is almost certainly bad practice. In the best case your system is totally unresponsive to any other events while it waits for the callback to return from client code (IIUC, this only happens when the human operator clicks a button or some such). Under certain circumstances you want exactly that, but be aware that in an MT environment you'd have to protect your FSM with a mutex and other threads will simply wait for said mutex to become available before they can proceed to offload their event. This results in bad scalability and takes away any chance for the processing of say emergency events from the outside world.
Ok, let me give you more details about what I'm doing. I am writing a library which is used by other devs in the team. Basically, this library handles the internals of a protocol and for the dev who is using it, it provides a class called Handler. This Handler class contains callbacks methods and action methods. For instance, receiveMessage() is called when a new message has been completely received and sendMessage() sends a message. The user will override the callback methods to write its own reaction code to events.
My code and the FSM are in a class called Connection. For each tcp connection, there is exactly one instance of Connection linked to one instance of a user class derived from Handler. This means that the user code only interacts with the state machine through methods I wrote.
Ok, that sounds good. So I take it there's no human operator involved anywhere, right?
No, no human operator here. The term "user" was misleading, sorry about that.
As I said, I am using ACE, and the ACE Reactor (event demultiplexer based on select), which means I am called by the Reactor whenever something happens on the socket. My current design is to process an event in the state machine when I am called by the reactor (this reactor is single threaded and that's what we want here).
ST renders many of my earlier comments invalid. Wrong assumption, sorry.
Depending on the state of my machine I will try to read from the socket, and depending on the result i will eventually post a "MessageComplete" event. The reaction to MessageComplete calls Handler::receiveMessage(). From that point if I want the user to be able to call action methods from within the callbacks, I need to use post_event in them. My problem is that if I want to write an action method "close", and if I use post_event in it, then close can't be called from outside a callback.
Right. IIUC, then Handler completely insulates the user code from the state_machine? If so, then you could solve the problem quite centrally in Handler, no? Before you call Handler::receiveMessage() you set a private bool in Handler and ensure that its reset when receiveMessage returns. Moreover, you'd have a private method called e.g. Handler::sendEvent(), which calls either process_event or post_event based on whether the bool is true or not. Not exactly award-winning software engineering, I know. But it would at least solve the problem without duplicating that darn check everywhere.
Exactly. Hence my initial question "does statechart can give this boolean value" instead of dealing with it myself. It's no big deal actually if the state_machine::is_processing() method does not exist, but it would be convenient to have it. Thank you for your time Andreas, the discution was informative. Best regards Philippe
Philippe DAVID
Exactly. Hence my initial question "does statechart can give this boolean value" instead of dealing with it myself. It's no big deal actually if the state_machine::is_processing() method does not exist, but it would be convenient to have it.
Ok, for now I think that the addition of a state_machine::is_processing() function is not justified. However, I'm prepared to reevaluate when I get additional similar requests. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
participants (2)
-
Andreas Huber
-
Philippe DAVID