I want to start a gen_server that additionally, will perform one action every minute.
What is the best way to schedule that?
I want to start a gen_server that additionally, will perform one action every minute.
What is the best way to schedule that?
You have two easy alternatives, use timer:send_interval/2
or erlang:send_after/3
. send_interval
is easier to setup, while send_after
(when used in the Erlang module) is more reliable since it is a built-in function, see the Efficiency Guide.
Using send_after
also ensures that the gen_server
process is not overloaded. If you were using the send_interval
function you would get a message regardless if the process can keep up or not. With send_after
being called just before the return in handle_info
you only schedule a new message once you handled the previous one. If you want more accurate time tracking you can still schedule a send_after
with the time set dynamically to something lower than ?INTERVAL
(or even 0) to catch up.
I would recommend something along the following lines in your gen_server
:
-define(INTERVAL, 60000). % One minute
init(Args) ->
... % Start first timer
erlang:send_after(?INTERVAL, self(), trigger),
...
handle_info(trigger, State) ->
... % Do the action
... % Start new timer
erlang:send_after(?INTERVAL, self(), trigger),
...
Instead of trigger
you could send something with a state if it is needed, like {trigger, Count}
or something.
To precisely control the timer, you may want to use erlang:start_timer
, and save each timer reference you have created.
erlang:start_timer
has a tiny difference with erlang:send_after
, see http://www.erlang.org/doc/man/erlang.html#start_timer-3 and http://www.erlang.org/doc/man/erlang.html#send_after-3
Example use case:
init(Args) ->
...
TRef = erlang:start_timer(?INTERVAL, self(), trigger),
State = #state{tref = TRef},
...
handle_info({timeout, _Ref, trigger}, State) ->
%% With this cancel call we are able to manually send the 'trigger' message
%% to re-align the timer, and prevent accidentally setting duplicate timers
erlang:cancel(State#state.tref),
...
TRef = erlang:start_timer(?INTERVAL, self(), trigger),
NewState = State#state{tref = TRef},
...
handle_cast(stop_timer, State) ->
TRef = State#state.tref,
erlang:cancel(TRef),
%% Remove the timeout message that may have been put in our queue just before
%% the call to erlang:cancel, so that no timeout message would ever get
%% handled after the 'stop_timer' message
receive
{timeout, TRef, _} -> void
after 0 -> void
end,
...
There is actually a built-in mechanism within gen_server to accomplish the same thing.
If the third element of response tuple of the init, handle_call, handle_cast or handle_info methods in the gen_server
is an integer, a timeout
message wil be sent to the process after that period of time in millisecs... which should be handled using handle_info. For eg :
init(Args) -> ... % Start first timer {ok, SomeState, 20000}. %% 20000 is the timeout interval handle_call(Input, From, State) -> ... % Do something ... % Do something else {reply, SomeState, 20000}. %% 20000 is the timeout interval handle_cast(Input, State) -> ... % Do something ... % Do something else {noreply, SomeState, 20000}. %% 20000 is the timeout interval %% A timeout message is sent to the gen_server to be handled in handle_info %% handle_info(timeout, State) -> ... % Do the action ... % Start new timer {noreply, SomeState, 20000}. %% "timeout" can be sent again after 20000 ms
timeout
is not intended for periodic execution. It is intended to launch some action or terminate when there nothing happen in this period. This timeout is terminated by each action even by some system (sys
, proc_lib
, ...) actions. Shortly, using timeout is discouraged to rely on except some "maintenance" stuff, like automatic termination or cleanups. –
Trickle If an integer time-out value is provided, a time-out occurs unless a request or a message is received within Timeout milliseconds.
This will fail if gen_server is receiving messages. –
Traps There is also the timer
module, which could be used.
http://erldocs.com/R14B02/stdlib/timer.html?i=8&search=timer#cancel_timer/1
© 2022 - 2024 — McMap. All rights reserved.