gen_fsm

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

gen_fsm

Karel Van Oudheusden
Hello again,


It's firday afternoon and I'm getting a bit impatient with the code (see
attachment). It's a simple instantiation of the 'gen_fsm' behaviour.
Could somebody please tell me what I am doing wrong?


Besides that, I have a more fundamental question:  If I am not mistaken,
the implementation of gen_fsm implies that one or more processes are
spawned.  My question is whether it is a good idea to keep this issue
transparent from the user who is using the behaviour.  Doesn't this
influence the performance of his implementation?  How efficient is a
behavriour with respect to inheritance in an onject oriented program in
which the same design pattern is implemented?


any comments are welcome,
KVO.
-------------- next part --------------
-module(fsmkvo).
-behaviour(gen_fsm).

-import(lists, [append/2]).

%% --------------------------------------------------------------------
%% CALLBACK functions:
%% --------------------------------------------------------------------
%% Initialisation routine:
init(Args) ->
        {ok, sleeping, [1]}.

%% Termination routine:
terminate(Reason,State,Data) ->
        ok.

%% Stop the FSM no matter what state it is in:
handle_event(stopit, AnyState, TheStateData) ->
        {stop, stopInvocation, []};

%% Print the State Data of the Current State of the FSM:
handle_event(printState, AnyState, TheStateData) ->
        io:format(" Currently in state  ~w~n", [AnyState]),
        io:format(" with state data: ~w~n", [TheStateData]),
        io:format(" ~n"),
        {next_state, AnyState, TheStateData}.

%% The STATE MACHINE:

sleeping({eat}, Data) ->
        %% ... actions for change from sleep to eat ...
        {next_state, eating, lists:append(Data,[2])}.

eating({work}, Data) ->
        {next_state, working, lists:append(Data,[3])}.

working({sleep}, Data) ->
        {next_state, sleeping, lists:append(Data,[4])};

working({eat}, Data) ->
        {next_state, eating, lists:append(Data,[5])}.
%% --------------------------------------------------------------------
%% CALLBACK end.
%% --------------------------------------------------------------------

%% ----------------------------------------------------------------------------------------------------------------

%% --------------------------------------------------------------------
%% interface routines:
%% --------------------------------------------------------------------
start() ->
        gen_fsm:start({local,fsmkvo}, fsmkvo, [], []).

letsEat() ->
        gen_fsm:send_event(fsmkvo, {eat}).

letsWork() ->
        gen_fsm:send_event(fsmkvo, {work}).

letsSleep() ->
        gen_fsm:send_event(fsmkvo, {sleep}).

printState() ->
        gen_fsm:send_all_state_event(fsmkvo, printState).

stop() ->
        gen_fsm:send_all_state_event(fsmkvo, stopit).
%% --------------------------------------------------------------------
%% interface end.
%% --------------------------------------------------------------------




Reply | Threaded
Open this post in threaded view
|

gen_fsm

Francesco Mazzoli-2
Hi,

>  It's firday afternoon and I'm getting a bit impatient with the code (see
> attachment). It's a simple instantiation of the 'gen_fsm' behaviour.
> Could somebody please tell me what I am doing wrong?

you forgot to export your client functions, your states and your call back
functions. Next time you are having problems, start the debugger, trace
compiling the code and then spawn your process.


>  Besides that, I have a more fundamental question:  If I am not
> mistaken, the implementation of gen_fsm implies that one or more
> processes are spawned.

It depends on your implementation and needs. In that you are registering
your process locally, you may only have one instance of it in your node. If you are not registering it, you may have
one or more, and access it with the Pid.


>  My question is whether it is a good idea to keep this issue
 > transparent from the user who is using the behaviour.  Doesn't this
 > influence the performance of his implementation?

When dealing with processes (or behaviors) it is always a good idea to
hide the fact that they are processes in a functional interface. It gives you more freedom when updating and
maintaining the code. Readability and ease of maintenance should always go before performance.

Regards,
Francesco
--
Francesco Cesarini
http://www.erlang-consulting.com



Reply | Threaded
Open this post in threaded view
|

gen_fsm

Vance Shipley-2
In reply to this post by Karel Van Oudheusden
> It's firday afternoon and I'm getting a bit impatient with the code (see
> attachment). It's a simple instantiation of the 'gen_fsm' behaviour.
> Could somebody please tell me what I am doing wrong?

As has already been pointed out, you needed to export the functions which
need to be called by outside modules.  This includes those that the gen_fsm
module must call.  I like to do it this way:

% export the callbacks common to gen_fsm behaviours
-export([init/1, code_change/4, handle_event/3,
      handle_sync_event/4, handle_info/3, terminate/3]).

% export the callbacks for (gen_fsm behaviour) states in this module
-export([sleeping/2, eating/2, working/2]).

% export the API functions
-export([start/0, letsEat/0, letsWork/0, letsSleep/0,
        printState/0, stop/0]).


> Besides that, I have a more fundamental question:  If I am not mistaken,
> the implementation of gen_fsm implies that one or more processes are
> spawned.  My question is whether it is a good idea to keep this issue
> transparent from the user who is using the behaviour.  Doesn't this
> influence the performance of his implementation?  How efficient is a
> behavriour with respect to inheritance in an onject oriented program in
> which the same design pattern is implemented?


In fact there is only one process involved.  The documentation can be a
little misleading as it shows what appears to be a message flow between
processes:

Callback module                            gen_fsm
----------------                            -------
gen_fsm:start_link                 ----->   start a new fsm process
Module:init/1                     <-----
                                            looping

gen_fsm:send_event                ----->
Module:StateName/2                <-----

.....


However the introduction to this diagram says:

        "The relationship between the generic interface functions
         (and received messages) and the callback functions can be
         illustrated as follows:"

Really this just shows the flow of execution which involves both your
module (fsmkvo.erl) and the gen_fsm.erl module which implements the
behaviour.  gen_fsm.erl contains the main loop of the resulting program.

You end up with one process which is executing code from both modules.
There are not any messages being sent other than when your other modules
execute gen_fsm:send_event/2 to send an event (message) into the state
machine.

The above diagram does however muddy the waters a bit 'cause it puts
the 'gen_fsm:send_event' under the 'Callback Module' column.  This
isn't how you've done it.  In your case you have an API function
(e.g. letsEat/0) which will be called by a user (e.g. the shell user).

Here we're running a shell to play with your code. The PID of the
shell process is <0.39.0>.

        1> self().
        <0.39.0>

We use your API function to start the gen_fsm process which now runs
as PID <0.36.0> (and which you have registered with the name "fsmkvo").

        2> fsmkvo:start().
        {ok,<0.36.0>}

Now we use your API function to change the state:

        3> fsmkvo:letsEat().
        ok

When we do this the shell process sources your module (fsmkvo.erl) to
get the code for the start function.  The shell process now executes
this code.  You defined letsEat/0 as:

        letsEat() ->
                gen_fsm:send_event(fsmkvo, {eat}).

In turn the shell now calls gen_fsm:send_event/2 which is defined as:

        send_event(Name, Event) ->
                Name ! {'$gen_event', Event},
                ok.

So the shell process is really doing:

        3> fsmkvo ! {'$gen_event', {eat}}.

It is sending a message to your process.  Since you included the gen_fsm
behaviour your process includes both your code (fsmkvo.erl) and the code
in gen_fsm.erl.  The gen_fsm.erl module contains the code to handle
receiving messages.  The gen_fsm code runs your code to handle the
implementation specific stuff.


        -Vance

Vance Shipley
Motivity Telecom Inc.
Tel: +1 519 579 5816