Big state machines
Ulf Wiger (AL/EAB)
Fri Apr 15 12:08:16 CEST 2005
Without having seen the code, I'd wager a guess
that you're suffering from the standard problem
of writing complex FSMs using FIFO, run-to-completion
The problem I'm referring to occurs when the FSM
has to coordinate with different actors; the FIFO
semantics force an arbitrary merge+serialization of
otherwise unsynchronized message streams, and since
your coordinating FSM cannot choose which message
source to handle next, it has to handle all
possible message sequences.
In order to handle this, you basically have to
write your FSM as a stack machine, where you keep
track of what you were doing, and what message you
expect to arrive next. In order to survive, you
soon realise that you have to draw an event-state
matrix. For each cell in your matrix, you have to
at least ask yourself whether the history matters
(i.e. the sequence of events that got you there).
If so, your event-state matrix will contain case
statements for certain cells, where you check for
various pre-conditions. The same questions will
of course also have to be present in your code,
so the programming pattern often becomes:
handle_event(Msg, State) ->
Q = what_was_I_doing(State),
case message_is_valid(Msg, Q) of
really_handle_message(Msg, State, Q);
case is_this_an_error(Msg, Q) of
case defer_or_discard(Msg, Q) of
defer(Msg, Q, State);
... or something to that effect. You get the point.
Perhaps this is not at all relevant in your case.
Difficult to say without having seen the source.
The remedy? Use plain Erlang state machines with
selective receive. You will still have to think
about whether you can break up your state machines
into several smaller ones. The proof is in your
code. If it simplifies the code - do it. Otherwise,
[mailto:]On Behalf Of Joel Reymont
Sent: den 15 april 2005 10:56
To: Erlang Users' List
Subject: Big state machines
I'm implementing my poker logic as a gen_fsm behavior. I'm finding that
my FSM is getting a bit unwieldy, though, what with all the tests I have
to write for different state transitions, etc.
I'm wondering if there's an idiomatic way of splitting big FSMs into
smaller chunks. Should I move the processing code into different modules
and leave just invocations in the FSM callback module?
I suppose this will help with reusing states in other state machines but
would like to hear how people have been tackling this problem.
More information about the erlang-questions