I did just get around to give the MSM a first shot and I have say I am
impressed at the neat design, the ease of use, "understandability",
tutorial ... exceptional work. Really great and it helped me a great
deal so far. So thanks a bunch for this teriffic addition to boost.
That's the kind of stuff I love boost for.
There is one question however that puzzles me. How can / do I prevent
state transistions when a transition function throws?
Most transition functions are defined like that:
void so_transition(event::Cause const &e) {
methodCausingAnException();
}
The tutorial only mentions this at the very end of the back-end docs
and this is not clear to me. I have stuff inside my transitions that
may throw and the example text mentiones other valid examples (like
connect() logic). So what am I supposed to do here?
This?
void so_transition(event::Cause const &e) {
try {
methodCausingAnException();
} catch (const std::exception &sex) {
return ???
}
}
The type is void and I can't return anything that would prevent the transition.
Or should I just let the exception pass? What is the correct way?
Suggestions are appreciated,
Stephan
_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users
I know how you feel. MSM is an awesome library. Unfortunately compilers
tend to disagree :)
> There is one question however that puzzles me. How can / do I prevent
> state transistions when a transition function throws?
MSM has two kinds of transition functions, actions and guards. What you
are describing above is an action, and it is not designed to prevent the
transition. You should use a guard. Guards have a boolean return value.
For an example see good_disk_format() function in the SimpleTutorial.cpp
in the MSM docs.
HTH
On Thu, Mar 29, 2012 at 7:07 PM, Juraj Ivančić <juraj....@gmail.com> wrote:
> I know how you feel. MSM is an awesome library. Unfortunately compilers tend
> to disagree :)
Indeed. I am having massive trouble boost.binding the process_event()
function, which I call from within a threaded pool like thingy. It was
so bad, I had to macro wrapper functions around it. Not even the usual
static_cast trick helped. But this is something I can live with,
considering all the benefits.
> MSM has two kinds of transition functions, actions and guards. What you are
> describing above is an action, and it is not designed to prevent the
> transition. You should use a guard. Guards have a boolean return value. For
> an example see good_disk_format() function in the SimpleTutorial.cpp in the
> MSM docs.
I know about this difference. But it doesn't cover what I mean.
Exceptions as in 'exceptional situations'. Consider the socket
example. You would probably like to use a MSM to implement a protocol.
Not when you do a transition from let's say state 'Idle' to state
'Connected' you would be able to make sure everything is ready for
connection in your guard. Like checking if you have your target
endpoint and it is correct and whatever else you need. But still, your
connect() that you would inevitably have to do in the transition may
fail. Or anything else like that. In my implementation I have a lot of
guards now checking whatever could go wrong but there's no way
eliminating all possible exception courses. I have to assume
process_event() will pass the exception to the caller and the
transition will not occur but the docs are not specific about this.
Plus since I called process event indirectly via Q and the wrapper
will not know how to deal with this case. Let alone guarantee RAII in
the transition.
I believe it would be good to be able to bind an exception handler to
each transition much like the guards, and to be able to return true or
false from that handler, depending on what this function thinks if the
exception should prevent transition or not. But this is only an idea.
Cheers,
Stephan
Thanks :) Happy you like it.
> There is one question however that puzzles me. How can / do I prevent
> state transistions when a transition function throws?
>
> Most transition functions are defined like that:
>
> void so_transition(event::Cause const &e) {
>
> methodCausingAnException();
> }
>
> The tutorial only mentions this at the very end of the back-end docs
> and this is not clear to me. I have stuff inside my transitions that
> may throw and the example text mentiones other valid examples (like
> connect() logic). So what am I supposed to do here?
>
> This?
>
> void so_transition(event::Cause const &e) {
> try {
> methodCausingAnException();
> } catch (const std::exception &sex) {
> return ???
> }
> }
>
> The type is void and I can't return anything that would prevent the
> transition.
> Or should I just let the exception pass? What is the correct way?
>
> Suggestions are appreciated,
First of all, I have to give a word of caution. The UML Standard does not
foresee exceptions and they don't mix well with the run-to-completion
algorithm on which state machines rely. Try to avoid them if you can.
This being said, exceptions happen in C++ ;-)
By default, MSM catches them, then calls the exception_caught handler, which
by default is:
template <class FSM,class Event>
void exception_caught (Event const&,FSM&,std::exception& )
{
BOOST_ASSERT(false);
}
Like the no_transition handler, you can overwrite it with your own.
When this is called, the transition is interrupted where it was and ceases
processing. This leaves you in a not very desirable state because you have
been interrupted somewhere in the guard/exit/action/entry chain.
When this happens, I advise you to process to yourself an error event to
handle this gracefully. For example:
template <class FSM,class Event>
void exception_caught (Event const&,FSM& fsm,std::exception& )
{
fsm.process_event(ErrorConnection());
}
But you still are somewhere in the middle of your transition. I think an
elegant option would be to catch the exception in your action, then process
yourself an error event. For example, using the more powerful functor
front-end (providing you the fsm as parameter):
struct so_transition
{
template <class EVT,class FSM,class SourceState,class TargetState>
void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
{
try {
methodCausingAnException();
}
catch (const std::exception &) {
fsm.process_event(ErrorConnection());
}
}
};
You can prevent MSM from catching the exception by activating in your front
end a switch:
typedef int no_exception_thrown;
Then, you get the exception thrown from your process_event call.
In any case, the transition where the exception occurs is terminated and you
don't need to terminate yourself.
> Stephan
HTH,
Christophe
I'd say compilers should be blamed for this ;-) (I'm still wondering how it
can be that compilers make such a poor use of my 6 cores with hyperthreading
when compile a single object file...)
Typically, boost forces compiler writers to improve.
But yes, that's the deal. MSM handles a lot for you at compile time to get
features, the best speed and ease of use at the cost of longer compile time.
> Indeed. I am having massive trouble boost.binding the process_event()
> function, which I call from within a threaded pool like thingy. It was
> so bad, I had to macro wrapper functions around it. Not even the usual
> static_cast trick helped. But this is something I can live with,
> considering all the benefits.
process_event is a template function, which means boost.bind requires you to
use a function pointer.
If what you mean is generating an event from an asio handler, I usually use
the following (simplified) solution (which I plan to add to a coming paper
with msm + asio) (from memory, did not try to compile):
template <class Fsm>
struct handle_write
{
void operator()(const boost::system::error_code& error) const
{
if (error) fsm_->process_event(WriteError());
else fsm_->process_event(WriteDone());
}
Fsm* fsm_;
};
HTH,
Christophe
On Thu, Mar 29, 2012 at 9:46 PM, Christophe Henry
<christoph...@googlemail.com> wrote:
> First of all, I have to give a word of caution. The UML Standard does not
> foresee exceptions and they don't mix well with the run-to-completion
> algorithm on which state machines rely. Try to avoid them if you can.
Yes, mum. ;-)
I know, it's quite a pain and I have to deal with that quite often.
Doing UML interface descriptions in common tools expose this
particular problem all the time. In my opinion however, it is a
problem the UML Standard has, not me. *duck*
> Like the no_transition handler, you can overwrite it with your own.
> When this is called, the transition is interrupted where it was and ceases
> processing. This leaves you in a not very desirable state because you have
> been interrupted somewhere in the guard/exit/action/entry chain.
> When this happens, I advise you to process to yourself an error event to
> handle this gracefully. For example:
>
> template <class FSM,class Event>
> void exception_caught (Event const&,FSM& fsm,std::exception& )
> {
> fsm.process_event(ErrorConnection());
> }
Yes, that can work. I wasn't aware I can post events from within the
state machine. But they are actually queued, as I see. Nice. It's
getting even better. This should be able to deal with the problem.
Only downside I can see is that I'd need an entry in the transition
table for each state. I already had to adjust those MPL settings. But
this will work, thanks.
> You can prevent MSM from catching the exception by activating in your front
> end a switch:
>
> typedef int no_exception_thrown;
>
> Then, you get the exception thrown from your process_event call.
I think your first solution ist better because it allows me better to
reset internal data to reasonable values.
> In any case, the transition where the exception occurs is terminated and you
> don't need to terminate yourself.
OK, thanks. This should be explicitly stated like that in the
tutorial. Or maybe it is and I just couldn't find it. Maybe there's
room for one of those little chapters titled "exceptions".
On a side note, there also could be room for a few words about how do
I position such a thing in an existing (or new) architecture. Like
best or common practice. When I started to work with it I was entirely
unsure about it. I am here in a MVC arch with basically a GUI and a
backend and threaded multiplexing controller (part of the GUI) ->
backend functions and demultiplexing data signals -> GUI changes. As a
first try, I understood the state machine as part of the controller
logic belonging to one functionality and essentially one widget. Which
meant that the SM was posting events to the backend functions in a
threaded Q. This proved no good as the result of this was that a state
could easily be switched without the backend having reacted yet,
creating races and the possibility of acting twice. So I have moved it
back behind the multiplexing, not posting async and so far I think I'm
far better off. Another issue were interactions with other parts of
the applications that send signals about, interfering with things the
SM is now concerned with. Effectively creating need for far more reach
the SM needs to have. Stuff like that. Christophe, if you are
interested, I may be able to churn out a little article and about that
if you are interested.
Cheers,
Stephan
>> Jurai,
>>
>> On Thu, Mar 29, 2012 at 7:07 PM, Juraj Ivančić
>> <juraj....@gmail.com> wrote:
>>> I know how you feel. MSM is an awesome library. Unfortunately
>>> compilers tend
>>> to disagree :)
>
> I'd say compilers should be blamed for this ;-) (I'm still wondering
> how it can be that compilers make such a poor use of my 6 cores with
> hyperthreading when compile a single object file...)
There isn't much inherent parallelism to exploit in C++ compilation of a
single translation unit.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
> If what you mean is generating an event from an asio handler, I
> usually use the following (simplified) solution (which I plan to add
> to a coming paper with msm + asio)
That sounds really cool! Please let us know when you publish!
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
_______________________________________________
You can count on it!
Christophe
I'm not an expert in this field, but this surprises me a bit.
I understand that there is not much parallelism in parsing a file, but
after?
Taking msm as example. Most of the compile time comes from template
instanciations of the function process_event for every event type. Adding
support for a new event adds roughly the same compilation time. Every
process_event has little in common with other process_event instances. What
prevents the compiler from handling each at the same time?
Christophe
> On Thu, Mar 29, 2012 at 9:46 PM, Christophe Henry
> <christoph...@googlemail.com> wrote:
>> First of all, I have to give a word of caution. The UML Standard does not
>> foresee exceptions and they don't mix well with the run-to-completion
>> algorithm on which state machines rely. Try to avoid them if you can.
>
> Yes, mum. ;-)
> I know, it's quite a pain and I have to deal with that quite often.
> Doing UML interface descriptions in common tools expose this
> particular problem all the time. In my opinion however, it is a
> problem the UML Standard has, not me. *duck*
I agree. Such a big Standard and so many holes...
>> Like the no_transition handler, you can overwrite it with your own.
>> When this is called, the transition is interrupted where it was and
>> ceases
>> processing. This leaves you in a not very desirable state because you
>> have
>> been interrupted somewhere in the guard/exit/action/entry chain.
>> When this happens, I advise you to process to yourself an error event to
>> handle this gracefully. For example:
>>
>> template <class FSM,class Event>
>> void exception_caught (Event const&,FSM& fsm,std::exception& )
>> {
>> fsm.process_event(ErrorConnection());
>> }
>
> Yes, that can work. I wasn't aware I can post events from within the
> state machine. But they are actually queued, as I see. Nice. It's
> getting even better. This should be able to deal with the problem.
> Only downside I can see is that I'd need an entry in the transition
> table for each state. I already had to adjust those MPL settings. But
> this will work, thanks.
Or you can use a second region NoError --> Error. This costs you only 1
transition and it is in the spirit of the Standard.
With a msm flag marking Error, you know when your fsm is in error mode.
>> You can prevent MSM from catching the exception by activating in your
>> front
>> end a switch:
>>
>> typedef int no_exception_thrown;
>>
>> Then, you get the exception thrown from your process_event call.
>
> I think your first solution ist better because it allows me better to
> reset internal data to reasonable values.
>
>> In any case, the transition where the exception occurs is terminated and
>> you
>> don't need to terminate yourself.
>
> OK, thanks. This should be explicitly stated like that in the
> tutorial. Or maybe it is and I just couldn't find it. Maybe there's
> room for one of those little chapters titled "exceptions".
Should be possible, yes ;-)
> On a side note, there also could be room for a few words about how do
> I position such a thing in an existing (or new) architecture. Like
> best or common practice. When I started to work with it I was entirely
> unsure about it. I am here in a MVC arch with basically a GUI and a
> backend and threaded multiplexing controller (part of the GUI) ->
> backend functions and demultiplexing data signals -> GUI changes. As a
> first try, I understood the state machine as part of the controller
> logic belonging to one functionality and essentially one widget. Which
> meant that the SM was posting events to the backend functions in a
> threaded Q. This proved no good as the result of this was that a state
> could easily be switched without the backend having reacted yet,
> creating races and the possibility of acting twice. So I have moved it
> back behind the multiplexing, not posting async and so far I think I'm
> far better off. Another issue were interactions with other parts of
> the applications that send signals about, interfering with things the
> SM is now concerned with. Effectively creating need for far more reach
> the SM needs to have. Stuff like that. Christophe, if you are
> interested, I may be able to churn out a little article and about that
> if you are interested.
I surely am interested as I plan a few papers or tutorials on this matter.
msm used with asio, or as the Controller part of a MVC.
Another point of view cannot hurt.
Cheers,
Christophe
>> on Thu Mar 29 2012, "Christophe Henry" <christophe.j.henry-AT-googlemail.com> wrote:
>>
>>>> Jurai,
>>>>
>>>> On Thu, Mar 29, 2012 at 7:07 PM, Juraj Ivančić
>>>> <juraj....@gmail.com> wrote:
>>>>> I know how you feel. MSM is an awesome library. Unfortunately
>>>>> compilers tend
>>>>> to disagree :)
>>>
>>> I'd say compilers should be blamed for this ;-) (I'm still wondering
>>> how it can be that compilers make such a poor use of my 6 cores with
>>> hyperthreading when compile a single object file...)
>>
>> There isn't much inherent parallelism to exploit in C++ compilation of a
>> single translation unit.
>>
>> --
>> Dave Abrahams
>> BoostPro Computing
>> http://www.boostpro.com
>
> I'm not an expert in this field, but this surprises me a bit.
> I understand that there is not much parallelism in parsing a file, but after?
> Taking msm as example. Most of the compile time comes from template instanciations of the function process_event for every event type. Adding support for a new event adds roughly the same compilation time. Every process_event has little in common with other process_event instances. What prevents the compiler from handling each at the same time?
The locking mechanism for ASTs would probably be very complicated if you want to do template instantiation in parallel. It probably could be done, but going that route isn't very attractive because the cores are much better used compiling 6 files in parallel.
Sebastian
Sure but it's still very annoying because in a well-designed application
with little dependency, I work very very often on a single cpp file where my
state machine (or other boost-heavy code) is used and have to wait up to 2mn
that this file is compiled with 1 thread while my other 11 are bored. Then I
wait until linking is done, on 1 thread.
This doesn't look like a very good usage of my cores either ;-)
I hereby make the promise to switch all my applications to the first
standard-conform compiler which compiles this one file on 12 threads thus
saving me hours per year :)
Christophe