[erlang-questions] gen_fsm - calling state function clauses directly (question from Learn You Some Erlang chapter 20)

Richard McLean rmcl2303@REDACTED
Fri Aug 12 10:16:34 CEST 2016


Hi,

I’m just learning erlang/OTP so please excuse me if I ask a newbie question.

I’m currently reading "Learn You Some Erlang For Great Good” by Fred Hébert.

Regarding Chapter 20 “The Count of Applications”…

The author presents an OTP application that uses gen_fsm to recursively grep through a directory tree using one worker process per file/regex.

There are a couple of instances where the code transitions from one state to another (and simulates triggering an event) by calling a state function directly rather than via a {next_state, StateName, StateData} return tuple.

For example (page 326) :

dispatching(done, Data) ->
    %% This is a special case. We cannot assume that all messages have NOT
    %% been received by the time we hit 'done'. As such, we directly move to
    %% listening/2 without waiting for an external event.
    listening(done, Data).

listening(done, #data{regex=Re, refs=[]}) -> % all received!
    [io:format("Regex ˜s has ˜p results˜n", [R,C]) || {R, C} <- Re],
    {stop, normal, done};
listening(done, Data) -> % entries still missing
    {next_state, listening, Data}.

And (page 328) :

handle_event({complete, Regex, Ref, Count}, State,
 Data = #data{regex=Re, refs=Refs}) ->
    {Regex, OldCount} = lists:keyfind(Regex, 1, Re),
    NewRe = lists:keyreplace(Regex, 1, Re, {Regex, OldCount+Count}),
    NewData = Data#data{regex=NewRe, refs=Refs--[Ref]},
    case State of
        dispatching ->
            {next_state, dispatching, NewData};
        listening ->
            listening(done, NewData)
    end.

*dispatching and listening are the two states implemented in this gen_fsm with state functions of matching name.

I’m specifically referring to the  "listening(done, Data)” call that is the last (tail) call of the dispatching(done, Data) clause, and the listening(done, NewData) call that is one of the possible last (tail) calls of the handle_event({complete, Regex, Ref, Count}, State, Data = #data{regex=Re, refs=Refs}) clause.

I understand that the author is trying to simultaneously change state and execute the correct clause without having to send any events, and that just returning a {next_state, listening, StateData} tuple would only change the state… and to execute a clause in the listening state would then require matching a particular event (ie. “done”), which would mean having to send a “done” event, and other events or messages in the queue may need to be processed in the meantime which could leave the FSM in a state other than “listening” (ie. "dispatching”) which might lead to some oscillating behaviour.

My main question is…

Is this approach recommended or not ?  
(and if not are there any other recommended ways to achieve the same thing ?)

Does calling state functions directly break any OTP principles or lead to any strange behaviour (eg. the main gen_fsm loop not knowing which state it’s in +/- any corruption of centrally held StateData of the main server loop) ?

I couldn’t find anything about direct state function calls after extensive googling.

The gen_fsm docs don’t mention anything about it either way - it only mentions state transitions via state function return tuples, and execution of state functions via matching events received while in that state.

Could someone who is familiar with Erlang/OTP give some advice ?

Regards

Richard






More information about the erlang-questions mailing list