Big state machines

Ulf Wiger ulf@REDACTED
Tue Apr 19 06:54:18 CEST 2005

Den 2005-04-19 07:44:24 skrev Vance Shipley <vances@REDACTED>:

> On Mon, Apr 18, 2005 at 10:03:59PM +0200, Ulf Wiger (AL/EAB) wrote:
> }
> }  .... Sadly, with UML and SDL, the default behaviour is to throw
> }  away messages that arrive in the wrong state, and have not been
> }  marked deferrable in that state. This makes it difficult to
> }  maintain large programs, unless the tool in question supports
> }  another default (everything deferrable unless otherwise specified.)
> I think it has to be this way.  You are always going to get late
> arrivals or other instances of messages you are not interested in.
> If it weren't for these semantics you'd need to account for every
> message the FSM handles in every state.  You'd also run the constant
> risk of overflowing the mailbox.  Saving is the exceptional condition
> done when you need to wait for a particular set of messages (e.g. ACK)
> before proceeding.

I may read your message wrong, but the problem with the UML approach
is that you cannot easily say, for a given state: "match these
expected messages, but implicitly refer any other message". This
is the semantics needed for all transient states, since a transient
state may _never_ discard a message it doesn't recognize.

In Erlang, you don't have to worry much about transient states,
because they can be hidden behind a function call. The classic
example is gen_server:call/3. It's not too surprising that UML
specifies a special SynchronousCall method, because given the
message passing semantics of UML, it's nearly impossible to
model a generic SynchronousCall. But for all transient states
that do not fit the SynchronousCall pattern, you're in deep
trouble (granted -- I think most transient states _do_ fit the
SynchronousCall pattern.)

> On the other hand what I would like would require you to explicitly
> throw away unwanted messages.  Ideally what I would like is just a
> gen_fsm which had the sematics that if an event handler didn't match
> the first event in the queue it kept trying until it had tried all
> messages and then kept trying for new messages.  This way you would
> write normal states as:
> idle(offhook, StateData) ->
>     ...
>     {next_state, dialtone};
> idle(Event, StateData) ->
> 	{next_state, idle}.
> When you want a state which saves signals you leave out the catchall
> clause.

Then the dispatcher would have to trigger on function_clause as
meaning "save this message". Would it not be better to have an
explicit "defer" callback? This would essentially mimic the
semantics of RoseRT - since there is no unsend() function in
Erlang, the dispatcher would have to manage a 'defer' queue
in memory. Unless you can make it transparent, there would
also have to be a recall() function (a la RoseRT), and then
you really have a mess on your hands. It would then be better
for the dispatcher to keep a FIFO queue in memory, and always
run all messages through it. unsend() would then be equivalent
to undo() (that is, keep the old Queue variable around, and
revert to it if necessary) -- but only as long as one hasn't
pulled another message from the Erlang message queue (which
is an irreversible action).


More information about the erlang-questions mailing list