supervisor.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 889 行 · 第 1/2 页

ERL
889
字号
	    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} ->	    {ok, State#state{children = ChAfter3 ++ ChBefore}};	{error, ChAfter3} ->	    restart(Child, State#state{children = ChAfter3 ++ ChBefore})    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),    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 approachmonitor_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) ->    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 ->    [Ch#child{pid = undefined} | Chs];del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid ->    [Ch#child{pid = undefined} | 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 ->    [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),    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).

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?