Boost logo

Boost :

From: Darryl Green (darryl.green_at_[hidden])
Date: 2004-06-03 18:02:06

Andreas Huber <ah2003 <at>> writes:

> Darryl Green < <at>> writes:
> > The actual invariants implied by the use of composite states and entry/exit
> > actions are not violated by this modification. Repositioning the transition
> > action within the same overall sequence doesn't break anything, except
> > possibly for the transition action itelf. I say this because the exit
> actions
> > should only depend on all inner exits having run. A transition action can
> > normally depend on all exit actions having been run. However, you only get
> > what you ask for. fsm::transition< event, destination, action_context,
> action
> > > clearly specifies the context as part of the transition action. As the
> > action is a member of that context/state object, it can hardly expect (in
> fact
> > it requires that this not be the case) that state's exit actions to have
> > when the action runs. It all seems logically consistent to me.
> I sort of agree, but I think that it's a bad idea to change the behavior of
> fsm::transition and simple_state<>::transit<>(). What do you say about the
> following:
> template<
> class Event,
> class Destination,
> class InStateReactionContext,
> class void ( InStateReactionContext::*pInStateReaction )(
> const Event & )
> class TransitionContext = unspecified,
> void ( TransitionContext::*pTransitionAction )(
> const Event & ) = unspecified >
> struct compound_transition;
> This is slightly different from your proposal but I think it is clearer and
> therefore more easily understood. All exit actions are always called after
> in-state reaction and before the transition action. IUC, then you wouldn't
> normally need the last two parameters, right?

Right. This works too. I don't really understand your objection to the
transition semantics I proposed. I personally find that proposal perfectly
clear (well I would), and the complication of another type of transition
unnecessary, but it is of course up to you. I'm done on this one, but I would
like to hear other comments - I have to agree with Rob Stewart that it is a bit
too quiet around here (though I'm not convinced that we need fsm "experts" -
I'm sure there is plenty to be offered by those untainted by too much previous
fsm experience).

> > > I'll have to change the implementation to run exit() and destructors when
> an
> > > exception is propagated from the transition action.
> > > Also when this happens we have a major problem if one of the called exit()
> > > functions throws as this would inevitably abort the program.
> >
> > I think you could allow exit() failure,
> Definitely not in this particular situation. This would be the same as
> allowing a destructor to throw. I know I said this before and people rightly
> argued that I'm wrong. But here we really have the same situation because a
> second exception is thrown (from exit()) while we are *unwinding* from a
> exception (thrown from the transition action). Now you suddenly have two
> exceptions. Which one are you going to handle? Since you can only catch one
> them the other one is inevitably lost.

I don't think it is really the same as throwing while unwinding - or are you
saying that the implementation would have to be such that it genuinely was
exactly that?

I envisioned that the transition exception would be caught and converted to an
event. The event would be (must be) queued (in a queue of depth 1) while doing
the exit action. If the exit threw, this would be handled immediately (in line
with the exit exception proposal outlined previously), but the transition
exception would *not* be lost. Only when/if the exit "recovery" completed
sucessfully would the fsm be considered to be in the correct context/state to
handle the original transition action failure event. Only if recovery failed
would we be in real trouble - at that point the fsm would have to
be "abandoned" (ie. terminated without exit).

> I think throwing exit actions make sense only when you propagate *all*
> exceptions to the state machine client.
> As soon as you want to be able to handle exceptions in the state machine
> itself you very quickly run into logical problems similar to the one above,
> *if* you allow exit actions to throw. For example, suppose that in StopWatch
> there is an additional transition from Active to a new state Error. If the
> entry action of Stopped throws, then the resulting exception_thrown event
> leads to a transition to Error. So far so good. What if the exit action of
> Active throws? We have one exception pending (since Error has never been
> reached we never had the chance to actually handle the exception) and now an
> additional exception is thrown. What do we do now? We can bail out at this
> point an propagate the second exception to the state machine client but we
> still have lost the first exception. *Bad* *idea*!

Yes, exit failure is "special". However, I think it can be dealt with as I
outlined above and in the previous post. That is, exit exception event handling
must not fail.

> Note that we don't have this problem if we never attempt to handle the
> exception in the state machine but propagate it out to the client instead
> because we wouldn't call any exit actions in this case (the library currently
> does but it wouldn't if we had exit()). Since exit() actions are not called
> when the state machine is later destructed we won't run into the same problem.
> Thoughts?

I'm not claiming any particular expertise or experience here - my first
reaction, which seems pretty common, is to be very keen to avoid mixing fsms
and exceptions at all. I certainly want at least the option of the simple
behaviour of just abandoning the fsm and propagating the exception. If anyone
else is still interested in giving serious consideration to other options, I'd
be interested in continuing to try to work something out, otherwise, I'm done
on this one too. I don't have any immediate need for anything more elaborate,
and I'd be pretty happy if I never did.

> > The custom reaction isn't
> > ugly - just obfuscating because its semantics aren't visible.
> Ok, I think compound_transition<> should solve this problem rather nicely.

It does.


Boost list run by bdawes at, gregod at, cpdaniel at, john at