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 + -
显示快捷键?