Big state machines

Ulf Wiger <>
Fri Apr 22 22:09:49 CEST 2005


Den 2005-04-22 18:31:12 skrev Vance Shipley <>:

> Having read your paper "Programming Models for Concurrency"(*)
> I think you have won me over to the pure erlang side.  I have
> some FSMs which require SDL saves and are pretty ugly in gen_fsm.
> Having selective receives will really simplify things for me.

That's great.

> However I'm not convinced that plain_fsm is solving the problem
> the way I want it solved.

That's ok.  (:


> In your plain_fsm documentation you say:
>
>    "... figure out where you really want to handle system messages.
>     Normally, it will suffice to do it in a fairly stable state."
>
> In one of your examples you didn't handle system messages while
> in the transient states waiting for a reply.  Is that a good idea?

I think this is somewhat analogous to having a gen_fsm that
performs a gen_server:call() in one of its callbacks.
While in the gen:await_reply() function (/sub-state), the
process will not respond to system messages. This is ok,
as long as it's a timeout-guarded dialogue, and the process
will eventually come out of it.

[...]

> So I wrote a behaviour module which uses some of the gen_fsm API
> but takes out the main loop.  I built macros to make the handling
> of system messages a little less verbose so that the real code is
> more of a focus.  I think I quite like this.

It looks quite nice.
The plain_fsm approach was a little bit more extreme in
trying minimize the amount of typing needed (and the
chances to mess things up...;) I'm not entirely convinced
that it was worth it.

One of the ways to go wrong is to forget the ?EXIT()
part, which could lead to hanging processes during
shutdown (normally, they are killed after a while.)
It's probably a good idea to always have the ?SYSTEM
and ?EXIT macros present, and writing it symmetrically
like you've done. Then, of course, one could discuss
whether they need to be two separate macros, but
OTOH that might serve to clarify what's happening.

/Uffe


>
> The new sdl_fsm looks like this:
>
> -module(sdl_fsm).
> -export([init/1, terminate/4, code_change/4]).
> -export([s/2, y/2, x/2]).
>
> -include("sys_fsm.hrl").
>
> -record(state, {}).
>
> init(_Args) ->
> 	process_flag(trap_exit, true),
> 	{ok, s, #state{}}.
>
> %%         ___
> %%        (_s_)
> %%          |
> %%       +--+--+
> %%     __|_  __|_
> %%     >_a_| >_b_|
> %%      _|_   _|_
> %%     (_x_) (_y_)
> %%
> s(SysData, StateData) ->
> 	receive
> 		?SYSTEM(s, SysData, StateData);
> 		?EXIT(s, SysData, StateData);
> 		a ->
> 			?DEBUG(a, s, SysData, StateData),
> 			x(SysData, StateData);
> 		b ->
> 			?DEBUG(b, s, SysData, StateData),
> 			y(SysData, StateData);
> 		Msg ->
> 			?DEBUG(Msg, s, SysData, StateData),
> 			s(SysData, StateData)
> 	end.
>
> %%            ___
> %%           (_x_)
> %%             |
> %%       +-----+------+
> %%     __|_  __|_   __|_
> %%     >_a_| >_b_| /_*_/
> %%      _|_   _|_
> %%     (_x_) (_y_)
> %%
> x(SysData, StateData) ->
> 	receive
> 		?SYSTEM(x, SysData, StateData);
> 		?EXIT(x, SysData, StateData);
> 		a ->
> 			?DEBUG(a, x, SysData, StateData),
> 			x(SysData, StateData);
> 		b ->
> 			?DEBUG(b, x, SysData, StateData),
> 			y(SysData, StateData)
> 	end.
>
> %%            ___
> %%           (_y_)
> %%             |
> %%       +-----+------+-----+
> %%     __|_  __|_   __|_  __|_
> %%     >_a_| >_b_| /_c_/ /_d_/
> %%      _|_   _|_
> %%     (_x_) (_y_)
> %%
> y(SysData, StateData) ->
> 	receive
> 		?SYSTEM(y, SysData, StateData);
> 		?EXIT(y, SysData, StateData);
> 		a ->
> 			?DEBUG(a, y, SysData, StateData),
> 			x(SysData, StateData);
> 		b ->
> 			?DEBUG(b, y, SysData, StateData),
> 			y(SysData, StateData);
> 		Msg when Msg /= c, Msg /= d ->
> 			?DEBUG(Msg, y, SysData, StateData),
> 			y(SysData, StateData)
> 	end.
>
>
> %%
> %% sys_fsm callbacks
> %%
> terminate(_Reason, _StateName, _SysData, _StateData) -> ok.
>
> code_change(_OldVsn, StateName, StateData, _Extra) ->
> 	{ok, StateName, StateData}.
>
>





More information about the erlang-questions mailing list