1 %% This file is a copy of supervisor.erl from the R16B Erlang/OTP
2 %% distribution, with the following modifications:
4 %% 1) the module name is supervisor2
6 %% 2) a find_child/2 utility function has been added
8 %% 3) Added an 'intrinsic' restart type. Like the transient type, this
9 %% type means the child should only be restarted if the child exits
10 %% abnormally. Unlike the transient type, if the child exits
11 %% normally, the supervisor itself also exits normally. If the
12 %% child is a supervisor and it exits normally (i.e. with reason of
13 %% 'shutdown') then the child's parent also exits normally.
15 %% 4) child specifications can contain, as the restart type, a tuple
16 %% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay}
17 %% where Delay >= 0 (see point (4) below for intrinsic). The delay,
18 %% in seconds, indicates what should happen if a child, upon being
19 %% restarted, exceeds the MaxT and MaxR parameters. Thus, if a
20 %% child exits, it is restarted as normal. If it exits sufficiently
21 %% quickly and often to exceed the boundaries set by the MaxT and
22 %% MaxR parameters, and a Delay is specified, then rather than
23 %% stopping the supervisor, the supervisor instead continues and
24 %% tries to start up the child again, Delay seconds later.
26 %% Note that you can never restart more frequently than the MaxT
27 %% and MaxR parameters allow: i.e. you must wait until *both* the
28 %% Delay has passed *and* the MaxT and MaxR parameters allow the
29 %% child to be restarted.
31 %% Also note that the Delay is a *minimum*. There is no guarantee
32 %% that the child will be restarted within that time, especially if
33 %% other processes are dying and being restarted at the same time -
34 %% essentially we have to wait for the delay to have passed and for
35 %% the MaxT and MaxR parameters to permit the child to be
36 %% restarted. This may require waiting for longer than Delay.
38 %% Sometimes, you may wish for a transient or intrinsic child to
39 %% exit abnormally so that it gets restarted, but still log
40 %% nothing. gen_server will log any exit reason other than
41 %% 'normal', 'shutdown' or {'shutdown', _}. Thus the exit reason of
42 %% {'shutdown', 'restart'} is interpreted to mean you wish the
43 %% child to be restarted according to the delay parameters, but
44 %% gen_server will not log the error. Thus from gen_server's
45 %% perspective it's a normal exit, whilst from supervisor's
46 %% perspective, it's an abnormal exit.
48 %% 5) normal, and {shutdown, _} exit reasons are all treated the same
49 %% (i.e. are regarded as normal exits)
51 %% All modifications are (C) 2010-2013 VMware, Inc.
55 %% Copyright Ericsson AB 1996-2012. All Rights Reserved.
57 %% The contents of this file are subject to the Erlang Public License,
58 %% Version 1.1, (the "License"); you may not use this file except in
59 %% compliance with the License. You should have received a copy of the
60 %% Erlang Public License along with this software. If not, it can be
61 %% retrieved online at http://www.erlang.org/.
63 %% Software distributed under the License is distributed on an "AS IS"
64 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
65 %% the License for the specific language governing rights and limitations
72 -behaviour(gen_server).
75 -export([start_link/2, start_link/3,
76 start_child/2, restart_child/2,
77 delete_child/2, terminate_child/2,
78 which_children/1, count_children/1,
79 find_child/2, check_childspecs/1]).
82 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
83 terminate/2, code_change/3]).
84 -export([try_again_restart/3]).
86 %%--------------------------------------------------------------------------
88 -export_type([child_spec/0, startchild_ret/0, strategy/0]).
90 %%--------------------------------------------------------------------------
93 -type child() :: 'undefined' | pid().
94 -type child_id() :: term().
95 -type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}.
96 -type modules() :: [module()] | 'dynamic'.
97 -type delay() :: non_neg_integer().
98 -type restart() :: 'permanent' | 'transient' | 'temporary' | 'intrinsic' | {'permanent', delay()} | {'transient', delay()} | {'intrinsic', delay()}.
99 -type shutdown() :: 'brutal_kill' | timeout().
100 -type worker() :: 'worker' | 'supervisor'.
101 -type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}.
102 -type sup_ref() :: (Name :: atom())
103 | {Name :: atom(), Node :: node()}
104 | {'global', Name :: atom()}
106 -type child_spec() :: {Id :: child_id(),
107 StartFunc :: mfargs(),
108 Restart :: restart(),
109 Shutdown :: shutdown(),
111 Modules :: modules()}.
113 -type strategy() :: 'one_for_all' | 'one_for_one'
114 | 'rest_for_one' | 'simple_one_for_one'.
117 %%--------------------------------------------------------------------------
120 -record(child, {% pid is undefined when child is not running
121 pid = undefined :: child() | {restarting,pid()} | [pid()],
124 restart_type :: restart(),
125 shutdown :: shutdown(),
126 child_type :: worker(),
127 modules = [] :: modules()}).
128 -type child_rec() :: #child{}.
145 -record(state, {name,
146 strategy :: strategy(),
147 children = [] :: [child_rec()],
148 dynamics :: ?DICT() | ?SET(),
149 intensity :: non_neg_integer(),
150 period :: pos_integer(),
154 -type state() :: #state{}.
156 -record(state, {name,
167 -define(is_simple(State), State#state.strategy =:= simple_one_for_one).
168 -define(is_permanent(R), ((R =:= permanent) orelse
170 tuple_size(R) == 2 andalso
171 element(1, R) =:= permanent))).
172 -define(is_explicit_restart(R),
173 R == {shutdown, restart}).
176 -callback init(Args :: term()) ->
177 {ok, {{RestartStrategy :: strategy(),
178 MaxR :: non_neg_integer(),
179 MaxT :: non_neg_integer()},
180 [ChildSpec :: child_spec()]}}
183 -define(restarting(_Pid_), {restarting,_Pid_}).
185 %%% ---------------------------------------------------
186 %%% This is a general process supervisor built upon gen_server.erl.
187 %%% Servers/processes should/could also be built using gen_server.erl.
188 %%% SupName = {local, atom()} | {global, atom()}.
189 %%% ---------------------------------------------------
191 -type startlink_err() :: {'already_started', pid()}
192 | {'shutdown', term()}
194 -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
196 -spec start_link(Module, Args) -> startlink_ret() when
201 start_link(Mod, Args) ->
202 gen_server:start_link(?MODULE, {self, Mod, Args}, []).
205 -spec start_link(SupName, Module, Args) -> startlink_ret() when
206 SupName :: sup_name(),
210 start_link(SupName, Mod, Args) ->
211 gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []).
213 %%% ---------------------------------------------------
214 %%% Interface functions.
215 %%% ---------------------------------------------------
217 -type startchild_err() :: 'already_present'
218 | {'already_started', Child :: child()} | term().
219 -type startchild_ret() :: {'ok', Child :: child()}
220 | {'ok', Child :: child(), Info :: term()}
221 | {'error', startchild_err()}.
223 -spec start_child(SupRef, ChildSpec) -> startchild_ret() when
225 ChildSpec :: child_spec() | (List :: [term()]).
227 start_child(Supervisor, ChildSpec) ->
228 call(Supervisor, {start_child, ChildSpec}).
231 -spec restart_child(SupRef, Id) -> Result when
234 Result :: {'ok', Child :: child()}
235 | {'ok', Child :: child(), Info :: term()}
237 Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' |
240 restart_child(Supervisor, Name) ->
241 call(Supervisor, {restart_child, Name}).
244 -spec delete_child(SupRef, Id) -> Result when
247 Result :: 'ok' | {'error', Error},
248 Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'.
250 delete_child(Supervisor, Name) ->
251 call(Supervisor, {delete_child, Name}).
253 %%-----------------------------------------------------------------
254 %% Func: terminate_child/2
255 %% Returns: ok | {error, Reason}
256 %% Note that the child is *always* terminated in some
257 %% way (maybe killed).
258 %%-----------------------------------------------------------------
260 -spec terminate_child(SupRef, Id) -> Result when
262 Id :: pid() | child_id(),
263 Result :: 'ok' | {'error', Error},
264 Error :: 'not_found' | 'simple_one_for_one'.
266 terminate_child(Supervisor, Name) ->
267 call(Supervisor, {terminate_child, Name}).
270 -spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when
272 Id :: child_id() | undefined,
273 Child :: child() | 'restarting',
275 Modules :: modules().
277 which_children(Supervisor) ->
278 call(Supervisor, which_children).
281 -spec count_children(SupRef) -> PropListOfCounts when
283 PropListOfCounts :: [Count],
284 Count :: {specs, ChildSpecCount :: non_neg_integer()}
285 | {active, ActiveProcessCount :: non_neg_integer()}
286 | {supervisors, ChildSupervisorCount :: non_neg_integer()}
287 |{workers, ChildWorkerCount :: non_neg_integer()}.
289 count_children(Supervisor) ->
290 call(Supervisor, count_children).
293 -spec find_child(Supervisor, Name) -> [pid()] when
294 Supervisor :: sup_ref(),
297 find_child(Supervisor, Name) ->
298 [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor),
301 call(Supervisor, Req) ->
302 gen_server:call(Supervisor, Req, infinity).
305 -spec check_childspecs(ChildSpecs) -> Result when
306 ChildSpecs :: [child_spec()],
307 Result :: 'ok' | {'error', Error :: term()}.
309 check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
310 case check_startspec(ChildSpecs) of
312 Error -> {error, Error}
314 check_childspecs(X) -> {error, {badarg, X}}.
316 %%%-----------------------------------------------------------------
317 %%% Called by timer:apply_after from restart/2
319 -spec try_again_restart(SupRef, Child, Reason) -> ok when
321 Child :: child_id() | pid(),
324 try_again_restart(Supervisor, Child, Reason) ->
325 cast(Supervisor, {try_again_restart, Child, Reason}).
327 cast(Supervisor, Req) ->
328 gen_server:cast(Supervisor, Req).
330 %%% ---------------------------------------------------
332 %%% Initialize the supervisor.
334 %%% ---------------------------------------------------
336 -type init_sup_name() :: sup_name() | 'self'.
338 -type stop_rsn() :: {'shutdown', term()}
339 | {'bad_return', {module(),'init', term()}}
340 | {'bad_start_spec', term()}
341 | {'start_spec', term()}
342 | {'supervisor_data', term()}.
344 -spec init({init_sup_name(), module(), [term()]}) ->
345 {'ok', state()} | 'ignore' | {'stop', stop_rsn()}.
347 init({SupName, Mod, Args}) ->
348 process_flag(trap_exit, true),
349 case Mod:init(Args) of
350 {ok, {SupFlags, StartSpec}} ->
351 case init_state(SupName, SupFlags, Mod, Args) of
352 {ok, State} when ?is_simple(State) ->
353 init_dynamic(State, StartSpec);
355 init_children(State, StartSpec);
357 {stop, {supervisor_data, Error}}
362 {stop, {bad_return, {Mod, init, Error}}}
365 init_children(State, StartSpec) ->
366 SupName = State#state.name,
367 case check_startspec(StartSpec) of
369 case start_children(Children, SupName) of
371 {ok, State#state{children = NChildren}};
372 {error, NChildren, Reason} ->
373 terminate_children(NChildren, SupName),
374 {stop, {shutdown, Reason}}
377 {stop, {start_spec, Error}}
380 init_dynamic(State, [StartSpec]) ->
381 case check_startspec([StartSpec]) of
383 {ok, State#state{children = Children}};
385 {stop, {start_spec, Error}}
387 init_dynamic(_State, StartSpec) ->
388 {stop, {bad_start_spec, StartSpec}}.
390 %%-----------------------------------------------------------------
391 %% Func: start_children/2
392 %% Args: Children = [child_rec()] in start order
393 %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod}
394 %% Purpose: Start all children. The new list contains #child's
396 %% Returns: {ok, NChildren} | {error, NChildren, Reason}
397 %% NChildren = [child_rec()] in termination order (reversed
399 %%-----------------------------------------------------------------
400 start_children(Children, SupName) -> start_children(Children, [], SupName).
402 start_children([Child|Chs], NChildren, SupName) ->
403 case do_start_child(SupName, Child) of
404 {ok, undefined} when Child#child.restart_type =:= temporary ->
405 start_children(Chs, NChildren, SupName);
407 start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
409 start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName);
411 report_error(start_error, Reason, Child, SupName),
412 {error, lists:reverse(Chs) ++ [Child | NChildren],
413 {failed_to_start_child,Child#child.name,Reason}}
415 start_children([], NChildren, _SupName) ->
418 do_start_child(SupName, Child) ->
419 #child{mfargs = {M, F, Args}} = Child,
420 case catch apply(M, F, Args) of
421 {ok, Pid} when is_pid(Pid) ->
422 NChild = Child#child{pid = Pid},
423 report_progress(NChild, SupName),
425 {ok, Pid, Extra} when is_pid(Pid) ->
426 NChild = Child#child{pid = Pid},
427 report_progress(NChild, SupName),
431 {error, What} -> {error, What};
432 What -> {error, What}
435 do_start_child_i(M, F, A) ->
436 case catch apply(M, F, A) of
437 {ok, Pid} when is_pid(Pid) ->
439 {ok, Pid, Extra} when is_pid(Pid) ->
449 %%% ---------------------------------------------------
451 %%% Callback functions.
453 %%% ---------------------------------------------------
455 -type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine
456 -spec handle_call(call(), term(), state()) -> {'reply', term(), state()}.
458 handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
459 Child = hd(State#state.children),
460 #child{mfargs = {M, F, A}} = Child,
462 case do_start_child_i(M, F, Args) of
463 {ok, undefined} when Child#child.restart_type =:= temporary ->
464 {reply, {ok, undefined}, State};
466 NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
467 {reply, {ok, Pid}, NState};
469 NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
470 {reply, {ok, Pid, Extra}, NState};
475 %% terminate_child for simple_one_for_one can only be done with pid
476 handle_call({terminate_child, Name}, _From, State) when not is_pid(Name),
478 {reply, {error, simple_one_for_one}, State};
480 handle_call({terminate_child, Name}, _From, State) ->
481 case get_child(Name, State, ?is_simple(State)) of
483 case do_terminate(Child, State#state.name) of
484 #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) ->
485 {reply, ok, state_del_child(Child, State)};
487 {reply, ok, replace_child(NChild, State)}
490 {reply, {error, not_found}, State}
493 %%% The requests delete_child and restart_child are invalid for
494 %%% simple_one_for_one supervisors.
495 handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
496 {reply, {error, simple_one_for_one}, State};
498 handle_call({start_child, ChildSpec}, _From, State) ->
499 case check_childspec(ChildSpec) of
501 {Resp, NState} = handle_start_child(Child, State),
502 {reply, Resp, NState};
504 {reply, {error, What}, State}
507 handle_call({restart_child, Name}, _From, State) ->
508 case get_child(Name, State) of
509 {value, Child} when Child#child.pid =:= undefined ->
510 case do_start_child(State#state.name, Child) of
512 NState = replace_child(Child#child{pid = Pid}, State),
513 {reply, {ok, Pid}, NState};
515 NState = replace_child(Child#child{pid = Pid}, State),
516 {reply, {ok, Pid, Extra}, NState};
518 {reply, Error, State}
520 {value, #child{pid=?restarting(_)}} ->
521 {reply, {error, restarting}, State};
523 {reply, {error, running}, State};
525 {reply, {error, not_found}, State}
528 handle_call({delete_child, Name}, _From, State) ->
529 case get_child(Name, State) of
530 {value, Child} when Child#child.pid =:= undefined ->
531 NState = remove_child(Child, State),
533 {value, #child{pid=?restarting(_)}} ->
534 {reply, {error, restarting}, State};
536 {reply, {error, running}, State};
538 {reply, {error, not_found}, State}
541 handle_call(which_children, _From, #state{children = [#child{restart_type = temporary,
544 State) when ?is_simple(State) ->
545 Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end,
546 ?SETS:to_list(dynamics_db(temporary, State#state.dynamics))),
547 {reply, Reply, State};
549 handle_call(which_children, _From, #state{children = [#child{restart_type = RType,
552 State) when ?is_simple(State) ->
553 Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods};
554 ({Pid, _}) -> {undefined, Pid, CT, Mods} end,
555 ?DICT:to_list(dynamics_db(RType, State#state.dynamics))),
556 {reply, Reply, State};
558 handle_call(which_children, _From, State) ->
560 lists:map(fun(#child{pid = ?restarting(_), name = Name,
561 child_type = ChildType, modules = Mods}) ->
562 {Name, restarting, ChildType, Mods};
563 (#child{pid = Pid, name = Name,
564 child_type = ChildType, modules = Mods}) ->
565 {Name, Pid, ChildType, Mods}
567 State#state.children),
568 {reply, Resp, State};
571 handle_call(count_children, _From, #state{children = [#child{restart_type = temporary,
572 child_type = CT}]} = State)
573 when ?is_simple(State) ->
575 ?SETS:fold(fun(Pid, {Alive, Tot}) ->
576 case is_pid(Pid) andalso is_process_alive(Pid) of
577 true ->{Alive+1, Tot +1};
581 end, {0, 0}, dynamics_db(temporary, State#state.dynamics)),
583 supervisor -> [{specs, 1}, {active, Active},
584 {supervisors, Count}, {workers, 0}];
585 worker -> [{specs, 1}, {active, Active},
586 {supervisors, 0}, {workers, Count}]
588 {reply, Reply, State};
590 handle_call(count_children, _From, #state{children = [#child{restart_type = RType,
591 child_type = CT}]} = State)
592 when ?is_simple(State) ->
594 ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) ->
595 case is_pid(Pid) andalso is_process_alive(Pid) of
601 end, {0, 0}, dynamics_db(RType, State#state.dynamics)),
603 supervisor -> [{specs, 1}, {active, Active},
604 {supervisors, Count}, {workers, 0}];
605 worker -> [{specs, 1}, {active, Active},
606 {supervisors, 0}, {workers, Count}]
608 {reply, Reply, State};
610 handle_call(count_children, _From, State) ->
611 %% Specs and children are together on the children list...
612 {Specs, Active, Supers, Workers} =
613 lists:foldl(fun(Child, Counts) ->
614 count_child(Child, Counts)
615 end, {0,0,0,0}, State#state.children),
617 %% Reformat counts to a property list.
618 Reply = [{specs, Specs}, {active, Active},
619 {supervisors, Supers}, {workers, Workers}],
620 {reply, Reply, State}.
623 count_child(#child{pid = Pid, child_type = worker},
624 {Specs, Active, Supers, Workers}) ->
625 case is_pid(Pid) andalso is_process_alive(Pid) of
626 true -> {Specs+1, Active+1, Supers, Workers+1};
627 false -> {Specs+1, Active, Supers, Workers+1}
629 count_child(#child{pid = Pid, child_type = supervisor},
630 {Specs, Active, Supers, Workers}) ->
631 case is_pid(Pid) andalso is_process_alive(Pid) of
632 true -> {Specs+1, Active+1, Supers+1, Workers};
633 false -> {Specs+1, Active, Supers+1, Workers}
637 %%% If a restart attempt failed, this message is sent via
638 %%% timer:apply_after(0,...) in order to give gen_server the chance to
639 %%% check it's inbox before trying again.
641 -spec handle_cast({try_again_restart, child_id() | pid(), term()}, state()) ->
642 {'noreply', state()} | {stop, shutdown, state()}.
644 handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State)
645 when ?is_simple(State) ->
646 RT = Child#child.restart_type,
647 RPid = restarting(Pid),
648 case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of
650 {M, F, _} = Child#child.mfargs,
651 NChild = Child#child{pid = RPid, mfargs = {M, F, Args}},
652 try_restart(Child#child.restart_type, Reason, NChild, State);
657 handle_cast({try_again_restart,Name,Reason}, State) ->
658 %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist
659 case lists:keysearch(Name,#child.name,State#state.children) of
660 {value, Child = #child{pid=?restarting(_), restart_type=RestartType}} ->
661 try_restart(RestartType, Reason, Child, State);
667 %% Take care of terminated children.
670 -spec handle_info(term(), state()) ->
671 {'noreply', state()} | {'stop', 'shutdown', state()}.
673 handle_info({'EXIT', Pid, Reason}, State) ->
674 case restart_child(Pid, Reason, State) of
677 {shutdown, State1} ->
678 {stop, shutdown, State1}
681 handle_info({delayed_restart, {RestartType, Reason, Child}}, State)
682 when ?is_simple(State) ->
683 try_restart(RestartType, Reason, Child, State);
684 handle_info({delayed_restart, {RestartType, Reason, Child}}, State) ->
685 case get_child(Child#child.name, State) of
687 try_restart(RestartType, Reason, Child1, State);
692 handle_info(Msg, State) ->
693 error_logger:error_msg("Supervisor received unexpected message: ~p~n",
698 %% Terminate this server.
701 -spec terminate(term(), state()) -> 'ok'.
703 terminate(_Reason, #state{children=[Child]} = State) when ?is_simple(State) ->
704 terminate_dynamic_children(Child, dynamics_db(Child#child.restart_type,
705 State#state.dynamics),
707 terminate(_Reason, State) ->
708 terminate_children(State#state.children, State#state.name).
711 %% Change code for the supervisor.
712 %% Call the new call-back module and fetch the new start specification.
713 %% Combine the new spec. with the old. If the new start spec. is
714 %% not valid the code change will not succeed.
715 %% Use the old Args as argument to Module:init/1.
716 %% NOTE: This requires that the init function of the call-back module
717 %% does not have any side effects.
720 -spec code_change(term(), state(), term()) ->
721 {'ok', state()} | {'error', term()}.
723 code_change(_, State, _) ->
724 case (State#state.module):init(State#state.args) of
725 {ok, {SupFlags, StartSpec}} ->
726 case catch check_flags(SupFlags) of
728 {Strategy, MaxIntensity, Period} = SupFlags,
729 update_childspec(State#state{strategy = Strategy,
730 intensity = MaxIntensity,
742 check_flags({Strategy, MaxIntensity, Period}) ->
743 validStrategy(Strategy),
744 validIntensity(MaxIntensity),
750 update_childspec(State, StartSpec) when ?is_simple(State) ->
751 case check_startspec(StartSpec) of
753 {ok, State#state{children = [Child]}};
757 update_childspec(State, StartSpec) ->
758 case check_startspec(StartSpec) of
760 OldC = State#state.children, % In reverse start order !
761 NewC = update_childspec1(OldC, Children, []),
762 {ok, State#state{children = NewC}};
767 update_childspec1([Child|OldC], Children, KeepOld) ->
768 case update_chsp(Child, Children) of
770 update_childspec1(OldC, NewChildren, KeepOld);
772 update_childspec1(OldC, Children, [Child|KeepOld])
774 update_childspec1([], Children, KeepOld) ->
775 %% Return them in (kept) reverse start order.
776 lists:reverse(Children ++ KeepOld).
778 update_chsp(OldCh, Children) ->
779 case lists:map(fun(Ch) when OldCh#child.name =:= Ch#child.name ->
780 Ch#child{pid = OldCh#child.pid};
786 false; % OldCh not found in new spec.
791 %%% ---------------------------------------------------
792 %%% Start a new child.
793 %%% ---------------------------------------------------
795 handle_start_child(Child, State) ->
796 case get_child(Child#child.name, State) of
798 case do_start_child(State#state.name, Child) of
799 {ok, undefined} when Child#child.restart_type =:= temporary ->
800 {{ok, undefined}, State};
802 {{ok, Pid}, save_child(Child#child{pid = Pid}, State)};
804 {{ok, Pid, Extra}, save_child(Child#child{pid = Pid}, State)};
806 {{error, {What, Child}}, State}
808 {value, OldChild} when is_pid(OldChild#child.pid) ->
809 {{error, {already_started, OldChild#child.pid}}, State};
810 {value, _OldChild} ->
811 {{error, already_present}, State}
814 %%% ---------------------------------------------------
815 %%% Restart. A process has terminated.
816 %%% Returns: {ok, state()} | {shutdown, state()}
817 %%% ---------------------------------------------------
819 restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) ->
820 RestartType = Child#child.restart_type,
821 case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of
823 {M, F, _} = Child#child.mfargs,
824 NChild = Child#child{pid = Pid, mfargs = {M, F, Args}},
825 do_restart(RestartType, Reason, NChild, State);
830 restart_child(Pid, Reason, State) ->
831 Children = State#state.children,
832 %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist
833 case lists:keysearch(Pid, #child.pid, Children) of
834 {value, #child{restart_type = RestartType} = Child} ->
835 do_restart(RestartType, Reason, Child, State);
840 try_restart(RestartType, Reason, Child, State) ->
841 case handle_restart(RestartType, Reason, Child, State) of
842 {ok, NState} -> {noreply, NState};
843 {shutdown, State2} -> {stop, shutdown, State2}
846 do_restart(RestartType, Reason, Child, State) ->
847 maybe_report_error(RestartType, Reason, Child, State),
848 handle_restart(RestartType, Reason, Child, State).
850 maybe_report_error(permanent, Reason, Child, State) ->
851 report_child_termination(Reason, Child, State);
852 maybe_report_error({permanent, _}, Reason, Child, State) ->
853 report_child_termination(Reason, Child, State);
854 maybe_report_error(_Type, Reason, Child, State) ->
855 case is_abnormal_termination(Reason) of
856 true -> report_child_termination(Reason, Child, State);
860 report_child_termination(Reason, Child, State) ->
861 report_error(child_terminated, Reason, Child, State#state.name).
863 handle_restart(permanent, _Reason, Child, State) ->
864 restart(Child, State);
865 handle_restart(transient, Reason, Child, State) ->
866 restart_if_explicit_or_abnormal(fun restart/2,
867 fun delete_child_and_continue/2,
868 Reason, Child, State);
869 handle_restart(intrinsic, Reason, Child, State) ->
870 restart_if_explicit_or_abnormal(fun restart/2,
871 fun delete_child_and_stop/2,
872 Reason, Child, State);
873 handle_restart(temporary, _Reason, Child, State) ->
874 delete_child_and_continue(Child, State);
875 handle_restart({permanent, _Delay}=Restart, Reason, Child, State) ->
876 do_restart_delay(Restart, Reason, Child, State);
877 handle_restart({transient, _Delay}=Restart, Reason, Child, State) ->
878 restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason),
879 fun delete_child_and_continue/2,
880 Reason, Child, State);
881 handle_restart({intrinsic, _Delay}=Restart, Reason, Child, State) ->
882 restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason),
883 fun delete_child_and_stop/2,
884 Reason, Child, State).
886 restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) ->
887 case ?is_explicit_restart(Reason) orelse is_abnormal_termination(Reason) of
888 true -> RestartHow(Child, State);
889 false -> Otherwise(Child, State)
892 defer_to_restart_delay(Restart, Reason) ->
893 fun(Child, State) -> do_restart_delay(Restart, Reason, Child, State) end.
895 delete_child_and_continue(Child, State) ->
896 {ok, state_del_child(Child, State)}.
898 delete_child_and_stop(Child, State) ->
899 {shutdown, state_del_child(Child, State)}.
901 is_abnormal_termination(normal) -> false;
902 is_abnormal_termination(shutdown) -> false;
903 is_abnormal_termination({shutdown, _}) -> false;
904 is_abnormal_termination(_Other) -> true.
906 do_restart_delay({RestartType, Delay}, Reason, Child, State) ->
907 case add_restart(State) of
909 maybe_restart(NState#state.strategy, Child, NState);
910 {terminate, _NState} ->
911 %% we've reached the max restart intensity, but the
912 %% add_restart will have added to the restarts
913 %% field. Given we don't want to die here, we need to go
914 %% back to the old restarts field otherwise we'll never
915 %% attempt to restart later, which is why we ignore
916 %% NState for this clause.
917 _TRef = erlang:send_after(trunc(Delay*1000), self(),
919 {{RestartType, Delay}, Reason, Child}}),
920 {ok, state_del_child(Child, State)}
923 restart(Child, State) ->
924 case add_restart(State) of
926 maybe_restart(NState#state.strategy, Child, NState);
927 {terminate, NState} ->
928 report_error(shutdown, reached_max_restart_intensity,
929 Child, State#state.name),
930 {shutdown, remove_child(Child, NState)}
933 maybe_restart(Strategy, Child, State) ->
934 case restart(Strategy, Child, State) of
935 {try_again, Reason, NState2} ->
936 %% Leaving control back to gen_server before
937 %% trying again. This way other incoming requsts
938 %% for the supervisor can be handled - e.g. a
939 %% shutdown request for the supervisor or the
941 Id = if ?is_simple(State) -> Child#child.pid;
942 true -> Child#child.name
944 timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]),
950 restart(simple_one_for_one, Child, State) ->
951 #child{pid = OldPid, mfargs = {M, F, A}} = Child,
952 Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type,
953 State#state.dynamics)),
954 case do_start_child_i(M, F, A) of
956 NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
959 NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
962 NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A,
964 report_error(start_error, Error, Child, State#state.name),
965 {try_again, Error, NState}
967 restart(one_for_one, Child, State) ->
968 OldPid = Child#child.pid,
969 case do_start_child(State#state.name, Child) of
971 NState = replace_child(Child#child{pid = Pid}, State),
974 NState = replace_child(Child#child{pid = Pid}, State),
977 NState = replace_child(Child#child{pid = restarting(OldPid)}, State),
978 report_error(start_error, Reason, Child, State#state.name),
979 {try_again, Reason, NState}
981 restart(rest_for_one, Child, State) ->
982 {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children),
983 ChAfter2 = terminate_children(ChAfter, State#state.name),
984 case start_children(ChAfter2, State#state.name) of
986 {ok, State#state{children = ChAfter3 ++ ChBefore}};
987 {error, ChAfter3, Reason} ->
988 NChild = Child#child{pid=restarting(Child#child.pid)},
989 NState = State#state{children = ChAfter3 ++ ChBefore},
990 {try_again, Reason, replace_child(NChild,NState)}
992 restart(one_for_all, Child, State) ->
993 Children1 = del_child(Child#child.pid, State#state.children),
994 Children2 = terminate_children(Children1, State#state.name),
995 case start_children(Children2, State#state.name) of
997 {ok, State#state{children = NChs}};
998 {error, NChs, Reason} ->
999 NChild = Child#child{pid=restarting(Child#child.pid)},
1000 NState = State#state{children = NChs},
1001 {try_again, Reason, replace_child(NChild,NState)}
1004 restarting(Pid) when is_pid(Pid) -> ?restarting(Pid);
1005 restarting(RPid) -> RPid.
1007 %%-----------------------------------------------------------------
1008 %% Func: terminate_children/2
1009 %% Args: Children = [child_rec()] in termination order
1010 %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
1011 %% Returns: NChildren = [child_rec()] in
1012 %% startup order (reversed termination order)
1013 %%-----------------------------------------------------------------
1014 terminate_children(Children, SupName) ->
1015 terminate_children(Children, SupName, []).
1017 %% Temporary children should not be restarted and thus should
1018 %% be skipped when building the list of terminated children, although
1019 %% we do want them to be shut down as many functions from this module
1020 %% use this function to just clear everything.
1021 terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) ->
1022 do_terminate(Child, SupName),
1023 terminate_children(Children, SupName, Res);
1024 terminate_children([Child | Children], SupName, Res) ->
1025 NChild = do_terminate(Child, SupName),
1026 terminate_children(Children, SupName, [NChild | Res]);
1027 terminate_children([], _SupName, Res) ->
1030 do_terminate(Child, SupName) when is_pid(Child#child.pid) ->
1031 case shutdown(Child#child.pid, Child#child.shutdown) of
1034 {error, normal} when not ?is_permanent(Child#child.restart_type) ->
1036 {error, OtherReason} ->
1037 report_error(shutdown_error, OtherReason, Child, SupName)
1039 Child#child{pid = undefined};
1040 do_terminate(Child, _SupName) ->
1041 Child#child{pid = undefined}.
1043 %%-----------------------------------------------------------------
1044 %% Shutdowns a child. We must check the EXIT value
1045 %% of the child, because it might have died with another reason than
1046 %% the wanted. In that case we want to report the error. We put a
1047 %% monitor on the child an check for the 'DOWN' message instead of
1048 %% checking for the 'EXIT' message, because if we check the 'EXIT'
1049 %% message a "naughty" child, who does unlink(Sup), could hang the
1051 %% Returns: ok | {error, OtherReason} (this should be reported)
1052 %%-----------------------------------------------------------------
1053 shutdown(Pid, brutal_kill) ->
1054 case monitor_child(Pid) of
1058 {'DOWN', _MRef, process, Pid, killed} ->
1060 {'DOWN', _MRef, process, Pid, OtherReason} ->
1061 {error, OtherReason}
1066 shutdown(Pid, Time) ->
1067 case monitor_child(Pid) of
1069 exit(Pid, shutdown), %% Try to shutdown gracefully
1071 {'DOWN', _MRef, process, Pid, shutdown} ->
1073 {'DOWN', _MRef, process, Pid, OtherReason} ->
1074 {error, OtherReason}
1076 exit(Pid, kill), %% Force termination.
1078 {'DOWN', _MRef, process, Pid, OtherReason} ->
1079 {error, OtherReason}
1086 %% Help function to shutdown/2 switches from link to monitor approach
1087 monitor_child(Pid) ->
1089 %% Do the monitor operation first so that if the child dies
1090 %% before the monitoring is done causing a 'DOWN'-message with
1091 %% reason noproc, we will get the real reason in the 'EXIT'-message
1092 %% unless a naughty child has already done unlink...
1093 erlang:monitor(process, Pid),
1097 %% If the child dies before the unlik we must empty
1098 %% the mail-box of the 'EXIT'-message and the 'DOWN'-message.
1099 {'EXIT', Pid, Reason} ->
1101 {'DOWN', _, process, Pid, _} ->
1105 %% If a naughty child did unlink and the child dies before
1106 %% monitor the result will be that shutdown/2 receives a
1107 %% 'DOWN'-message with reason noproc.
1108 %% If the child should die after the unlink there
1109 %% will be a 'DOWN'-message with a correct reason
1110 %% that will be handled in shutdown/2.
1115 %%-----------------------------------------------------------------
1116 %% Func: terminate_dynamic_children/3
1117 %% Args: Child = child_rec()
1118 %% Dynamics = ?DICT() | ?SET()
1119 %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
1123 %% Shutdown all dynamic children. This happens when the supervisor is
1124 %% stopped. Because the supervisor can have millions of dynamic children, we
1125 %% can have an significative overhead here.
1126 %%-----------------------------------------------------------------
1127 terminate_dynamic_children(Child, Dynamics, SupName) ->
1128 {Pids, EStack0} = monitor_dynamic_children(Child, Dynamics),
1129 Sz = ?SETS:size(Pids),
1130 EStack = case Child#child.shutdown of
1132 ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
1133 wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
1135 ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
1136 wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
1138 ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
1139 TRef = erlang:start_timer(Time, self(), kill),
1140 wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
1142 %% Unroll stacked errors and report them
1143 ?DICT:fold(fun(Reason, Ls, _) ->
1144 report_error(shutdown_error, Reason,
1145 Child#child{pid=Ls}, SupName)
1149 monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) ->
1150 ?SETS:fold(fun(P, {Pids, EStack}) ->
1151 case monitor_child(P) of
1153 {?SETS:add_element(P, Pids), EStack};
1157 {Pids, ?DICT:append(Reason, P, EStack)}
1159 end, {?SETS:new(), ?DICT:new()}, Dynamics);
1160 monitor_dynamic_children(#child{restart_type=RType}, Dynamics) ->
1161 ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) ->
1162 case monitor_child(P) of
1164 {?SETS:add_element(P, Pids), EStack};
1165 {error, normal} when not ?is_permanent(RType) ->
1168 {Pids, ?DICT:append(Reason, P, EStack)}
1170 (?restarting(_), _, {Pids, EStack}) ->
1172 end, {?SETS:new(), ?DICT:new()}, Dynamics).
1174 wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) ->
1176 wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) ->
1177 %% If the timer has expired before its cancellation, we must empty the
1178 %% mail-box of the 'timeout'-message.
1179 erlang:cancel_timer(TRef),
1181 {timeout, TRef, kill} ->
1186 wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz,
1189 {'DOWN', _MRef, process, Pid, killed} ->
1190 wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
1193 {'DOWN', _MRef, process, Pid, Reason} ->
1194 wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
1195 TRef, ?DICT:append(Reason, Pid, EStack))
1197 wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz,
1200 {'DOWN', _MRef, process, Pid, shutdown} ->
1201 wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
1204 {'DOWN', _MRef, process, Pid, normal} when not ?is_permanent(RType) ->
1205 wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
1208 {'DOWN', _MRef, process, Pid, Reason} ->
1209 wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1,
1210 TRef, ?DICT:append(Reason, Pid, EStack));
1212 {timeout, TRef, kill} ->
1213 ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
1214 wait_dynamic_children(Child, Pids, Sz-1, undefined, EStack)
1217 %%-----------------------------------------------------------------
1218 %% Child/State manipulating functions.
1219 %%-----------------------------------------------------------------
1221 %% Note we do not want to save the parameter list for temporary processes as
1222 %% they will not be restarted, and hence we do not need this information.
1223 %% Especially for dynamic children to simple_one_for_one supervisors
1224 %% it could become very costly as it is not uncommon to spawn
1225 %% very many such processes.
1226 save_child(#child{restart_type = temporary,
1227 mfargs = {M, F, _}} = Child, #state{children = Children} = State) ->
1228 State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]};
1229 save_child(Child, #state{children = Children} = State) ->
1230 State#state{children = [Child |Children]}.
1232 save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) ->
1233 State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))};
1234 save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) ->
1235 State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}.
1237 dynamics_db(temporary, undefined) ->
1239 dynamics_db(_, undefined) ->
1241 dynamics_db(_,Dynamics) ->
1244 dynamic_child_args(Pid, Dynamics) ->
1245 case ?SETS:is_set(Dynamics) of
1249 ?DICT:find(Pid, Dynamics)
1252 state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) ->
1253 NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)),
1254 State#state{dynamics = NDynamics};
1255 state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) ->
1256 NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)),
1257 State#state{dynamics = NDynamics};
1258 state_del_child(Child, State) ->
1259 NChildren = del_child(Child#child.name, State#state.children),
1260 State#state{children = NChildren}.
1262 del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) when Ch#child.name =:= Name ->
1264 del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary ->
1266 del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name ->
1267 [Ch#child{pid = undefined} | Chs];
1268 del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary ->
1270 del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid ->
1271 [Ch#child{pid = undefined} | Chs];
1272 del_child(Name, [Ch|Chs]) ->
1273 [Ch|del_child(Name, Chs)];
1277 %% Chs = [S4, S3, Ch, S1, S0]
1278 %% Ret: {[S4, S3, Ch], [S1, S0]}
1279 split_child(Name, Chs) ->
1280 split_child(Name, Chs, []).
1282 split_child(Name, [Ch|Chs], After) when Ch#child.name =:= Name ->
1283 {lists:reverse([Ch#child{pid = undefined} | After]), Chs};
1284 split_child(Pid, [Ch|Chs], After) when Ch#child.pid =:= Pid ->
1285 {lists:reverse([Ch#child{pid = undefined} | After]), Chs};
1286 split_child(Name, [Ch|Chs], After) ->
1287 split_child(Name, Chs, [Ch | After]);
1288 split_child(_, [], After) ->
1289 {lists:reverse(After), []}.
1291 get_child(Name, State) ->
1292 get_child(Name, State, false).
1293 get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) ->
1294 get_dynamic_child(Pid, State);
1295 get_child(Name, State, _) ->
1296 lists:keysearch(Name, #child.name, State#state.children).
1298 get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) ->
1299 DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics),
1300 case is_dynamic_pid(Pid, DynamicsDb) of
1302 {value, Child#child{pid=Pid}};
1304 RPid = restarting(Pid),
1305 case is_dynamic_pid(RPid, DynamicsDb) of
1307 {value, Child#child{pid=RPid}};
1309 case erlang:is_process_alive(Pid) of
1311 false -> {value, Child}
1316 is_dynamic_pid(Pid, Dynamics) ->
1317 case ?SETS:is_set(Dynamics) of
1319 ?SETS:is_element(Pid, Dynamics);
1321 ?DICT:is_key(Pid, Dynamics)
1324 replace_child(Child, State) ->
1325 Chs = do_replace_child(Child, State#state.children),
1326 State#state{children = Chs}.
1328 do_replace_child(Child, [Ch|Chs]) when Ch#child.name =:= Child#child.name ->
1330 do_replace_child(Child, [Ch|Chs]) ->
1331 [Ch|do_replace_child(Child, Chs)].
1333 remove_child(Child, State) ->
1334 Chs = lists:keydelete(Child#child.name, #child.name, State#state.children),
1335 State#state{children = Chs}.
1337 %%-----------------------------------------------------------------
1338 %% Func: init_state/4
1339 %% Args: SupName = {local, atom()} | {global, atom()} | self
1340 %% Type = {Strategy, MaxIntensity, Period}
1341 %% Strategy = one_for_one | one_for_all | simple_one_for_one |
1343 %% MaxIntensity = integer() >= 0
1344 %% Period = integer() > 0
1347 %% Purpose: Check that Type is of correct type (!)
1348 %% Returns: {ok, state()} | Error
1349 %%-----------------------------------------------------------------
1350 init_state(SupName, Type, Mod, Args) ->
1351 case catch init_state1(SupName, Type, Mod, Args) of
1358 init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
1359 validStrategy(Strategy),
1360 validIntensity(MaxIntensity),
1361 validPeriod(Period),
1362 {ok, #state{name = supname(SupName,Mod),
1363 strategy = Strategy,
1364 intensity = MaxIntensity,
1368 init_state1(_SupName, Type, _, _) ->
1369 {invalid_type, Type}.
1371 validStrategy(simple_one_for_one) -> true;
1372 validStrategy(one_for_one) -> true;
1373 validStrategy(one_for_all) -> true;
1374 validStrategy(rest_for_one) -> true;
1375 validStrategy(What) -> throw({invalid_strategy, What}).
1377 validIntensity(Max) when is_integer(Max),
1379 validIntensity(What) -> throw({invalid_intensity, What}).
1381 validPeriod(Period) when is_integer(Period),
1383 validPeriod(What) -> throw({invalid_period, What}).
1385 supname(self, Mod) -> {self(), Mod};
1388 %%% ------------------------------------------------------
1389 %%% Check that the children start specification is valid.
1390 %%% Shall be a six (6) tuple
1391 %%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
1392 %%% where Name is an atom
1393 %%% Func is {Mod, Fun, Args} == {atom(), atom(), list()}
1394 %%% RestartType is permanent | temporary | transient |
1395 %%% intrinsic | {permanent, Delay} |
1396 %%% {transient, Delay} | {intrinsic, Delay}
1398 %%% Shutdown = integer() > 0 | infinity | brutal_kill
1399 %%% ChildType = supervisor | worker
1400 %%% Modules = [atom()] | dynamic
1401 %%% Returns: {ok, [child_rec()]} | Error
1402 %%% ------------------------------------------------------
1404 check_startspec(Children) -> check_startspec(Children, []).
1406 check_startspec([ChildSpec|T], Res) ->
1407 case check_childspec(ChildSpec) of
1409 case lists:keymember(Child#child.name, #child.name, Res) of
1410 true -> {duplicate_child_name, Child#child.name};
1411 false -> check_startspec(T, [Child | Res])
1415 check_startspec([], Res) ->
1416 {ok, lists:reverse(Res)}.
1418 check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) ->
1419 catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods);
1420 check_childspec(X) -> {invalid_child_spec, X}.
1422 check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
1425 validRestartType(RestartType),
1426 validChildType(ChildType),
1427 validShutdown(Shutdown, ChildType),
1429 {ok, #child{name = Name, mfargs = Func, restart_type = RestartType,
1430 shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
1432 validChildType(supervisor) -> true;
1433 validChildType(worker) -> true;
1434 validChildType(What) -> throw({invalid_child_type, What}).
1436 validName(_Name) -> true.
1438 validFunc({M, F, A}) when is_atom(M),
1441 validFunc(Func) -> throw({invalid_mfa, Func}).
1443 validRestartType(permanent) -> true;
1444 validRestartType(temporary) -> true;
1445 validRestartType(transient) -> true;
1446 validRestartType(intrinsic) -> true;
1447 validRestartType({permanent, Delay}) -> validDelay(Delay);
1448 validRestartType({intrinsic, Delay}) -> validDelay(Delay);
1449 validRestartType({transient, Delay}) -> validDelay(Delay);
1450 validRestartType(RestartType) -> throw({invalid_restart_type,
1453 validDelay(Delay) when is_number(Delay),
1455 validDelay(What) -> throw({invalid_delay, What}).
1457 validShutdown(Shutdown, _)
1458 when is_integer(Shutdown), Shutdown > 0 -> true;
1459 validShutdown(infinity, _) -> true;
1460 validShutdown(brutal_kill, _) -> true;
1461 validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}).
1463 validMods(dynamic) -> true;
1464 validMods(Mods) when is_list(Mods) ->
1465 lists:foreach(fun(Mod) ->
1468 true -> throw({invalid_module, Mod})
1472 validMods(Mods) -> throw({invalid_modules, Mods}).
1474 %%% ------------------------------------------------------
1475 %%% Add a new restart and calculate if the max restart
1476 %%% intensity has been reached (in that case the supervisor
1477 %%% shall terminate).
1478 %%% All restarts accured inside the period amount of seconds
1479 %%% are kept in the #state.restarts list.
1480 %%% Returns: {ok, State'} | {terminate, State'}
1481 %%% ------------------------------------------------------
1483 add_restart(State) ->
1484 I = State#state.intensity,
1485 P = State#state.period,
1486 R = State#state.restarts,
1488 R1 = add_restart([Now|R], Now, P),
1489 State1 = State#state{restarts = R1},
1491 CurI when CurI =< I ->
1497 add_restart([R|Restarts], Now, Period) ->
1498 case inPeriod(R, Now, Period) of
1500 [R|add_restart(Restarts, Now, Period)];
1504 add_restart([], _, _) ->
1507 inPeriod(Time, Now, Period) ->
1508 case difference(Time, Now) of
1509 T when T > Period ->
1516 %% Time = {MegaSecs, Secs, MicroSecs} (NOTE: MicroSecs is ignored)
1517 %% Calculate the time elapsed in seconds between two timestamps.
1518 %% If MegaSecs is equal just subtract Secs.
1519 %% Else calculate the Mega difference and add the Secs difference,
1520 %% note that Secs difference can be negative, e.g.
1521 %% {827, 999999, 676} diff {828, 1, 653753} == > 2 secs.
1523 difference({TimeM, TimeS, _}, {CurM, CurS, _}) when CurM > TimeM ->
1524 ((CurM - TimeM) * 1000000) + (CurS - TimeS);
1525 difference({_, TimeS, _}, {_, CurS, _}) ->
1528 %%% ------------------------------------------------------
1529 %%% Error and progress reporting.
1530 %%% ------------------------------------------------------
1532 report_error(Error, Reason, Child, SupName) ->
1533 ErrorMsg = [{supervisor, SupName},
1534 {errorContext, Error},
1536 {offender, extract_child(Child)}],
1537 error_logger:error_report(supervisor_report, ErrorMsg).
1540 extract_child(Child) when is_list(Child#child.pid) ->
1541 [{nb_children, length(Child#child.pid)},
1542 {name, Child#child.name},
1543 {mfargs, Child#child.mfargs},
1544 {restart_type, Child#child.restart_type},
1545 {shutdown, Child#child.shutdown},
1546 {child_type, Child#child.child_type}];
1547 extract_child(Child) ->
1548 [{pid, Child#child.pid},
1549 {name, Child#child.name},
1550 {mfargs, Child#child.mfargs},
1551 {restart_type, Child#child.restart_type},
1552 {shutdown, Child#child.shutdown},
1553 {child_type, Child#child.child_type}].
1555 report_progress(Child, SupName) ->
1556 Progress = [{supervisor, SupName},
1557 {started, extract_child(Child)}],
1558 error_logger:info_report(progress, Progress).