|
Boost Users : |
From: Andreas Huber (ahd6974-spamboostorgtrap_at_[hidden])
Date: 2007-11-20 18:53:04
"Philippe DAVID" <philippe.a.david_at_[hidden]> wrote in message
news:20071120173440.x0inzib4w0sssow0_at_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?
> 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.
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net