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