'after' in gen_server

orbitz <>
Fri Mar 24 04:24:17 CET 2006


The reason I don't like 1 that much is because my code looks something 
like:
handle_cast({irc_connect, Nick}, #state{state=connecting, dict=Dict, 
irclib=Irclib} = State) ->
     % Do connect stuff
     join_channels(Irclib, dict_proc:fetch(join_on_connect, Dict)),
     {noreply, State#state{nick=Nick, state=idle}};
handle_cast({stop, _}, #state{state=connecting} = State) ->
     {stop, stop, State};
handle_cast({irc_message, {_, "PONG", _}}, #state{state=pong, 
pong_timeout=Ref} = State) ->
     {ok, cancel} = timer:cancel(Ref),
     {noreply, State#state{state=idle, pong_timeout=undefined}};
handle_cast(irc_closed, #state{irclib=Irclib} = State) ->
     irc_lib:connect(Irclib),
     {noreply, State#state{state=connecting}};
handle_cast({irc_message, {_, "PING", [Server]}}, #state{irclib=Irclib} 
= State) ->
     irc_lib:pong(Irclib, Server),
     {noreply, State};
handle_cast({irc_message, {_, "KICK", [Channel, Nick, _]}}, 
#state{irclib=Irclib, nick=Nick} = State) ->
     irc_lib:join(Irclib, Channel),
     {noreply, State};

So it gets ugly to return a timeout in all of these places.

On Mar 23, 2006, at 10:01 PM, Rick Pettit wrote:

> On Thu, Mar 23, 2006 at 08:59:45PM -0500,  wrote:
>> The only problem I have with that is it sounds very error prone.  What
>> if I forget a timeout?  The more callbacks i implement the uglier 
>> it'll
>> get, won't it?
>
> The way I see it you have two options:
>
>   1) Use the gen_server behaviour and its timeout mechanism--this 
> involves
>      returning an additional timeout parameter from:
>
>        Module:init/1
>        Module:handle_call/3
>        Module:handle_cast/2
>        Module:handle_info/2
>
>      You will service the idle timeout in Module:handle_info/2. This 
> is pretty
>      simple, IMO, but I agree it is overkill if you don't otherwise 
> require a
>      gen_server.
>
>   2) Don't use gen_server (or any OTP behaviour)--rewrite your process 
> as
>      a simple server with receive loop. Now that you have direct 
> access to
>      the receive loop, you can plug in your 'after' clause with idle 
> timeout
>      and deal with the server going idle there.
>
> If you insist on using a gen_server than I can't see the point in not 
> going
> with (1)--anything else is going to require more server state, more 
> code, and
> will ultimately be more error prone.
>
> -Rick
>
>> On Mar 23, 2006, at 4:35 PM, Rick Pettit wrote:
>>
>>> On Thu, Mar 23, 2006 at 04:26:22PM -0500,  wrote:
>>>> I want to do it from all callbacks.  Basically I want to make it so 
>>>> if
>>>> the server is idle for X amount of time, then that means something 
>>>> is
>>>> wrong and I need to do something to figure out what is wrong.
>>>
>>> Then have your callbacks set a gen_server "idle timeout" by way of 
>>> the
>>> gen_server timeout mechanism, and service timeouts in handle_info/2.
>>>
>>> It is as simple as passing a timeout value in the return value from
>>> your
>>> callbacks--Module:init/1, Module:handle_call/3, Module:handle_cast/2,
>>> etc.
>>>
>>> -Rick
>>>
>>>> On Mar 23, 2006, at 11:04 AM, Rick Pettit wrote:
>>>>
>>>>> On Thu, Mar 23, 2006 at 10:42:04AM -0500,  wrote:
>>>>>> I am reworking a bit of code into gen_server pattern.  In this 
>>>>>> code
>>>>>> I
>>>>>> have a typical loop construct.  In this I want to perform an 
>>>>>> action
>>>>>> if
>>>>>> no messages have been receive in a certain amount of time, to do
>>>>>> this
>>>>>> I
>>>>>> simply have after sometimeout ->.  Is there any equivalence of 
>>>>>> this
>>>>>> in
>>>>>> gen_server?
>>>>>
>>>>> If I understand you correctly, all you need to do is set a timeout
>>>>> when
>>>>> returning from one of the gen_server callback functions.
>>>>>
>>>>> For example, instead of returning {ok,State} from Module:init/1,
>>>>> return
>>>>> {ok,State,TimeoutMs}. You can do this for all the gen_server 
>>>>> callback
>>>>> routines (at least handle_call/handle_cast, etc).
>>>>>
>>>>> This effectively sets a timer which will expire if no message is
>>>>> received
>>>>> by the gen_server before TimeoutMs has elapsed. If/when the timer
>>>>> does
>>>>> expire, you receive a 'timeout' message in Module:handle_info/2.
>>>>>
>>>>> -Rick
>>>>>
>>>>> P.S. Since gen_server abstracts the server receive loop, I (as a
>>>>> general rule
>>>>>    of thumb) *never* call receive in a gen_server callback module.
>>>>>
>>>>>> It has been suggested that I use a timer and record when the last
>>>>>> message has come in and when the timer signals check against that
>>>>>> message.  This seems a poor solution.  Another suggestions was to
>>>>>> use
>>>>>> a
>>>>>> gen_fsm with a timeout, but this seem a bit much just to get an
>>>>>> 'after'
>>>>>> mechanism. Any other suggestions?  Perhaps my disregard of gen_fsm
>>>>>> is
>>>>>> a
>>>>>> bit hasty?
>>>>>
>>>>
>>>
>>
>




More information about the erlang-questions mailing list