supervisor.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 889 行 · 第 1/2 页
ERL
889 行
%% ``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), case Mod:init(Args) of {ok, {SupFlags, StartSpec}} -> 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} -> case start_children(Children, SupName) of {ok, NChildren} -> {ok, State#state{children = NChildren}}; {error, NChildren} -> terminate_children(NChildren, SupName), {stop, shutdown} end; Error -> {stop, {start_spec, Error}} end.init_dynamic(State, [StartSpec]) -> case check_startspec([StartSpec]) of {ok, 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} -> NState = State#state{dynamics = ?DICT:store(Pid, Args, State#state.dynamics)}, {reply, {ok, Pid}, NState}; {ok, Pid, Extra} -> 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]} -> {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, []), {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, {{ok, Pid}, State#state{children = [Child#child{pid = Pid}|Children]}}; {ok, Pid, Extra} -> Children = State#state.children, {{ok, Pid, Extra}, State#state{children = [Child#child{pid = Pid}|Children]}}; {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,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?