gen_fsm design question

19 views
Skip to first unread message

Garrett Smith

unread,
Oct 21, 2009, 3:41:58 PM10/21/09
to Chicago Erlang Users Group
I'm using a gen_fsm to monitor a registration state between two nodes.
The state transitions are straight forward: disconnected -> connected
-> registered. If the registration state changes (I'm using gen_fsm
timeouts to poll at regular intervals), it reverts the state
accordingly and restarts the cycle.

The question/problem I have is related to handle_info/3. If anyone
sends a message to the gen_fsm, it can derail the process by resetting
the timeout timer. If I don't return a timeout from handle_info, the
process falls out of its polling/retry cycle.

I'd like to be able to say, "ignore the message" and maintain the
state (including the timeout timer) of the process. But per the docs,
there isn't a no_reply option supported by gen_fsm.

What's the Erlang Tao here? I'm tempted to call exit in handle_info
and crash and burn, but I haven't seen that pattern used in OTP for
unhandled messages. The alternative would be to return the current
state with a generic timeout to get the process back into its routine.
This feel defensive though.

Thoughts?

Martin Logan

unread,
Oct 24, 2009, 3:22:41 PM10/24/09
to ce...@googlegroups.com
Why are messages being sent to the fsm that arrive in the handle info
clause - are they planned or unplanned?

Garrett Smith

unread,
Oct 24, 2009, 3:41:38 PM10/24/09
to ce...@googlegroups.com
One could be planned - nodedown messages, though I'm not using that
approach at the moment.

The ones I'm worried about are the unplanned messages.

At the moment I'm calling erlang:error for anything I don't expect.

Martin Logan

unread,
Oct 24, 2009, 3:55:48 PM10/24/09
to ce...@googlegroups.com
There are no unplanned messages :) Don't handle them. Not handling
them is just like making an assertion, something we do in Erlang
everytime we say something like

{ok, Value} = func(Args)

If we don't get back ok value the process blows up and the supervisor
restarts the process and any other dependent processes in a
predictable manner to reestablish a working base state. These actions
are seen in the sasl logs and perhaps even the error logs. If the
problem affects quality of service too much then it must be resolved
and cause eliminated whether that cause be an errant message sender or
something else. If the QOS is not effected greatly then so be it -
let it be.

As for the planned messages - don't :) You can handle this if you
really want to by tracking time elapsed since last timeout by adding a
start time every time you reset your timeout in the correct case.
Then track how long it has been since that start time and subtract
that from the timeout you return in the handle info case. Much uglier
than just not using the handle info case in this process and
architecting another place to receive your node down messages if
possible.

Hope that helps,
Martin

Garrett Smith

unread,
Oct 24, 2009, 4:11:43 PM10/24/09
to ce...@googlegroups.com
On Sat, Oct 24, 2009 at 2:55 PM, Martin Logan <martin...@gmail.com> wrote:
>
> There are no unplanned messages :)  Don't handle them.

Some of the other OTP behaviors have an explicit 'no_reply' return
value, indicating such. Not so with gen_fsm. As far as not handling
it, does something like this look right?

handle_event(Event, StateName, State) ->
{unexpected_event, Event, StateName, State).

> As for the planned messages - don't :)

That's where I came down on this. Too much of a hassle to properly
restore the state. I think if I was concerned about handling something
like a nodedown msg, I'd create another process, registering it with
net_kernel and then notify the gen_fsm via its public API.

> Hope that helps,

Absolutely -- thanks!

Martin Logan

unread,
Oct 24, 2009, 5:56:28 PM10/24/09
to ce...@googlegroups.com
On Sat, Oct 24, 2009 at 3:11 PM, Garrett Smith <g...@rre.tt> wrote:
>
> On Sat, Oct 24, 2009 at 2:55 PM, Martin Logan <martin...@gmail.com> wrote:
>>
>> There are no unplanned messages :)  Don't handle them.
>
> Some of the other OTP behaviors have an explicit 'no_reply' return
> value, indicating such. Not so with gen_fsm. As far as not handling
> it, does something like this look right?
>
> handle_event(Event, StateName, State) ->
>    {unexpected_event, Event, StateName, State).


In terms of not handling it, I mean truly not handling it. make all
your clauses explicit such that any unexpected messages will generate
a function clause exception.

On the noreply front it just means the message was sent asynchronously
or that it was sent synchronously but that a reply will be done
explicitly with the gen:reply function or one of its descendants.

Garrett Smith

unread,
Oct 24, 2009, 6:06:34 PM10/24/09
to ce...@googlegroups.com
On Sat, Oct 24, 2009 at 4:56 PM, Martin Logan <martin...@gmail.com> wrote:
>
> On Sat, Oct 24, 2009 at 3:11 PM, Garrett Smith <g...@rre.tt> wrote:
>>
>> On Sat, Oct 24, 2009 at 2:55 PM, Martin Logan <martin...@gmail.com> wrote:
>>>
>>> There are no unplanned messages :)  Don't handle them.
>>
>> Some of the other OTP behaviors have an explicit 'no_reply' return
>> value, indicating such. Not so with gen_fsm. As far as not handling
>> it, does something like this look right?
>>
>> handle_event(Event, StateName, State) ->
>>    {unexpected_event, Event, StateName, State).
>
>
> In terms of not handling it, I mean truly not handling it.  make all
> your clauses explicit such that any unexpected messages will generate
> a function clause exception.

These are part of the gen_fsm callbacks though -- which means
compilation warnings if they're not implemented. It's better to suffer
the warnings than to have noop functions?

Martin Logan

unread,
Oct 24, 2009, 6:10:05 PM10/24/09
to ce...@googlegroups.com
You can make the generic catch all variables that they default to in
most templates to something like 'unhandled'. This will allow you to
know when you get unwanted messages. Why are you worried about
unhandled messages - why would someone send these to you. Are you not
wrapping all your message sending in the API of the same module? No
one should be doing Pid ! Msg type stuff to your process. Your entire
protocol should be wrapped by functions in the same module as you
spawn your process. This will keep you out of harms way - does that
make sense?

Garrett Smith

unread,
Oct 24, 2009, 6:17:37 PM10/24/09
to ce...@googlegroups.com
On Sat, Oct 24, 2009 at 5:10 PM, Martin Logan <martin...@gmail.com> wrote:
>
> You can make the generic catch all variables that they default to in
> most templates to something like 'unhandled'.  This will allow you to
> know when you get unwanted messages.  Why are you worried about
> unhandled messages - why would someone send these to you. Are you not
> wrapping all your message sending in the API of the same module? No
> one should be doing Pid ! Msg type stuff to your process. Your entire
> protocol should be wrapped by functions in the same module as you
> spawn your process.  This will keep you out of harms way - does that
> make sense?

Yup. I use the pattern where all messages are wrapped up behind a
public API and uses the OTP module functions for actually sending
messages. I was wondering about the catch all functions that OTP makes
you use and how "assertive" one should be with unexpected conditions.

I think I got it now :)

Thanks!

Reply all
Reply to author
Forward
0 new messages