Commits

Fred T-H committed b256108

Changed the supervisor structure of Chut as a whole

The current way to manage supervision is to have each user of the
system living on his own. There is no top supervisor and terminating a
given user is done by killing its supervisor.

This new revision adds a new top-level supervisor (chut_user_supersup)
which will create and terminate every user process. Adding this supervisor
will allow making sure no rogue processes are living and will allow to
start and stop chut as a whole with ease.

Comments (0)

Files changed (5)

src/chut_SUITE.erl

 %% Reason = term()
 %%--------------------------------------------------------------------
 init_per_suite(Config) ->
+    {ok, Pid} = chut_user_supersup:start_link(),
+    unlink(Pid),
     Config.
 %%--------------------------------------------------------------------
 %% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
 %% Config0 = Config1 = [tuple()]
 %%--------------------------------------------------------------------
 end_per_suite(_Config) ->
+    exit(whereis(chut_user_supersup), kill),
     ok.
 %%--------------------------------------------------------------------
 %% Function: init_per_group(GroupName, Config0) ->
 %% times out in due time.
 %%--------------------------------------------------------------------
 chut_user_timeout(_Config) ->
-    [Id, Sleep, Expected] = [id, 500, [{monitor, id}, id, {manager,id}]],
+    Id = make_ref(),
+    [Sleep, Expected] = [500, [{monitor, Id}, Id, {manager,Id}]],
     IsProc = fun(Name) -> lists:member(Name, global:registered_names()) end,
     chut_user:start(Id, Sleep, 0),
     true = lists:all(IsProc, Expected),
     timer:sleep(Sleep*3),
-    io:format("procs: ~p~n", [global:registered_names()]),
     false = lists:any(IsProc, Expected),
     ok.
 
 %% timing out, and that once it stops listening, the user does time out.
 %%--------------------------------------------------------------------
 chut_user_subscribe(_Config) ->
-    [Id, Sleep, Expected] = [id, 500, [{monitor, id}, id, {manager,id}]],
+    Id = make_ref(),
+    [Sleep, Expected] = [500, [{monitor, Id}, Id, {manager,Id}]],
     IsProc = fun(Name) -> lists:member(Name, global:registered_names()) end,
     chut_user:start(Id, Sleep, 0),
     {ok, Handler} = chut_user:subscribe(Id),
 chut_client_aware_crashed_user(_Config) ->
     A = make_ref(),
     {ok, H} = chut_client:connect(A),
-    chut_user:terminate(A,kill),
+    chut_user:terminate(A),
     try chut_client:listen(A,H) of
         _ -> throw(no_dead_signal)
     catch

src/chut_user.erl

 -module(chut_user).
 
 %% API
--export([start/3, terminate/2, subscribe/1, unsubscribe/2, subscribers/1,
+-export([start/3, terminate/1, subscribe/1, unsubscribe/2, subscribers/1,
          message/3, relay/3, history/1]).
 
 %%--------------------------------------------------------------------
     case global:whereis_name(UserId) of
         undefined ->
             %% start it here
-            chut_user_sup:start_link(UserId, TimeOut),
+            ChildSpec = {UserId,
+                         {chut_user_sup, start_link, [UserId, TimeOut]},
+                         transient,
+                         TimeOut,
+                         supervisor,
+                         [chut_user_sup]},
+            supervisor:start_child(chut_user_supersup,ChildSpec),
             chut_user_manager:add_handler(UserId, chut_user_dispatch_handler, UserId),
             chut_user_manager:add_handler(UserId, chut_user_history_handler, {UserId,HistoryLimit}),
             io:format("started user process for id ~p~n",[UserId]),
 %% Description: kills the supervisor which should trickle down to the
 %% children.
 %%--------------------------------------------------------------------
-terminate(UserId, Reason) ->
-    Pid = global:whereis_name(UserId),
-    io:format("Killing user ~p. Reason: ~p~n",[UserId, Reason]),
-    erlang:exit(Pid, kill).
+terminate(UserId) ->
+    supervisor:terminate_child(chut_user_supersup, UserId),
+    supervisor:delete_child(chut_user_supersup, UserId).
 
 %%--------------------------------------------------------------------
 %% Function: subscribe(UserId) -> {ok, HandlerId}

src/chut_user_monitor.erl

     %% maybe store messages?
     io:format("timing out~n"),
     {id, UserId} = proplists:lookup(id, State),
-    chut_user:terminate(UserId, timeout), % take care, circular dependency!
+    chut_user:terminate(UserId), % take care, circular dependency!
     {stop, normal, State};
 waiting(_Event, State) -> % catch-all to avoid crashing
     io:format("invalid timeout msg:~p~n",[_Event]),

src/chut_user_sup.erl

 -export([start_link/2]).
 
 start_link(UserId, TimeOut) ->
-    {ok,Pid} = supervisor:start_link({global, UserId}, ?MODULE, [UserId, TimeOut]),
-    unlink(Pid).
+    supervisor:start_link({global, UserId}, ?MODULE, [UserId, TimeOut]).
 
 
 %%--------------------------------------------------------------------

src/chut_user_supersup.erl

-%% this is a supervisor of all the users' supervisors
+%%%==================================================================
+%%% Supervises all user supervisors on a given node.
+%%% Acts as an entry point to add users and then remove them (this
+%%% might be done from a user's internal FSM.)
+%%%==================================================================
 -module(chut_user_supersup).
+-behaviour(supervisor).
+
+%% supervisor callback
+-export([init/1]).
+%% API
+-export([start_link/0]).
+
+start_link() ->
+    supervisor:start_link({local, chut_user_supersup}, ?MODULE, []).
+
+init([]) ->
+    {ok, {{one_for_one, 1, 15000}, []}}.