Big state machines

Ulf Wiger (AL/EAB) ulf.wiger@REDACTED
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
semantics.

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
    true ->
      really_handle_message(Msg, State, Q);
    false ->
      case is_this_an_error(Msg, Q) of
        true ->
          erlang:fault(...);
        false ->
          NewState = 
            case defer_or_discard(Msg, Q) of
              defer ->
                defer(Msg, Q, State);
              discard ->
                State
            end
      end
  end.

... 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,
don't.

/Uffe

-----Original Message-----
From: owner-erlang-questions@REDACTED
[mailto:owner-erlang-questions@REDACTED]On Behalf Of Joel Reymont
Sent: den 15 april 2005 10:56
To: Erlang Users' List
Subject: Big state machines


Dear Erlangers,

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.

    Thanks, Joel

-- 
http://wagerlabs.com/tech





More information about the erlang-questions mailing list