123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934 |
- %% ``The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
- %% compliance with the License. You should have received a copy of the
- %% Erlang Public License along with this software. If not, it can be
- %% retrieved via the world wide web at http://www.erlang.org/.
- %%
- %% Software distributed under the License is distributed on an "AS IS"
- %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- %% the License for the specific language governing rights and limitations
- %% under the License.
- %%
- %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
- %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
- %% AB. All Rights Reserved.''
- %%
- %% $Id$
- %%
- -module(supervisor).
- -behaviour(gen_server).
- %% External exports
- -export([start_link/2,start_link/3,
- start_child/2, restart_child/2,
- delete_child/2, terminate_child/2,
- which_children/1,
- check_childspecs/1]).
- -export([behaviour_info/1]).
- %% Internal exports
- -export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]).
- -export([handle_cast/2]).
- -define(DICT, dict).
-
- -record(state, {name,
- strategy,
- children = [],
- dynamics = ?DICT:new(),
- intensity,
- period,
- restarts = [],
- module,
- args}).
- -record(child, {pid = undefined, % pid is undefined when child is not running
- name,
- mfa,
- restart_type,
- shutdown,
- child_type,
- modules = []}).
- -define(is_simple(State), State#state.strategy =:= simple_one_for_one).
- behaviour_info(callbacks) ->
- [{init,1}];
- behaviour_info(_Other) ->
- undefined.
- %%% ---------------------------------------------------
- %%% This is a general process supervisor built upon gen_server.erl.
- %%% Servers/processes should/could also be built using gen_server.erl.
- %%% SupName = {local, atom()} | {global, atom()}.
- %%% ---------------------------------------------------
- start_link(Mod, Args) ->
- gen_server:start_link(supervisor, {self, Mod, Args}, []).
-
- start_link(SupName, Mod, Args) ->
- gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []).
-
- %%% ---------------------------------------------------
- %%% Interface functions.
- %%% ---------------------------------------------------
- start_child(Supervisor, ChildSpec) ->
- call(Supervisor, {start_child, ChildSpec}).
- restart_child(Supervisor, Name) ->
- call(Supervisor, {restart_child, Name}).
- delete_child(Supervisor, Name) ->
- call(Supervisor, {delete_child, Name}).
- %%-----------------------------------------------------------------
- %% Func: terminate_child/2
- %% Returns: ok | {error, Reason}
- %% Note that the child is *always* terminated in some
- %% way (maybe killed).
- %%-----------------------------------------------------------------
- terminate_child(Supervisor, Name) ->
- call(Supervisor, {terminate_child, Name}).
- which_children(Supervisor) ->
- call(Supervisor, which_children).
- call(Supervisor, Req) ->
- gen_server:call(Supervisor, Req, infinity).
- check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
- case check_startspec(ChildSpecs) of
- {ok, _} -> ok;
- Error -> {error, Error}
- end;
- check_childspecs(X) -> {error, {badarg, X}}.
- %%% ---------------------------------------------------
- %%%
- %%% Initialize the supervisor.
- %%%
- %%% ---------------------------------------------------
- init({SupName, Mod, Args}) ->
- process_flag(trap_exit, true),
- gen:reg_behaviour(?MODULE),
- case Mod:init(Args) of
- {ok, {SupFlags, StartSpec}} ->
- gproc:reg({p,l,supflags}, SupFlags),
- case init_state(SupName, SupFlags, Mod, Args) of
- {ok, State} when ?is_simple(State) ->
- init_dynamic(State, StartSpec);
- {ok, State} ->
- init_children(State, StartSpec);
- Error ->
- {stop, {supervisor_data, Error}}
- end;
- ignore ->
- ignore;
- Error ->
- {stop, {bad_return, {Mod, init, Error}}}
- end.
-
- init_children(State, StartSpec) ->
- SupName = State#state.name,
- case check_startspec(StartSpec) of
- {ok, Children} ->
- reg_children(Children),
- case start_children(Children, SupName) of
- {ok, NChildren} ->
- set_children(NChildren),
- {ok, State#state{children = NChildren}};
- {error, NChildren} ->
- terminate_children(NChildren, SupName),
- {stop, shutdown}
- end;
- Error ->
- {stop, {start_spec, Error}}
- end.
- reg_children(Children) ->
- lists:foreach(
- fun(Ch) ->
- gproc:reg({p,l,{childspec,Ch#child.name}}, Ch)
- end, Children).
- set_children(Children) ->
- lists:foreach(
- fun(Ch) ->
- gproc:set_value({p,l,{childspec,Ch#child.name}}, Ch)
- end, Children).
- unreg_child(Child) ->
- gproc:unreg({p,l,{childspec,Child#child.name}}).
- set_child(Child) ->
- catch gproc:set_value({p,l,{childspec,Child#child.name}}, Child).
- init_dynamic(State, [StartSpec]) ->
- case check_startspec([StartSpec]) of
- {ok, Children} ->
- reg_children(Children),
- {ok, State#state{children = Children}};
- Error ->
- {stop, {start_spec, Error}}
- end;
- init_dynamic(_State, StartSpec) ->
- {stop, {bad_start_spec, StartSpec}}.
- %%-----------------------------------------------------------------
- %% Func: start_children/2
- %% Args: Children = [#child] in start order
- %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
- %% Purpose: Start all children. The new list contains #child's
- %% with pids.
- %% Returns: {ok, NChildren} | {error, NChildren}
- %% NChildren = [#child] in termination order (reversed
- %% start order)
- %%-----------------------------------------------------------------
- start_children(Children, SupName) -> start_children(Children, [], SupName).
- start_children([Child|Chs], NChildren, SupName) ->
- case do_start_child(SupName, Child) of
- {ok, Pid} ->
- start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
- {ok, Pid, _Extra} ->
- start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
- {error, Reason} ->
- report_error(start_error, Reason, Child, SupName),
- {error, lists:reverse(Chs) ++ [Child | NChildren]}
- end;
- start_children([], NChildren, _SupName) ->
- {ok, NChildren}.
- do_start_child(SupName, Child) ->
- #child{mfa = {M, F, A}} = Child,
- case catch apply(M, F, A) of
- {ok, Pid} when is_pid(Pid) ->
- NChild = Child#child{pid = Pid},
- report_progress(NChild, SupName),
- {ok, Pid};
- {ok, Pid, Extra} when is_pid(Pid) ->
- NChild = Child#child{pid = Pid},
- report_progress(NChild, SupName),
- {ok, Pid, Extra};
- ignore ->
- {ok, undefined};
- {error, What} -> {error, What};
- What -> {error, What}
- end.
- do_start_child_i(M, F, A) ->
- case catch apply(M, F, A) of
- {ok, Pid} when is_pid(Pid) ->
- {ok, Pid};
- {ok, Pid, Extra} when is_pid(Pid) ->
- {ok, Pid, Extra};
- ignore ->
- {ok, undefined};
- {error, Error} ->
- {error, Error};
- What ->
- {error, What}
- end.
-
- %%% ---------------------------------------------------
- %%%
- %%% Callback functions.
- %%%
- %%% ---------------------------------------------------
- handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
- #child{mfa = {M, F, A}} = hd(State#state.children),
- Args = A ++ EArgs,
- case do_start_child_i(M, F, Args) of
- {ok, Pid} ->
- gproc:reg({p,l,{simple_child,Pid}}, Args),
- NState = State#state{dynamics =
- ?DICT:store(Pid, Args, State#state.dynamics)},
- {reply, {ok, Pid}, NState};
- {ok, Pid, Extra} ->
- gproc:reg({p,l,{simple_child,Pid}}, Args),
- NState = State#state{dynamics =
- ?DICT:store(Pid, Args, State#state.dynamics)},
- {reply, {ok, Pid, Extra}, NState};
- What ->
- {reply, What, State}
- end;
- %%% The requests terminate_child, delete_child and restart_child are
- %%% invalid for simple_one_for_one supervisors.
- handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
- {reply, {error, simple_one_for_one}, State};
- handle_call({start_child, ChildSpec}, _From, State) ->
- case check_childspec(ChildSpec) of
- {ok, Child} ->
- {Resp, NState} = handle_start_child(Child, State),
- {reply, Resp, NState};
- What ->
- {reply, {error, What}, State}
- end;
- handle_call({restart_child, Name}, _From, State) ->
- case get_child(Name, State) of
- {value, Child} when Child#child.pid =:= undefined ->
- case do_start_child(State#state.name, Child) of
- {ok, Pid} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {reply, {ok, Pid}, NState};
- {ok, Pid, Extra} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {reply, {ok, Pid, Extra}, NState};
- Error ->
- {reply, Error, State}
- end;
- {value, _} ->
- {reply, {error, running}, State};
- _ ->
- {reply, {error, not_found}, State}
- end;
- handle_call({delete_child, Name}, _From, State) ->
- case get_child(Name, State) of
- {value, Child} when Child#child.pid =:= undefined ->
- NState = remove_child(Child, State),
- {reply, ok, NState};
- {value, _} ->
- {reply, {error, running}, State};
- _ ->
- {reply, {error, not_found}, State}
- end;
- handle_call({terminate_child, Name}, _From, State) ->
- case get_child(Name, State) of
- {value, Child} ->
- NChild = do_terminate(Child, State#state.name),
- {reply, ok, replace_child(NChild, State)};
- _ ->
- {reply, {error, not_found}, State}
- end;
- handle_call(which_children, _From, State) when ?is_simple(State) ->
- [#child{child_type = CT, modules = Mods}] = State#state.children,
- Reply = lists:map(fun({Pid, _}) -> {undefined, Pid, CT, Mods} end,
- ?DICT:to_list(State#state.dynamics)),
- {reply, Reply, State};
- handle_call(which_children, _From, State) ->
- Resp =
- lists:map(fun(#child{pid = Pid, name = Name,
- child_type = ChildType, modules = Mods}) ->
- {Name, Pid, ChildType, Mods}
- end,
- State#state.children),
- {reply, Resp, State}.
- %%% Hopefully cause a function-clause as there is no API function
- %%% that utilizes cast.
- handle_cast(null, State) ->
- error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n",
- []),
- {noreply, State}.
- %%
- %% Take care of terminated children.
- %%
- handle_info({'EXIT', Pid, Reason}, State) ->
- case restart_child(Pid, Reason, State) of
- {ok, State1} ->
- {noreply, State1};
- {shutdown, State1} ->
- {stop, shutdown, State1}
- end;
- handle_info(Msg, State) ->
- error_logger:error_msg("Supervisor received unexpected message: ~p~n",
- [Msg]),
- {noreply, State}.
- %%
- %% Terminate this server.
- %%
- terminate(_Reason, State) ->
- terminate_children(State#state.children, State#state.name),
- ok.
- %%
- %% Change code for the supervisor.
- %% Call the new call-back module and fetch the new start specification.
- %% Combine the new spec. with the old. If the new start spec. is
- %% not valid the code change will not succeed.
- %% Use the old Args as argument to Module:init/1.
- %% NOTE: This requires that the init function of the call-back module
- %% does not have any side effects.
- %%
- code_change(_, State, _) ->
- case (State#state.module):init(State#state.args) of
- {ok, {SupFlags, StartSpec}} ->
- case catch check_flags(SupFlags) of
- ok ->
- {Strategy, MaxIntensity, Period} = SupFlags,
- update_childspec(State#state{strategy = Strategy,
- intensity = MaxIntensity,
- period = Period},
- StartSpec);
- Error ->
- {error, Error}
- end;
- ignore ->
- {ok, State};
- Error ->
- Error
- end.
- check_flags({Strategy, MaxIntensity, Period}) ->
- validStrategy(Strategy),
- validIntensity(MaxIntensity),
- validPeriod(Period),
- ok;
- check_flags(What) ->
- {bad_flags, What}.
- update_childspec(State, StartSpec) when ?is_simple(State) ->
- case check_startspec(StartSpec) of
- {ok, [Child]} ->
- set_children([Child]),
- {ok, State#state{children = [Child]}};
- Error ->
- {error, Error}
- end;
- update_childspec(State, StartSpec) ->
- case check_startspec(StartSpec) of
- {ok, Children} ->
- OldC = State#state.children, % In reverse start order !
- NewC = update_childspec1(OldC, Children, []),
- set_children(NewC),
- {ok, State#state{children = NewC}};
- Error ->
- {error, Error}
- end.
- update_childspec1([Child|OldC], Children, KeepOld) ->
- case update_chsp(Child, Children) of
- {ok,NewChildren} ->
- update_childspec1(OldC, NewChildren, KeepOld);
- false ->
- update_childspec1(OldC, Children, [Child|KeepOld])
- end;
- update_childspec1([], Children, KeepOld) ->
- % Return them in (keeped) reverse start order.
- lists:reverse(Children ++ KeepOld).
- update_chsp(OldCh, Children) ->
- case lists:map(fun(Ch) when OldCh#child.name =:= Ch#child.name ->
- Ch#child{pid = OldCh#child.pid};
- (Ch) ->
- Ch
- end,
- Children) of
- Children ->
- false; % OldCh not found in new spec.
- NewC ->
- {ok, NewC}
- end.
-
- %%% ---------------------------------------------------
- %%% Start a new child.
- %%% ---------------------------------------------------
- handle_start_child(Child, State) ->
- case get_child(Child#child.name, State) of
- false ->
- case do_start_child(State#state.name, Child) of
- {ok, Pid} ->
- Children = State#state.children,
- NewChild = Child#child{pid = Pid},
- NewC = [NewChild|Children],
- set_child(NewChild),
- {{ok, Pid},
- State#state{children = NewC}};
- {ok, Pid, Extra} ->
- Children = State#state.children,
- NewChild = Child#child{pid = Pid},
- NewC = [NewChild|Children],
- set_child(NewChild),
- {{ok, Pid, Extra},
- State#state{children = NewC}};
- {error, What} ->
- {{error, {What, Child}}, State}
- end;
- {value, OldChild} when OldChild#child.pid =/= undefined ->
- {{error, {already_started, OldChild#child.pid}}, State};
- {value, _OldChild} ->
- {{error, already_present}, State}
- end.
- %%% ---------------------------------------------------
- %%% Restart. A process has terminated.
- %%% Returns: {ok, #state} | {shutdown, #state}
- %%% ---------------------------------------------------
- restart_child(Pid, Reason, State) when ?is_simple(State) ->
- case ?DICT:find(Pid, State#state.dynamics) of
- {ok, Args} ->
- [Child] = State#state.children,
- RestartType = Child#child.restart_type,
- {M, F, _} = Child#child.mfa,
- NChild = Child#child{pid = Pid, mfa = {M, F, Args}},
- do_restart(RestartType, Reason, NChild, State);
- error ->
- {ok, State}
- end;
- restart_child(Pid, Reason, State) ->
- Children = State#state.children,
- case lists:keysearch(Pid, #child.pid, Children) of
- {value, Child} ->
- RestartType = Child#child.restart_type,
- do_restart(RestartType, Reason, Child, State);
- _ ->
- {ok, State}
- end.
- do_restart(permanent, Reason, Child, State) ->
- report_error(child_terminated, Reason, Child, State#state.name),
- restart(Child, State);
- do_restart(_, normal, Child, State) ->
- NState = state_del_child(Child, State),
- {ok, NState};
- do_restart(_, shutdown, Child, State) ->
- NState = state_del_child(Child, State),
- {ok, NState};
- do_restart(transient, Reason, Child, State) ->
- report_error(child_terminated, Reason, Child, State#state.name),
- restart(Child, State);
- do_restart(temporary, Reason, Child, State) ->
- report_error(child_terminated, Reason, Child, State#state.name),
- NState = state_del_child(Child, State),
- {ok, NState}.
- restart(Child, State) ->
- case add_restart(State) of
- {ok, NState} ->
- restart(NState#state.strategy, Child, NState);
- {terminate, NState} ->
- report_error(shutdown, reached_max_restart_intensity,
- Child, State#state.name),
- {shutdown, remove_child(Child, NState)}
- end.
- restart(simple_one_for_one, Child, State) ->
- #child{mfa = {M, F, A}} = Child,
- Dynamics = ?DICT:erase(Child#child.pid, State#state.dynamics),
- case do_start_child_i(M, F, A) of
- {ok, Pid} ->
- NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
- {ok, NState};
- {ok, Pid, _Extra} ->
- NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
- {ok, NState};
- {error, Error} ->
- report_error(start_error, Error, Child, State#state.name),
- restart(Child, State)
- end;
- restart(one_for_one, Child, State) ->
- case do_start_child(State#state.name, Child) of
- {ok, Pid} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {ok, NState};
- {ok, Pid, _Extra} ->
- NState = replace_child(Child#child{pid = Pid}, State),
- {ok, NState};
- {error, Reason} ->
- report_error(start_error, Reason, Child, State#state.name),
- restart(Child, State)
- end;
- restart(rest_for_one, Child, State) ->
- {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children),
- ChAfter2 = terminate_children(ChAfter, State#state.name),
- case start_children(ChAfter2, State#state.name) of
- {ok, ChAfter3} ->
- NewC = ChAfter3 ++ ChBefore,
- {ok, State#state{children = NewC}};
- {error, ChAfter3} ->
- NewC = ChAfter3 ++ ChBefore,
- restart(Child, State#state{children = NewC})
- end;
- restart(one_for_all, Child, State) ->
- Children1 = del_child(Child#child.pid, State#state.children),
- Children2 = terminate_children(Children1, State#state.name),
- case start_children(Children2, State#state.name) of
- {ok, NChs} ->
- {ok, State#state{children = NChs}};
- {error, NChs} ->
- restart(Child, State#state{children = NChs})
- end.
- %%-----------------------------------------------------------------
- %% Func: terminate_children/2
- %% Args: Children = [#child] in termination order
- %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
- %% Returns: NChildren = [#child] in
- %% startup order (reversed termination order)
- %%-----------------------------------------------------------------
- terminate_children(Children, SupName) ->
- terminate_children(Children, SupName, []).
- terminate_children([Child | Children], SupName, Res) ->
- NChild = do_terminate(Child, SupName),
- set_child(NChild),
- terminate_children(Children, SupName, [NChild | Res]);
- terminate_children([], _SupName, Res) ->
- Res.
- do_terminate(Child, SupName) when Child#child.pid =/= undefined ->
- case shutdown(Child#child.pid,
- Child#child.shutdown) of
- ok ->
- Child#child{pid = undefined};
- {error, OtherReason} ->
- report_error(shutdown_error, OtherReason, Child, SupName),
- Child#child{pid = undefined}
- end;
- do_terminate(Child, _SupName) ->
- Child.
- %%-----------------------------------------------------------------
- %% Shutdowns a child. We must check the EXIT value
- %% of the child, because it might have died with another reason than
- %% the wanted. In that case we want to report the error. We put a
- %% monitor on the child an check for the 'DOWN' message instead of
- %% checking for the 'EXIT' message, because if we check the 'EXIT'
- %% message a "naughty" child, who does unlink(Sup), could hang the
- %% supervisor.
- %% Returns: ok | {error, OtherReason} (this should be reported)
- %%-----------------------------------------------------------------
- shutdown(Pid, brutal_kill) ->
-
- case monitor_child(Pid) of
- ok ->
- exit(Pid, kill),
- receive
- {'DOWN', _MRef, process, Pid, killed} ->
- ok;
- {'DOWN', _MRef, process, Pid, OtherReason} ->
- {error, OtherReason}
- end;
- {error, Reason} ->
- {error, Reason}
- end;
- shutdown(Pid, Time) ->
-
- case monitor_child(Pid) of
- ok ->
- exit(Pid, shutdown), %% Try to shutdown gracefully
- receive
- {'DOWN', _MRef, process, Pid, shutdown} ->
- ok;
- {'DOWN', _MRef, process, Pid, OtherReason} ->
- {error, OtherReason}
- after Time ->
- exit(Pid, kill), %% Force termination.
- receive
- {'DOWN', _MRef, process, Pid, OtherReason} ->
- {error, OtherReason}
- end
- end;
- {error, Reason} ->
- {error, Reason}
- end.
- %% Help function to shutdown/2 switches from link to monitor approach
- monitor_child(Pid) ->
-
- %% Do the monitor operation first so that if the child dies
- %% before the monitoring is done causing a 'DOWN'-message with
- %% reason noproc, we will get the real reason in the 'EXIT'-message
- %% unless a naughty child has already done unlink...
- erlang:monitor(process, Pid),
- unlink(Pid),
- receive
- %% If the child dies before the unlik we must empty
- %% the mail-box of the 'EXIT'-message and the 'DOWN'-message.
- {'EXIT', Pid, Reason} ->
- receive
- {'DOWN', _, process, Pid, _} ->
- {error, Reason}
- end
- after 0 ->
- %% If a naughty child did unlink and the child dies before
- %% monitor the result will be that shutdown/2 receives a
- %% 'DOWN'-message with reason noproc.
- %% If the child should die after the unlink there
- %% will be a 'DOWN'-message with a correct reason
- %% that will be handled in shutdown/2.
- ok
- end.
-
-
- %%-----------------------------------------------------------------
- %% Child/State manipulating functions.
- %%-----------------------------------------------------------------
- state_del_child(#child{pid = Pid}, State) when ?is_simple(State) ->
- gproc:unreg({p,l,{simple_child,Pid}}),
- NDynamics = ?DICT:erase(Pid, State#state.dynamics),
- State#state{dynamics = NDynamics};
- state_del_child(Child, State) ->
- NChildren = del_child(Child#child.name, State#state.children),
- State#state{children = NChildren}.
- del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name ->
- NewCh = Ch#child{pid = undefined},
- set_child(NewCh),
- [NewCh | Chs];
- del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid ->
- NewCh = Ch#child{pid = undefined},
- set_child(NewCh),
- [NewCh | Chs];
- del_child(Name, [Ch|Chs]) ->
- [Ch|del_child(Name, Chs)];
- del_child(_, []) ->
- [].
- %% Chs = [S4, S3, Ch, S1, S0]
- %% Ret: {[S4, S3, Ch], [S1, S0]}
- split_child(Name, Chs) ->
- split_child(Name, Chs, []).
- split_child(Name, [Ch|Chs], After) when Ch#child.name =:= Name ->
- {lists:reverse([Ch#child{pid = undefined} | After]), Chs};
- split_child(Pid, [Ch|Chs], After) when Ch#child.pid =:= Pid ->
- {lists:reverse([Ch#child{pid = undefined} | After]), Chs};
- split_child(Name, [Ch|Chs], After) ->
- split_child(Name, Chs, [Ch | After]);
- split_child(_, [], After) ->
- {lists:reverse(After), []}.
- get_child(Name, State) ->
- lists:keysearch(Name, #child.name, State#state.children).
- replace_child(Child, State) ->
- Chs = do_replace_child(Child, State#state.children),
- State#state{children = Chs}.
- do_replace_child(Child, [Ch|Chs]) when Ch#child.name =:= Child#child.name ->
- set_child(Child),
- [Child | Chs];
- do_replace_child(Child, [Ch|Chs]) ->
- [Ch|do_replace_child(Child, Chs)].
- remove_child(Child, State) ->
- Chs = lists:keydelete(Child#child.name, #child.name, State#state.children),
- unreg_child(Child),
- State#state{children = Chs}.
- %%-----------------------------------------------------------------
- %% Func: init_state/4
- %% Args: SupName = {local, atom()} | {global, atom()} | self
- %% Type = {Strategy, MaxIntensity, Period}
- %% Strategy = one_for_one | one_for_all | simple_one_for_one |
- %% rest_for_one
- %% MaxIntensity = integer()
- %% Period = integer()
- %% Mod :== atom()
- %% Arsg :== term()
- %% Purpose: Check that Type is of correct type (!)
- %% Returns: {ok, #state} | Error
- %%-----------------------------------------------------------------
- init_state(SupName, Type, Mod, Args) ->
- case catch init_state1(SupName, Type, Mod, Args) of
- {ok, State} ->
- {ok, State};
- Error ->
- Error
- end.
- init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
- validStrategy(Strategy),
- validIntensity(MaxIntensity),
- validPeriod(Period),
- {ok, #state{name = supname(SupName,Mod),
- strategy = Strategy,
- intensity = MaxIntensity,
- period = Period,
- module = Mod,
- args = Args}};
- init_state1(_SupName, Type, _, _) ->
- {invalid_type, Type}.
- validStrategy(simple_one_for_one) -> true;
- validStrategy(one_for_one) -> true;
- validStrategy(one_for_all) -> true;
- validStrategy(rest_for_one) -> true;
- validStrategy(What) -> throw({invalid_strategy, What}).
- validIntensity(Max) when is_integer(Max),
- Max >= 0 -> true;
- validIntensity(What) -> throw({invalid_intensity, What}).
- validPeriod(Period) when is_integer(Period),
- Period > 0 -> true;
- validPeriod(What) -> throw({invalid_period, What}).
- supname(self,Mod) -> {self(),Mod};
- supname(N,_) -> N.
- %%% ------------------------------------------------------
- %%% Check that the children start specification is valid.
- %%% Shall be a six (6) tuple
- %%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
- %%% where Name is an atom
- %%% Func is {Mod, Fun, Args} == {atom, atom, list}
- %%% RestartType is permanent | temporary | transient
- %%% Shutdown = integer() | infinity | brutal_kill
- %%% ChildType = supervisor | worker
- %%% Modules = [atom()] | dynamic
- %%% Returns: {ok, [#child]} | Error
- %%% ------------------------------------------------------
- check_startspec(Children) -> check_startspec(Children, []).
- check_startspec([ChildSpec|T], Res) ->
- case check_childspec(ChildSpec) of
- {ok, Child} ->
- case lists:keysearch(Child#child.name, #child.name, Res) of
- {value, _} -> {duplicate_child_name, Child#child.name};
- _ -> check_startspec(T, [Child | Res])
- end;
- Error -> Error
- end;
- check_startspec([], Res) ->
- {ok, lists:reverse(Res)}.
- check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) ->
- catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods);
- check_childspec(X) -> {invalid_child_spec, X}.
- check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
- validName(Name),
- validFunc(Func),
- validRestartType(RestartType),
- validChildType(ChildType),
- validShutdown(Shutdown, ChildType),
- validMods(Mods),
- {ok, #child{name = Name, mfa = Func, restart_type = RestartType,
- shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
- validChildType(supervisor) -> true;
- validChildType(worker) -> true;
- validChildType(What) -> throw({invalid_child_type, What}).
- validName(_Name) -> true.
- validFunc({M, F, A}) when is_atom(M),
- is_atom(F),
- is_list(A) -> true;
- validFunc(Func) -> throw({invalid_mfa, Func}).
- validRestartType(permanent) -> true;
- validRestartType(temporary) -> true;
- validRestartType(transient) -> true;
- validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}).
- validShutdown(Shutdown, _)
- when is_integer(Shutdown), Shutdown > 0 -> true;
- validShutdown(infinity, supervisor) -> true;
- validShutdown(brutal_kill, _) -> true;
- validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}).
- validMods(dynamic) -> true;
- validMods(Mods) when is_list(Mods) ->
- lists:foreach(fun(Mod) ->
- if
- is_atom(Mod) -> ok;
- true -> throw({invalid_module, Mod})
- end
- end,
- Mods);
- validMods(Mods) -> throw({invalid_modules, Mods}).
- %%% ------------------------------------------------------
- %%% Add a new restart and calculate if the max restart
- %%% intensity has been reached (in that case the supervisor
- %%% shall terminate).
- %%% All restarts accured inside the period amount of seconds
- %%% are kept in the #state.restarts list.
- %%% Returns: {ok, State'} | {terminate, State'}
- %%% ------------------------------------------------------
- add_restart(State) ->
- I = State#state.intensity,
- P = State#state.period,
- R = State#state.restarts,
- Now = erlang:now(),
- R1 = add_restart([Now|R], Now, P),
- State1 = State#state{restarts = R1},
- case length(R1) of
- CurI when CurI =< I ->
- {ok, State1};
- _ ->
- {terminate, State1}
- end.
- add_restart([R|Restarts], Now, Period) ->
- case inPeriod(R, Now, Period) of
- true ->
- [R|add_restart(Restarts, Now, Period)];
- _ ->
- []
- end;
- add_restart([], _, _) ->
- [].
- inPeriod(Time, Now, Period) ->
- case difference(Time, Now) of
- T when T > Period ->
- false;
- _ ->
- true
- end.
- %%
- %% Time = {MegaSecs, Secs, MicroSecs} (NOTE: MicroSecs is ignored)
- %% Calculate the time elapsed in seconds between two timestamps.
- %% If MegaSecs is equal just subtract Secs.
- %% Else calculate the Mega difference and add the Secs difference,
- %% note that Secs difference can be negative, e.g.
- %% {827, 999999, 676} diff {828, 1, 653753} == > 2 secs.
- %%
- difference({TimeM, TimeS, _}, {CurM, CurS, _}) when CurM > TimeM ->
- ((CurM - TimeM) * 1000000) + (CurS - TimeS);
- difference({_, TimeS, _}, {_, CurS, _}) ->
- CurS - TimeS.
- %%% ------------------------------------------------------
- %%% Error and progress reporting.
- %%% ------------------------------------------------------
- report_error(Error, Reason, Child, SupName) ->
- ErrorMsg = [{supervisor, SupName},
- {errorContext, Error},
- {reason, Reason},
- {offender, extract_child(Child)}],
- error_logger:error_report(supervisor_report, ErrorMsg).
- extract_child(Child) ->
- [{pid, Child#child.pid},
- {name, Child#child.name},
- {mfa, Child#child.mfa},
- {restart_type, Child#child.restart_type},
- {shutdown, Child#child.shutdown},
- {child_type, Child#child.child_type}].
- report_progress(Child, SupName) ->
- Progress = [{supervisor, SupName},
- {started, extract_child(Child)}],
- error_logger:info_report(progress, Progress).
|