Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to do something periodically in Erlang?

I have a process that needs to do some work every fifteen seconds. I'm currently doing it like this:

-behavior(gen_server).  interval_milliseconds ()-> 15000. init()->     {ok,       _State = FascinatingStateData,      _TimeoutInterval = interval_milliseconds ()     }.  %% This gets called automatically as a result of our handlers %% including the optional _TimeoutInterval value in the returned %% Result handle_info(timeout, StateData)->     {noreply,       _State = do_some_work(StateData),       _TimeoutInterval = interval_milliseconds ()     }. 

This works, but it's extremely brittle: if I want to teach my server a new message, when I write any new handler function, I have to remember to include the optional timeout interval in its return value. That is, say if I'm handling a synchronous call, I need to do this:

%% Someone wants to know our state; tell them handle_call(query_state_data, _From, StateData)->     {reply, StateData, _NewStateData = whatever (), interval_milliseconds ()}; 

instead of

%% Someone wants to know our state; tell them handle_call(query_state_data, _From, StateData)->     {reply, StateData, _NewStateData = whatever ()}; 

As you might guess, I've made that very mistake a number of times. It's nasty, because once the code handles that query_state_data message, the timeouts no longer get generated, and the whole server grinds to a halt. (I can "defibrillate" it manually by getting a shell on the machine and sending a "timeout" message by hand, but ... eww.)

Now, I could try to remember to always specify that optional Timeout parameter in my Result value. But that doesn't scale: I'll forget someday, and will be staring at this bug once again. So: what's a better way?

I don't think I want to write an actual loop that runs forever, and spends most of its time sleeping; that seems counter to the spirit of OTP.

like image 993
offby1 Avatar asked Apr 20 '09 18:04

offby1


1 Answers

Use timer:send_interval/2. E.g.:

-behavior(gen_server).  interval_milliseconds()-> 15000. init()->     timer:send_interval(interval_milliseconds(), interval),     {ok, FascinatingStateData}.  %% this clause will be called every 15 seconds handle_info(interval, StateData)->     State2 = do_some_work(StateData)     {noreply, State2}. 
like image 139
gleber Avatar answered Oct 25 '22 23:10

gleber