0

I have to implement erlang gen_server processes which are alive for hours. But after timeout gen_server process should be killed. These processes are started dynamically so dynamical supervising is used. The idea is to use timer:apply_after() on process init. So init of gen_server process looks like that

init(Time) -> 
   timer:apply_after(Time, my_supervisor, kill_child, [self()]),
   % do other init things
   ok.

I'm a bit new to erlang so the question is does this approach is fine or it has some drawbacks? Is there a better solution?

Thanks!

3 Answers3

3

I would do something different:

init([]) ->
    erlang:send_after(Time, self(), timeout_shutdown),
    {ok, #state{}}.

handle_info(timeout_shutdown, State) ->
    {stop, normal, State};
...

This way, the process gracefully shuts down itself, without the need for the supervisor to kill it. Better, you can declare the child as transient in the supervisor, so it doesn't get restarted.

I GIVE CRAP ANSWERS
  • 18,739
  • 3
  • 42
  • 47
2

You might want to consider using erlang:send_after/3 and react to the message in your gen_server with handle_info:

Creating timers using erlang:send_after/3 and erlang:start_timer/3 is much more efficient than using the timers provided by the timer module. The timer module uses a separate process to manage the timers, and that process can easily become overloaded if many processes create and cancel timers frequently (especially when using the SMP emulator).

The functions in the timer module that do not manage timers (such as timer:tc/3 or timer:sleep/1), do not call the timer-server process and are therefore harmless.

http://www.erlang.org/doc/efficiency_guide/commoncaveats.html

helios35
  • 1,577
  • 1
  • 14
  • 27
  • Thank you for the reply. But I have an issue with process termination when handling it through the message. So the issue is when process is dead it still remains in supervisor state and it's impossible to start the process with the same name because supervisor says {error,already_present}, Is there any workaround? Thank again! – Konstantin Shamko Apr 01 '14 at 20:25
0

you can try this code: the process worker1 shutdown itself every second, the process worker2 shutdown itself every two seconds. You just need load the two beam, and run super:start_link(). super:main(). in erl shell.

This is supervosor:

-module(super).
-behaviour(supervisor).
%% API
-export([start_link/0]).
-export([stop/0]).
-export([main/]).
%% Supervisor callbacks
-export([init/1]).

-define(SERVER, ?MODULE).

start_link() ->
    supervisor:start_link({local, ?SERVER}, ?MODULE, []).

init([]) ->
    RestartStrategy = simple_one_for_one,
    MaxRestarts = 1000,
    MaxSecondsBetweenRestarts = 3600,

    SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},

    Restart = permanent,
    Shutdown = 2000,
    Type = worker,

    AChild = {worker, {worker, start_link, []},
          Restart, Shutdown, Type, [worker]},
    {ok, {SupFlags, [AChild]}}.

main() ->
    add_worker(worker1, 1000),
    add_worker(worker2, 2000).

add_worker(WorkerName, Time) when is_atom(WorkerName)->
    supervisor:start_child(?SERVER, [WorkerName, Time]).
stop() ->
    exit(whereis(?SERVER), shutdown).

This is gen_server:

-module(worker).
-behaviour(gen_server).
%% API
-export([start_link/2]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
     terminate/2, code_change/3]).

-define(SERVER, ?MODULE). 

-record(state, {}).

start_link(WorkerName, Time) ->
    io:format("server: ~p start!~n", [WorkerName]),
    gen_server:start_link({local, WorkerName}, ?MODULE, [WorkerName, Time], []).

init([WorkerName, Time]) ->
    erlang:send_after(Time, self(), {WorkerName, timeout_shutdown}),
    {ok, #state{}}.


handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.


handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({WorkerName, timeout_shutdown}, State) ->
    io:format("server: ~p timeout_shutdown!~n", [WorkerName]),
    {stop, normal, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.
BlackMamba
  • 10,054
  • 7
  • 44
  • 67
  • But if i start worker dynamically like supervisor:start_child(Sup, {some_name, {MFA}, ...}). When this worker dies after a timeout i'm not able to start another worker with some_name name as it was not removed for supervisor's state. and that is the problem – Konstantin Shamko Apr 02 '14 at 10:04
  • I have modified my code. you can see it, when the worker dies, the supervisor will start it agian, you needn't restart it by yourself. – BlackMamba Apr 02 '14 at 12:11