PageRenderTime 154ms CodeModel.GetById 2ms app.highlight 138ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/stdlib/src/supervisor.erl

https://github.com/bsmr-erlang/otp
Erlang | 1474 lines | 1113 code | 153 blank | 208 comment | 1 complexity | 38e1bfa0e39eea4aa60205ba44efd0b4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1%%
   2%% %CopyrightBegin%
   3%%
   4%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
   5%%
   6%% Licensed under the Apache License, Version 2.0 (the "License");
   7%% you may not use this file except in compliance with the License.
   8%% You may obtain a copy of the License at
   9%%
  10%%     http://www.apache.org/licenses/LICENSE-2.0
  11%%
  12%% Unless required by applicable law or agreed to in writing, software
  13%% distributed under the License is distributed on an "AS IS" BASIS,
  14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15%% See the License for the specific language governing permissions and
  16%% limitations under the License.
  17%%
  18%% %CopyrightEnd%
  19%%
  20-module(supervisor).
  21
  22-behaviour(gen_server).
  23
  24%% External exports
  25-export([start_link/2, start_link/3,
  26	 start_child/2, restart_child/2,
  27	 delete_child/2, terminate_child/2,
  28	 which_children/1, count_children/1,
  29	 check_childspecs/1, get_childspec/2]).
  30
  31%% Internal exports
  32-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  33	 terminate/2, code_change/3, format_status/2]).
  34
  35%% For release_handler only
  36-export([get_callback_module/1]).
  37
  38-include("logger.hrl").
  39
  40-define(report_error(Error, Reason, Child, SupName),
  41        ?LOG_ERROR(#{label=>{supervisor,Error},
  42                     report=>[{supervisor,SupName},
  43                              {errorContext,Error},
  44                              {reason,Reason},
  45                              {offender,extract_child(Child)}]},
  46                   #{domain=>[otp,sasl],
  47                     report_cb=>fun logger:format_otp_report/1,
  48                     logger_formatter=>#{title=>"SUPERVISOR REPORT"},
  49                     error_logger=>#{tag=>error_report,
  50                                     type=>supervisor_report}})).
  51
  52%%--------------------------------------------------------------------------
  53
  54-export_type([sup_flags/0, child_spec/0, startchild_ret/0, strategy/0]).
  55
  56%%--------------------------------------------------------------------------
  57
  58-type child()    :: 'undefined' | pid().
  59-type child_id() :: term().
  60-type mfargs()   :: {M :: module(), F :: atom(), A :: [term()] | undefined}.
  61-type modules()  :: [module()] | 'dynamic'.
  62-type restart()  :: 'permanent' | 'transient' | 'temporary'.
  63-type shutdown() :: 'brutal_kill' | timeout().
  64-type worker()   :: 'worker' | 'supervisor'.
  65-type sup_name() :: {'local', Name :: atom()}
  66                  | {'global', Name :: atom()}
  67                  | {'via', Module :: module(), Name :: any()}.
  68-type sup_ref()  :: (Name :: atom())
  69                  | {Name :: atom(), Node :: node()}
  70                  | {'global', Name :: atom()}
  71                  | {'via', Module :: module(), Name :: any()}
  72                  | pid().
  73-type child_spec() :: #{id := child_id(),       % mandatory
  74			start := mfargs(),      % mandatory
  75			restart => restart(),   % optional
  76			shutdown => shutdown(), % optional
  77			type => worker(),       % optional
  78			modules => modules()}   % optional
  79                    | {Id :: child_id(),
  80                       StartFunc :: mfargs(),
  81                       Restart :: restart(),
  82                       Shutdown :: shutdown(),
  83                       Type :: worker(),
  84                       Modules :: modules()}.
  85
  86-type strategy() :: 'one_for_all' | 'one_for_one'
  87                  | 'rest_for_one' | 'simple_one_for_one'.
  88
  89-type sup_flags() :: #{strategy => strategy(),         % optional
  90		       intensity => non_neg_integer(), % optional
  91		       period => pos_integer()}        % optional
  92                   | {RestartStrategy :: strategy(),
  93                      Intensity :: non_neg_integer(),
  94                      Period :: pos_integer()}.
  95-type children() :: {Ids :: [child_id()], Db :: #{child_id() => child_rec()}}.
  96
  97%%--------------------------------------------------------------------------
  98%% Defaults
  99-define(default_flags, #{strategy  => one_for_one,
 100			 intensity => 1,
 101			 period    => 5}).
 102-define(default_child_spec, #{restart  => permanent,
 103			      type     => worker}).
 104%% Default 'shutdown' is 5000 for workers and infinity for supervisors.
 105%% Default 'modules' is [M], where M comes from the child's start {M,F,A}.
 106
 107%%--------------------------------------------------------------------------
 108
 109-record(child, {% pid is undefined when child is not running
 110	        pid = undefined :: child()
 111	                         | {restarting, pid() | undefined}
 112	                         | [pid()],
 113		id              :: child_id(),
 114		mfargs          :: mfargs(),
 115		restart_type    :: restart(),
 116		shutdown        :: shutdown(),
 117		child_type      :: worker(),
 118		modules = []    :: modules()}).
 119-type child_rec() :: #child{}.
 120
 121-record(state, {name,
 122		strategy               :: strategy() | 'undefined',
 123		children = {[],#{}}    :: children(), % Ids in start order
 124                dynamics               :: {'maps', #{pid() => list()}}
 125                                        | {'sets', sets:set(pid())}
 126                                        | 'undefined',
 127		intensity              :: non_neg_integer() | 'undefined',
 128		period                 :: pos_integer() | 'undefined',
 129		restarts = [],
 130		dynamic_restarts = 0   :: non_neg_integer(),
 131	        module,
 132	        args}).
 133-type state() :: #state{}.
 134
 135-define(is_simple(State), State#state.strategy =:= simple_one_for_one).
 136-define(is_temporary(_Child_), _Child_#child.restart_type=:=temporary).
 137-define(is_transient(_Child_), _Child_#child.restart_type=:=transient).
 138-define(is_permanent(_Child_), _Child_#child.restart_type=:=permanent).
 139
 140-callback init(Args :: term()) ->
 141    {ok, {SupFlags :: sup_flags(), [ChildSpec :: child_spec()]}}
 142    | ignore.
 143
 144-define(restarting(_Pid_), {restarting,_Pid_}).
 145
 146%%% ---------------------------------------------------
 147%%% This is a general process supervisor built upon gen_server.erl.
 148%%% Servers/processes should/could also be built using gen_server.erl.
 149%%% SupName = {local, atom()} | {global, atom()}.
 150%%% ---------------------------------------------------
 151
 152-type startlink_err() :: {'already_started', pid()}
 153                         | {'shutdown', term()}
 154                         | term().
 155-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
 156
 157-spec start_link(Module, Args) -> startlink_ret() when
 158      Module :: module(),
 159      Args :: term().
 160start_link(Mod, Args) ->
 161    gen_server:start_link(supervisor, {self, Mod, Args}, []).
 162 
 163-spec start_link(SupName, Module, Args) -> startlink_ret() when
 164      SupName :: sup_name(),
 165      Module :: module(),
 166      Args :: term().
 167start_link(SupName, Mod, Args) ->
 168    gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []).
 169 
 170%%% ---------------------------------------------------
 171%%% Interface functions.
 172%%% ---------------------------------------------------
 173
 174-type startchild_err() :: 'already_present'
 175			| {'already_started', Child :: child()} | term().
 176-type startchild_ret() :: {'ok', Child :: child()}
 177                        | {'ok', Child :: child(), Info :: term()}
 178			| {'error', startchild_err()}.
 179
 180-spec start_child(SupRef, ChildSpec) -> startchild_ret() when
 181      SupRef :: sup_ref(),
 182      ChildSpec :: child_spec() | (List :: [term()]).
 183start_child(Supervisor, ChildSpec) ->
 184    call(Supervisor, {start_child, ChildSpec}).
 185
 186-spec restart_child(SupRef, Id) -> Result when
 187      SupRef :: sup_ref(),
 188      Id :: child_id(),
 189      Result :: {'ok', Child :: child()}
 190              | {'ok', Child :: child(), Info :: term()}
 191              | {'error', Error},
 192      Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' |
 193	       term().
 194restart_child(Supervisor, Id) ->
 195    call(Supervisor, {restart_child, Id}).
 196
 197-spec delete_child(SupRef, Id) -> Result when
 198      SupRef :: sup_ref(),
 199      Id :: child_id(),
 200      Result :: 'ok' | {'error', Error},
 201      Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'.
 202delete_child(Supervisor, Id) ->
 203    call(Supervisor, {delete_child, Id}).
 204
 205%%-----------------------------------------------------------------
 206%% Func: terminate_child/2
 207%% Returns: ok | {error, Reason}
 208%%          Note that the child is *always* terminated in some
 209%%          way (maybe killed).
 210%%-----------------------------------------------------------------
 211
 212-spec terminate_child(SupRef, Id) -> Result when
 213      SupRef :: sup_ref(),
 214      Id :: pid() | child_id(),
 215      Result :: 'ok' | {'error', Error},
 216      Error :: 'not_found' | 'simple_one_for_one'.
 217terminate_child(Supervisor, Id) ->
 218    call(Supervisor, {terminate_child, Id}).
 219
 220-spec get_childspec(SupRef, Id) -> Result when
 221      SupRef :: sup_ref(),
 222      Id :: pid() | child_id(),
 223      Result :: {'ok', child_spec()} | {'error', Error},
 224      Error :: 'not_found'.
 225get_childspec(Supervisor, Id) ->
 226    call(Supervisor, {get_childspec, Id}).
 227
 228-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when
 229      SupRef :: sup_ref(),
 230      Id :: child_id() | undefined,
 231      Child :: child() | 'restarting',
 232      Type :: worker(),
 233      Modules :: modules().
 234which_children(Supervisor) ->
 235    call(Supervisor, which_children).
 236
 237-spec count_children(SupRef) -> PropListOfCounts when
 238      SupRef :: sup_ref(),
 239      PropListOfCounts :: [Count],
 240      Count :: {specs, ChildSpecCount :: non_neg_integer()}
 241             | {active, ActiveProcessCount :: non_neg_integer()}
 242             | {supervisors, ChildSupervisorCount :: non_neg_integer()}
 243             |{workers, ChildWorkerCount :: non_neg_integer()}.
 244count_children(Supervisor) ->
 245    call(Supervisor, count_children).
 246
 247call(Supervisor, Req) ->
 248    gen_server:call(Supervisor, Req, infinity).
 249
 250-spec check_childspecs(ChildSpecs) -> Result when
 251      ChildSpecs :: [child_spec()],
 252      Result :: 'ok' | {'error', Error :: term()}.
 253check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
 254    case check_startspec(ChildSpecs) of
 255	{ok, _} -> ok;
 256	Error -> {error, Error}
 257    end;
 258check_childspecs(X) -> {error, {badarg, X}}.
 259
 260%%%-----------------------------------------------------------------
 261%%% Called by release_handler during upgrade
 262-spec get_callback_module(Pid) -> Module when
 263      Pid :: pid(),
 264      Module :: atom().
 265get_callback_module(Pid) ->
 266    {status, _Pid, {module, _Mod},
 267     [_PDict, _SysState, _Parent, _Dbg, Misc]} = sys:get_status(Pid),
 268    case lists:keyfind(supervisor, 1, Misc) of
 269	{supervisor, [{"Callback", Mod}]} ->
 270	    Mod;
 271	_ ->
 272	    [_Header, _Data, {data, [{"State", State}]} | _] = Misc,
 273	    State#state.module
 274    end.
 275
 276%%% ---------------------------------------------------
 277%%% 
 278%%% Initialize the supervisor.
 279%%% 
 280%%% ---------------------------------------------------
 281
 282-type init_sup_name() :: sup_name() | 'self'.
 283
 284-type stop_rsn() :: {'shutdown', term()}
 285                  | {'bad_return', {module(),'init', term()}}
 286                  | {'bad_start_spec', term()}
 287                  | {'start_spec', term()}
 288                  | {'supervisor_data', term()}.
 289
 290-spec init({init_sup_name(), module(), [term()]}) ->
 291        {'ok', state()} | 'ignore' | {'stop', stop_rsn()}.
 292
 293init({SupName, Mod, Args}) ->
 294    process_flag(trap_exit, true),
 295    case Mod:init(Args) of
 296	{ok, {SupFlags, StartSpec}} ->
 297	    case init_state(SupName, SupFlags, Mod, Args) of
 298		{ok, State} when ?is_simple(State) ->
 299		    init_dynamic(State, StartSpec);
 300		{ok, State} ->
 301		    init_children(State, StartSpec);
 302		Error ->
 303		    {stop, {supervisor_data, Error}}
 304	    end;
 305	ignore ->
 306	    ignore;
 307	Error ->
 308	    {stop, {bad_return, {Mod, init, Error}}}
 309    end.
 310
 311init_children(State, StartSpec) ->
 312    SupName = State#state.name,
 313    case check_startspec(StartSpec) of
 314        {ok, Children} ->
 315            case start_children(Children, SupName) of
 316                {ok, NChildren} ->
 317                    {ok, State#state{children = NChildren}};
 318                {error, NChildren, Reason} ->
 319                    _ = terminate_children(NChildren, SupName),
 320                    {stop, {shutdown, Reason}}
 321            end;
 322        Error ->
 323            {stop, {start_spec, Error}}
 324    end.
 325
 326init_dynamic(State, [StartSpec]) ->
 327    case check_startspec([StartSpec]) of
 328        {ok, Children} ->
 329	    {ok, dyn_init(State#state{children = Children})};
 330        Error ->
 331            {stop, {start_spec, Error}}
 332    end;
 333init_dynamic(_State, StartSpec) ->
 334    {stop, {bad_start_spec, StartSpec}}.
 335
 336%%-----------------------------------------------------------------
 337%% Func: start_children/2
 338%% Args: Children = children() % Ids in start order
 339%%       SupName = {local, atom()} | {global, atom()} | {pid(), Mod}
 340%% Purpose: Start all children.  The new map contains #child's
 341%%          with pids.
 342%% Returns: {ok, NChildren} | {error, NChildren, Reason}
 343%%          NChildren = children() % Ids in termination order
 344%%                                   (reversed start order)
 345%%-----------------------------------------------------------------
 346start_children(Children, SupName) ->
 347    Start =
 348        fun(Id,Child) ->
 349                case do_start_child(SupName, Child) of
 350                    {ok, undefined} when ?is_temporary(Child) ->
 351                        remove;
 352                    {ok, Pid} ->
 353                        {update,Child#child{pid = Pid}};
 354                    {ok, Pid, _Extra} ->
 355                        {update,Child#child{pid = Pid}};
 356                    {error, Reason} ->
 357                        ?report_error(start_error, Reason, Child, SupName),
 358                        {abort,{failed_to_start_child,Id,Reason}}
 359                end
 360        end,
 361    children_map(Start,Children).
 362
 363do_start_child(SupName, Child) ->
 364    #child{mfargs = {M, F, Args}} = Child,
 365    case do_start_child_i(M, F, Args) of
 366	{ok, Pid} when is_pid(Pid) ->
 367	    NChild = Child#child{pid = Pid},
 368	    report_progress(NChild, SupName),
 369	    {ok, Pid};
 370	{ok, Pid, Extra} when is_pid(Pid) ->
 371	    NChild = Child#child{pid = Pid},
 372	    report_progress(NChild, SupName),
 373	    {ok, Pid, Extra};
 374        Other ->
 375            Other
 376    end.
 377
 378do_start_child_i(M, F, A) ->
 379    case catch apply(M, F, A) of
 380	{ok, Pid} when is_pid(Pid) ->
 381	    {ok, Pid};
 382	{ok, Pid, Extra} when is_pid(Pid) ->
 383	    {ok, Pid, Extra};
 384	ignore ->
 385	    {ok, undefined};
 386	{error, Error} ->
 387	    {error, Error};
 388	What ->
 389	    {error, What}
 390    end.
 391
 392%%% ---------------------------------------------------
 393%%% 
 394%%% Callback functions.
 395%%% 
 396%%% ---------------------------------------------------
 397-type call() :: 'which_children' | 'count_children' | {_, _}.	% XXX: refine
 398-spec handle_call(call(), term(), state()) -> {'reply', term(), state()}.
 399
 400handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
 401    Child = get_dynamic_child(State),
 402    #child{mfargs = {M, F, A}} = Child,
 403    Args = A ++ EArgs,
 404    case do_start_child_i(M, F, Args) of
 405	{ok, undefined} ->
 406	    {reply, {ok, undefined}, State};
 407	{ok, Pid} ->
 408	    NState = dyn_store(Pid, Args, State),
 409	    {reply, {ok, Pid}, NState};
 410	{ok, Pid, Extra} ->
 411	    NState = dyn_store(Pid, Args, State),
 412	    {reply, {ok, Pid, Extra}, NState};
 413	What ->
 414	    {reply, What, State}
 415    end;
 416
 417handle_call({start_child, ChildSpec}, _From, State) ->
 418    case check_childspec(ChildSpec) of
 419	{ok, Child} ->
 420	    {Resp, NState} = handle_start_child(Child, State),
 421	    {reply, Resp, NState};
 422	What ->
 423	    {reply, {error, What}, State}
 424    end;
 425
 426%% terminate_child for simple_one_for_one can only be done with pid
 427handle_call({terminate_child, Id}, _From, State) when not is_pid(Id),
 428                                                      ?is_simple(State) ->
 429    {reply, {error, simple_one_for_one}, State};
 430
 431handle_call({terminate_child, Id}, _From, State) ->
 432    case find_child(Id, State) of
 433	{ok, Child} ->
 434	    do_terminate(Child, State#state.name),
 435            {reply, ok, del_child(Child, State)};
 436	error ->
 437	    {reply, {error, not_found}, State}
 438    end;
 439
 440%% restart_child request is invalid for simple_one_for_one supervisors
 441handle_call({restart_child, _Id}, _From, State) when ?is_simple(State) ->
 442    {reply, {error, simple_one_for_one}, State};
 443
 444handle_call({restart_child, Id}, _From, State) ->
 445    case find_child(Id, State) of
 446	{ok, Child} when Child#child.pid =:= undefined ->
 447	    case do_start_child(State#state.name, Child) of
 448		{ok, Pid} ->
 449		    NState = set_pid(Pid, Id, State),
 450		    {reply, {ok, Pid}, NState};
 451		{ok, Pid, Extra} ->
 452		    NState = set_pid(Pid, Id, State),
 453		    {reply, {ok, Pid, Extra}, NState};
 454		Error ->
 455		    {reply, Error, State}
 456	    end;
 457	{ok, #child{pid=?restarting(_)}} ->
 458	    {reply, {error, restarting}, State};
 459	{ok, _} ->
 460	    {reply, {error, running}, State};
 461	_ ->
 462	    {reply, {error, not_found}, State}
 463    end;
 464
 465%% delete_child request is invalid for simple_one_for_one supervisors
 466handle_call({delete_child, _Id}, _From, State) when ?is_simple(State) ->
 467    {reply, {error, simple_one_for_one}, State};
 468
 469handle_call({delete_child, Id}, _From, State) ->
 470    case find_child(Id, State) of
 471	{ok, Child} when Child#child.pid =:= undefined ->
 472	    NState = remove_child(Id, State),
 473	    {reply, ok, NState};
 474	{ok, #child{pid=?restarting(_)}} ->
 475	    {reply, {error, restarting}, State};
 476	{ok, _} ->
 477	    {reply, {error, running}, State};
 478	_ ->
 479	    {reply, {error, not_found}, State}
 480    end;
 481
 482handle_call({get_childspec, Id}, _From, State) ->
 483    case find_child(Id, State) of
 484	{ok, Child} ->
 485            {reply, {ok, child_to_spec(Child)}, State};
 486	error ->
 487	    {reply, {error, not_found}, State}
 488    end;
 489
 490handle_call(which_children, _From, State) when ?is_simple(State) ->
 491    #child{child_type = CT,modules = Mods} = get_dynamic_child(State),
 492    Reply = dyn_map(fun(?restarting(_)) -> {undefined, restarting, CT, Mods};
 493                       (Pid) -> {undefined, Pid, CT, Mods}
 494                    end, State),
 495    {reply, Reply, State};
 496
 497handle_call(which_children, _From, State) ->
 498    Resp =
 499	children_to_list(
 500          fun(Id,#child{pid = ?restarting(_),
 501                        child_type = ChildType, modules = Mods}) ->
 502                  {Id, restarting, ChildType, Mods};
 503             (Id,#child{pid = Pid,
 504                        child_type = ChildType, modules = Mods}) ->
 505                  {Id, Pid, ChildType, Mods}
 506          end,
 507          State#state.children),
 508    {reply, Resp, State};
 509
 510handle_call(count_children, _From,  #state{dynamic_restarts = Restarts} = State)
 511  when ?is_simple(State) ->
 512    #child{child_type = CT} = get_dynamic_child(State),
 513    Sz = dyn_size(State),
 514    Active = Sz - Restarts, % Restarts is always 0 for temporary children
 515    Reply = case CT of
 516		supervisor -> [{specs, 1}, {active, Active},
 517			       {supervisors, Sz}, {workers, 0}];
 518		worker -> [{specs, 1}, {active, Active},
 519			   {supervisors, 0}, {workers, Sz}]
 520	    end,
 521    {reply, Reply, State};
 522
 523handle_call(count_children, _From, State) ->
 524    %% Specs and children are together on the children list...
 525    {Specs, Active, Supers, Workers} =
 526	children_fold(fun(_Id, Child, Counts) ->
 527                              count_child(Child, Counts)
 528                      end, {0,0,0,0}, State#state.children),
 529
 530    %% Reformat counts to a property list.
 531    Reply = [{specs, Specs}, {active, Active},
 532	     {supervisors, Supers}, {workers, Workers}],
 533    {reply, Reply, State}.
 534
 535count_child(#child{pid = Pid, child_type = worker},
 536	    {Specs, Active, Supers, Workers}) ->
 537    case is_pid(Pid) andalso is_process_alive(Pid) of
 538	true ->  {Specs+1, Active+1, Supers, Workers+1};
 539	false -> {Specs+1, Active, Supers, Workers+1}
 540    end;
 541count_child(#child{pid = Pid, child_type = supervisor},
 542	    {Specs, Active, Supers, Workers}) ->
 543    case is_pid(Pid) andalso is_process_alive(Pid) of
 544	true ->  {Specs+1, Active+1, Supers+1, Workers};
 545	false -> {Specs+1, Active, Supers+1, Workers}
 546    end.
 547
 548%%% If a restart attempt failed, this message is cast
 549%%% from restart/2 in order to give gen_server the chance to
 550%%% check it's inbox before trying again.
 551-spec handle_cast({try_again_restart, child_id() | {'restarting',pid()}}, state()) ->
 552			 {'noreply', state()} | {stop, shutdown, state()}.
 553
 554handle_cast({try_again_restart,TryAgainId}, State) ->
 555    case find_child_and_args(TryAgainId, State) of
 556	{ok, Child = #child{pid=?restarting(_)}} ->
 557	    case restart(Child,State) of
 558		{ok, State1} ->
 559		    {noreply, State1};
 560		{shutdown, State1} ->
 561		    {stop, shutdown, State1}
 562	    end;
 563	_ ->
 564	    {noreply,State}
 565    end.
 566
 567%%
 568%% Take care of terminated children.
 569%%
 570-spec handle_info(term(), state()) ->
 571        {'noreply', state()} | {'stop', 'shutdown', state()}.
 572
 573handle_info({'EXIT', Pid, Reason}, State) ->
 574    case restart_child(Pid, Reason, State) of
 575	{ok, State1} ->
 576	    {noreply, State1};
 577	{shutdown, State1} ->
 578	    {stop, shutdown, State1}
 579    end;
 580
 581handle_info(Msg, State) ->
 582    ?LOG_ERROR("Supervisor received unexpected message: ~tp~n",[Msg],
 583               #{domain=>[otp],
 584                 error_logger=>#{tag=>error}}),
 585    {noreply, State}.
 586
 587%%
 588%% Terminate this server.
 589%%
 590-spec terminate(term(), state()) -> 'ok'.
 591
 592terminate(_Reason, State) when ?is_simple(State) ->
 593    terminate_dynamic_children(State);
 594terminate(_Reason, State) ->
 595    terminate_children(State#state.children, State#state.name).
 596
 597%%
 598%% Change code for the supervisor.
 599%% Call the new call-back module and fetch the new start specification.
 600%% Combine the new spec. with the old. If the new start spec. is
 601%% not valid the code change will not succeed.
 602%% Use the old Args as argument to Module:init/1.
 603%% NOTE: This requires that the init function of the call-back module
 604%%       does not have any side effects.
 605%%
 606-spec code_change(term(), state(), term()) ->
 607        {'ok', state()} | {'error', term()}.
 608
 609code_change(_, State, _) ->
 610    case (State#state.module):init(State#state.args) of
 611	{ok, {SupFlags, StartSpec}} ->
 612	    case set_flags(SupFlags, State) of
 613		{ok, State1}  ->
 614                    update_childspec(State1, StartSpec);
 615		{invalid_type, SupFlags} ->
 616		    {error, {bad_flags, SupFlags}}; % backwards compatibility
 617		Error ->
 618		    {error, Error}
 619	    end;
 620	ignore ->
 621	    {ok, State};
 622	Error ->
 623	    Error
 624    end.
 625
 626update_childspec(State, StartSpec) when ?is_simple(State) ->
 627    case check_startspec(StartSpec) of
 628        {ok, {[_],_}=Children} ->
 629            {ok, State#state{children = Children}};
 630        Error ->
 631            {error, Error}
 632    end;
 633update_childspec(State, StartSpec) ->
 634    case check_startspec(StartSpec) of
 635	{ok, Children} ->
 636	    OldC = State#state.children, % In reverse start order !
 637	    NewC = update_childspec1(OldC, Children, []),
 638	    {ok, State#state{children = NewC}};
 639        Error ->
 640	    {error, Error}
 641    end.
 642
 643update_childspec1({[Id|OldIds], OldDb}, {Ids,Db}, KeepOld) ->
 644    case update_chsp(maps:get(Id,OldDb), Db) of
 645	{ok,NewDb} ->
 646	    update_childspec1({OldIds,OldDb}, {Ids,NewDb}, KeepOld);
 647	false ->
 648	    update_childspec1({OldIds,OldDb}, {Ids,Db}, [Id|KeepOld])
 649    end;
 650update_childspec1({[],OldDb}, {Ids,Db}, KeepOld) ->
 651    KeepOldDb = maps:with(KeepOld,OldDb),
 652    %% Return them in (kept) reverse start order.
 653    {lists:reverse(Ids ++ KeepOld),maps:merge(KeepOldDb,Db)}.
 654
 655update_chsp(#child{id=Id}=OldChild, NewDb) ->
 656    case maps:find(Id, NewDb) of
 657        {ok,Child} ->
 658            {ok,NewDb#{Id => Child#child{pid = OldChild#child.pid}}};
 659        error -> % Id not found in new spec.
 660            false
 661    end.
 662
 663    
 664%%% ---------------------------------------------------
 665%%% Start a new child.
 666%%% ---------------------------------------------------
 667
 668handle_start_child(Child, State) ->
 669    case find_child(Child#child.id, State) of
 670	error ->
 671	    case do_start_child(State#state.name, Child) of
 672		{ok, undefined} when ?is_temporary(Child) ->
 673		    {{ok, undefined}, State};
 674		{ok, Pid} ->
 675		    {{ok, Pid}, save_child(Child#child{pid = Pid}, State)};
 676		{ok, Pid, Extra} ->
 677		    {{ok, Pid, Extra}, save_child(Child#child{pid = Pid}, State)};
 678		{error, What} ->
 679		    {{error, {What, Child}}, State}
 680	    end;
 681	{ok, OldChild} when is_pid(OldChild#child.pid) ->
 682	    {{error, {already_started, OldChild#child.pid}}, State};
 683	{ok, _OldChild} ->
 684	    {{error, already_present}, State}
 685    end.
 686
 687%%% ---------------------------------------------------
 688%%% Restart. A process has terminated.
 689%%% Returns: {ok, state()} | {shutdown, state()}
 690%%% ---------------------------------------------------
 691
 692restart_child(Pid, Reason, State) ->
 693    case find_child_and_args(Pid, State) of
 694        {ok, Child} ->
 695	    do_restart(Reason, Child, State);
 696	error ->
 697	    {ok, State}
 698    end.
 699
 700do_restart(Reason, Child, State) when ?is_permanent(Child) ->
 701    ?report_error(child_terminated, Reason, Child, State#state.name),
 702    restart(Child, State);
 703do_restart(normal, Child, State) ->
 704    NState = del_child(Child, State),
 705    {ok, NState};
 706do_restart(shutdown, Child, State) ->
 707    NState = del_child(Child, State),
 708    {ok, NState};
 709do_restart({shutdown, _Term}, Child, State) ->
 710    NState = del_child(Child, State),
 711    {ok, NState};
 712do_restart(Reason, Child, State) when ?is_transient(Child) ->
 713    ?report_error(child_terminated, Reason, Child, State#state.name),
 714    restart(Child, State);
 715do_restart(Reason, Child, State) when ?is_temporary(Child) ->
 716    ?report_error(child_terminated, Reason, Child, State#state.name),
 717    NState = del_child(Child, State),
 718    {ok, NState}.
 719
 720restart(Child, State) ->
 721    case add_restart(State) of
 722	{ok, NState} ->
 723	    case restart(NState#state.strategy, Child, NState) of
 724		{{try_again, TryAgainId}, NState2} ->
 725		    %% Leaving control back to gen_server before
 726		    %% trying again. This way other incoming requsts
 727		    %% for the supervisor can be handled - e.g. a
 728		    %% shutdown request for the supervisor or the
 729		    %% child.
 730                    try_again_restart(TryAgainId),
 731		    {ok,NState2};
 732		Other ->
 733		    Other
 734	    end;
 735	{terminate, NState} ->
 736	    ?report_error(shutdown, reached_max_restart_intensity,
 737			 Child, State#state.name),
 738	    {shutdown, del_child(Child, NState)}
 739    end.
 740
 741restart(simple_one_for_one, Child, State0) ->
 742    #child{pid = OldPid, mfargs = {M, F, A}} = Child,
 743    State1 = case OldPid of
 744		?restarting(_) ->
 745		    NRes = State0#state.dynamic_restarts - 1,
 746		    State0#state{dynamic_restarts = NRes};
 747		_ ->
 748		    State0
 749	    end,
 750    State2 = dyn_erase(OldPid, State1),
 751    case do_start_child_i(M, F, A) of
 752	{ok, Pid} ->
 753            NState = dyn_store(Pid, A, State2),
 754	    {ok, NState};
 755	{ok, Pid, _Extra} ->
 756            NState = dyn_store(Pid, A, State2),
 757	    {ok, NState};
 758	{error, Error} ->
 759            ROldPid = restarting(OldPid),
 760	    NRestarts = State2#state.dynamic_restarts + 1,
 761	    State3 = State2#state{dynamic_restarts = NRestarts},
 762            NState = dyn_store(ROldPid, A, State3),
 763	    ?report_error(start_error, Error, Child, NState#state.name),
 764	    {{try_again, ROldPid}, NState}
 765    end;
 766restart(one_for_one, #child{id=Id} = Child, State) ->
 767    OldPid = Child#child.pid,
 768    case do_start_child(State#state.name, Child) of
 769	{ok, Pid} ->
 770	    NState = set_pid(Pid, Id, State),
 771	    {ok, NState};
 772	{ok, Pid, _Extra} ->
 773	    NState = set_pid(Pid, Id, State),
 774	    {ok, NState};
 775	{error, Reason} ->
 776	    NState = set_pid(restarting(OldPid), Id, State),
 777	    ?report_error(start_error, Reason, Child, State#state.name),
 778	    {{try_again,Id}, NState}
 779    end;
 780restart(rest_for_one, #child{id=Id} = Child, #state{name=SupName} = State) ->
 781    {ChAfter, ChBefore} = split_child(Id, State#state.children),
 782    {Return, ChAfter2} = restart_multiple_children(Child, ChAfter, SupName),
 783    {Return, State#state{children = append(ChAfter2,ChBefore)}};
 784restart(one_for_all, Child, #state{name=SupName} = State) ->
 785    Children1 = del_child(Child#child.id, State#state.children),
 786    {Return, NChildren} = restart_multiple_children(Child, Children1, SupName),
 787    {Return, State#state{children = NChildren}}.
 788
 789restart_multiple_children(Child, Children, SupName) ->
 790    Children1 = terminate_children(Children, SupName),
 791    case start_children(Children1, SupName) of
 792	{ok, NChildren} ->
 793	    {ok, NChildren};
 794	{error, NChildren, {failed_to_start_child, FailedId, _Reason}} ->
 795            NewPid = if FailedId =:= Child#child.id ->
 796                             restarting(Child#child.pid);
 797                        true ->
 798                             ?restarting(undefined)
 799                     end,
 800	    {{try_again, FailedId}, set_pid(NewPid,FailedId,NChildren)}
 801    end.
 802
 803restarting(Pid) when is_pid(Pid) -> ?restarting(Pid);
 804restarting(RPid) -> RPid.
 805
 806-spec try_again_restart(child_id() | {'restarting',pid()}) -> 'ok'.
 807try_again_restart(TryAgainId) ->
 808    gen_server:cast(self(), {try_again_restart, TryAgainId}).
 809
 810%%-----------------------------------------------------------------
 811%% Func: terminate_children/2
 812%% Args: Children = children() % Ids in termination order
 813%%       SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
 814%% Returns: NChildren = children() % Ids in startup order
 815%%                                 % (reversed termination order)
 816%%-----------------------------------------------------------------
 817terminate_children(Children, SupName) ->
 818    Terminate =
 819        fun(_Id,Child) when ?is_temporary(Child) ->
 820                %% Temporary children should not be restarted and thus should
 821                %% be skipped when building the list of terminated children.
 822                do_terminate(Child, SupName),
 823                remove;
 824           (_Id,Child) ->
 825                do_terminate(Child, SupName),
 826                {update,Child#child{pid=undefined}}
 827        end,
 828    {ok,NChildren} = children_map(Terminate, Children),
 829    NChildren.
 830
 831do_terminate(Child, SupName) when is_pid(Child#child.pid) ->
 832    case shutdown(Child#child.pid, Child#child.shutdown) of
 833        ok ->
 834            ok;
 835        {error, normal} when not (?is_permanent(Child)) ->
 836            ok;
 837        {error, OtherReason} ->
 838            ?report_error(shutdown_error, OtherReason, Child, SupName)
 839    end,
 840    ok;
 841do_terminate(_Child, _SupName) ->
 842    ok.
 843
 844%%-----------------------------------------------------------------
 845%% Shutdowns a child. We must check the EXIT value 
 846%% of the child, because it might have died with another reason than
 847%% the wanted. In that case we want to report the error. We put a 
 848%% monitor on the child an check for the 'DOWN' message instead of 
 849%% checking for the 'EXIT' message, because if we check the 'EXIT' 
 850%% message a "naughty" child, who does unlink(Sup), could hang the 
 851%% supervisor. 
 852%% Returns: ok | {error, OtherReason}  (this should be reported)
 853%%-----------------------------------------------------------------
 854shutdown(Pid, brutal_kill) ->
 855    case monitor_child(Pid) of
 856	ok ->
 857	    exit(Pid, kill),
 858	    receive
 859		{'DOWN', _MRef, process, Pid, killed} ->
 860		    ok;
 861		{'DOWN', _MRef, process, Pid, OtherReason} ->
 862		    {error, OtherReason}
 863	    end;
 864	{error, Reason} ->      
 865	    {error, Reason}
 866    end;
 867shutdown(Pid, Time) ->
 868    case monitor_child(Pid) of
 869	ok ->
 870	    exit(Pid, shutdown), %% Try to shutdown gracefully
 871	    receive 
 872		{'DOWN', _MRef, process, Pid, shutdown} ->
 873		    ok;
 874		{'DOWN', _MRef, process, Pid, OtherReason} ->
 875		    {error, OtherReason}
 876	    after Time ->
 877		    exit(Pid, kill),  %% Force termination.
 878		    receive
 879			{'DOWN', _MRef, process, Pid, OtherReason} ->
 880			    {error, OtherReason}
 881		    end
 882	    end;
 883	{error, Reason} ->      
 884	    {error, Reason}
 885    end.
 886
 887%% Help function to shutdown/2 switches from link to monitor approach
 888monitor_child(Pid) ->
 889    
 890    %% Do the monitor operation first so that if the child dies 
 891    %% before the monitoring is done causing a 'DOWN'-message with
 892    %% reason noproc, we will get the real reason in the 'EXIT'-message
 893    %% unless a naughty child has already done unlink...
 894    erlang:monitor(process, Pid),
 895    unlink(Pid),
 896
 897    receive
 898	%% If the child dies before the unlik we must empty
 899	%% the mail-box of the 'EXIT'-message and the 'DOWN'-message.
 900	{'EXIT', Pid, Reason} -> 
 901	    receive 
 902		{'DOWN', _, process, Pid, _} ->
 903		    {error, Reason}
 904	    end
 905    after 0 -> 
 906	    %% If a naughty child did unlink and the child dies before
 907	    %% monitor the result will be that shutdown/2 receives a 
 908	    %% 'DOWN'-message with reason noproc.
 909	    %% If the child should die after the unlink there
 910	    %% will be a 'DOWN'-message with a correct reason
 911	    %% that will be handled in shutdown/2. 
 912	    ok   
 913    end.
 914
 915%%-----------------------------------------------------------------
 916%% Func: terminate_dynamic_children/1
 917%% Args: State
 918%% Returns: ok
 919%%
 920%% Shutdown all dynamic children. This happens when the supervisor is
 921%% stopped. Because the supervisor can have millions of dynamic children, we
 922%% can have a significative overhead here.
 923%%-----------------------------------------------------------------
 924terminate_dynamic_children(State) ->
 925    Child = get_dynamic_child(State),
 926    {Pids, EStack0} = monitor_dynamic_children(Child,State),
 927    Sz = sets:size(Pids),
 928    EStack = case Child#child.shutdown of
 929                 brutal_kill ->
 930                     sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
 931                     wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
 932                 infinity ->
 933                     sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
 934                     wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
 935                 Time ->
 936                     sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
 937                     TRef = erlang:start_timer(Time, self(), kill),
 938                     wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
 939             end,
 940    %% Unroll stacked errors and report them
 941    dict:fold(fun(Reason, Ls, _) ->
 942                      ?report_error(shutdown_error, Reason,
 943                                   Child#child{pid=Ls}, State#state.name)
 944              end, ok, EStack).
 945
 946monitor_dynamic_children(Child,State) ->
 947    dyn_fold(fun(P,{Pids, EStack}) when is_pid(P) ->
 948                     case monitor_child(P) of
 949                         ok ->
 950                             {sets:add_element(P, Pids), EStack};
 951                         {error, normal} when not (?is_permanent(Child)) ->
 952                             {Pids, EStack};
 953                         {error, Reason} ->
 954                             {Pids, dict:append(Reason, P, EStack)}
 955                     end;
 956                (?restarting(_), {Pids, EStack}) ->
 957                     {Pids, EStack}
 958             end, {sets:new(), dict:new()}, State).
 959
 960wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) ->
 961    EStack;
 962wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) ->
 963	%% If the timer has expired before its cancellation, we must empty the
 964	%% mail-box of the 'timeout'-message.
 965    _ = erlang:cancel_timer(TRef),
 966    receive
 967        {timeout, TRef, kill} ->
 968            EStack
 969    after 0 ->
 970            EStack
 971    end;
 972wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz,
 973                      TRef, EStack) ->
 974    receive
 975        {'DOWN', _MRef, process, Pid, killed} ->
 976            wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
 977                                  TRef, EStack);
 978
 979        {'DOWN', _MRef, process, Pid, Reason} ->
 980            wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
 981                                  TRef, dict:append(Reason, Pid, EStack))
 982    end;
 983wait_dynamic_children(Child, Pids, Sz, TRef, EStack) ->
 984    receive
 985        {'DOWN', _MRef, process, Pid, shutdown} ->
 986            wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
 987                                  TRef, EStack);
 988
 989        {'DOWN', _MRef, process, Pid, {shutdown, _}} ->
 990            wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
 991                                  TRef, EStack);
 992
 993        {'DOWN', _MRef, process, Pid, normal} when not (?is_permanent(Child)) ->
 994            wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
 995                                  TRef, EStack);
 996
 997        {'DOWN', _MRef, process, Pid, Reason} ->
 998            wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
 999                                  TRef, dict:append(Reason, Pid, EStack));
1000
1001        {timeout, TRef, kill} ->
1002            sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
1003            wait_dynamic_children(Child, Pids, Sz, undefined, EStack)
1004    end.
1005
1006%%-----------------------------------------------------------------
1007%% Access #state.children
1008%%-----------------------------------------------------------------
1009
1010%% Note we do not want to save the parameter list for temporary processes as
1011%% they will not be restarted, and hence we do not need this information.
1012%% Especially for dynamic children to simple_one_for_one supervisors
1013%% it could become very costly as it is not uncommon to spawn
1014%% very many such processes.
1015-spec save_child(child_rec(), state()) -> state().
1016save_child(#child{mfargs = {M, F, _}} = Child, State) when ?is_temporary(Child) ->
1017    do_save_child(Child#child{mfargs = {M, F, undefined}}, State);
1018save_child(Child, State) ->
1019    do_save_child(Child, State).
1020
1021-spec do_save_child(child_rec(), state()) -> state().
1022do_save_child(#child{id = Id} = Child, #state{children = {Ids,Db}} = State) ->
1023    State#state{children = {[Id|Ids],Db#{Id => Child}}}.
1024
1025-spec del_child(child_rec(), state()) -> state();
1026               (child_id(), children()) -> children().
1027del_child(#child{pid = Pid}, State) when ?is_simple(State) ->
1028    dyn_erase(Pid,State);
1029del_child(Child, State) when is_record(Child,child), is_record(State,state) ->
1030    NChildren = del_child(Child#child.id, State#state.children),
1031    State#state{children = NChildren};
1032del_child(Id, {Ids,Db}) ->
1033    case maps:get(Id, Db) of
1034        Child when Child#child.restart_type =:= temporary ->
1035            {lists:delete(Id, Ids), maps:remove(Id, Db)};
1036        Child ->
1037            {Ids, Db#{Id=>Child#child{pid=undefined}}}
1038    end.
1039
1040%% In: {[S4, S3, Ch, S1, S0],Db}
1041%% Ret: {{[S4, S3, Ch],Db1}, {[S1, S0],Db2}}
1042%% Db1 and Db2 contain the keys in the lists they are associated with.
1043-spec split_child(child_id(), children()) -> {children(), children()}.
1044split_child(Id, {Ids,Db}) ->
1045    {IdsAfter,IdsBefore} = split_ids(Id, Ids, []),
1046    DbBefore = maps:with(IdsBefore,Db),
1047    #{Id:=Ch} = DbAfter = maps:with(IdsAfter,Db),
1048    {{IdsAfter,DbAfter#{Id=>Ch#child{pid=undefined}}},{IdsBefore,DbBefore}}.
1049
1050split_ids(Id, [Id|Ids], After) ->
1051    {lists:reverse([Id|After]), Ids};
1052split_ids(Id, [Other|Ids], After) ->
1053    split_ids(Id, Ids, [Other | After]).
1054
1055%% Find the child record for a given Pid (dynamic child) or Id
1056%% (non-dynamic child). This is called from the API functions.
1057-spec find_child(pid() | child_id(), state()) -> {ok,child_rec()} | error.
1058find_child(Pid, State) when is_pid(Pid), ?is_simple(State) ->
1059    case find_dynamic_child(Pid, State) of
1060        error ->
1061            case find_dynamic_child(restarting(Pid), State) of
1062                error ->
1063		    case erlang:is_process_alive(Pid) of
1064			true -> error;
1065			false -> {ok, get_dynamic_child(State)}
1066		    end;
1067                Other ->
1068                    Other
1069            end;
1070        Other ->
1071            Other
1072    end;
1073find_child(Id, #state{children = {_Ids,Db}}) ->
1074    maps:find(Id, Db).
1075
1076%% Get the child record - either by child id or by pid.  If
1077%% simple_one_for_one, then insert the pid and args into the returned
1078%% child record. This is called when trying to restart the child.
1079-spec find_child_and_args(IdOrPid, state()) -> {ok, child_rec()} | error when
1080      IdOrPid :: pid() | {restarting,pid()} | child_id().
1081find_child_and_args(Pid, State) when ?is_simple(State) ->
1082    case find_dynamic_child(Pid, State) of
1083        {ok,#child{mfargs={M,F,_}} = Child} ->
1084            {ok, Args} = dyn_args(Pid, State),
1085            {ok, Child#child{mfargs = {M, F, Args}}};
1086        error ->
1087            error
1088    end;
1089find_child_and_args(Pid, State) when is_pid(Pid) ->
1090    find_child_by_pid(Pid, State);
1091find_child_and_args(Id, #state{children={_Ids,Db}})  ->
1092    maps:find(Id, Db).
1093
1094%% Given the pid, find the child record for a dynamic child, and
1095%% include the pid in the returned record.
1096-spec find_dynamic_child(IdOrPid, state()) -> {ok, child_rec()} | error when
1097      IdOrPid :: pid() | {restarting,pid()} | child_id().
1098find_dynamic_child(Pid, State) ->
1099    case dyn_exists(Pid, State) of
1100        true ->
1101            Child = get_dynamic_child(State),
1102            {ok, Child#child{pid=Pid}};
1103        false ->
1104            error
1105    end.
1106
1107%% Given the pid, find the child record for a non-dyanamic child.
1108-spec find_child_by_pid(IdOrPid, state()) -> {ok,child_rec()} | error when
1109      IdOrPid :: pid() | {restarting,pid()}.
1110find_child_by_pid(Pid,#state{children={_Ids,Db}}) ->
1111    Fun = fun(_Id,#child{pid=P}=Ch,_) when P =:= Pid ->
1112                  throw(Ch);
1113             (_,_,error) ->
1114                  error
1115          end,
1116    try maps:fold(Fun,error,Db)
1117    catch throw:Child -> {ok,Child}
1118    end.
1119
1120%% Get the child record from a simple_one_for_one supervisor - no pid
1121%% It is assumed that the child can always be found
1122-spec get_dynamic_child(state()) -> child_rec().
1123get_dynamic_child(#state{children={[Id],Db}}) ->
1124    #{Id := Child} = Db,
1125    Child.
1126
1127%% Update pid in the given child record and store it in the process state
1128-spec set_pid(term(), child_id(), state()) -> state();
1129             (term(), child_id(), children()) -> children().
1130set_pid(Pid, Id, #state{children=Children} = State) ->
1131    State#state{children = set_pid(Pid, Id, Children)};
1132set_pid(Pid, Id, {Ids, Db}) ->
1133    NewDb = maps:update_with(Id, fun(Child) -> Child#child{pid=Pid} end, Db),
1134    {Ids,NewDb}.
1135
1136%% Remove the Id and the child record from the process state
1137-spec remove_child(child_id(), state()) -> state().
1138remove_child(Id, #state{children={Ids,Db}} = State) ->
1139    NewIds = lists:delete(Id,Ids),
1140    NewDb = maps:remove(Id,Db),
1141    State#state{children = {NewIds,NewDb}}.
1142
1143%% In the order of Ids, traverse the children and update each child
1144%% according to the return value of the Fun.
1145%% On error, abort and return the merge of the old and the updated map.
1146%% NOTE: The returned list of Ids is reverted compared to the input.
1147-spec children_map(Fun, children()) -> {ok, children()} |
1148                                       {error,children(),Reason} when
1149      Fun :: fun((child_id(),child_rec()) -> {update,child_rec()} |
1150                                             remove |
1151                                             {abort, Reason}),
1152      Reason :: term().
1153children_map(Fun,{Ids,Db}) ->
1154    children_map(Fun, Ids, Db, []).
1155
1156children_map(Fun,[Id|Ids],Db,Acc) ->
1157    case Fun(Id,maps:get(Id,Db)) of
1158        {update,Child} ->
1159            children_map(Fun,Ids,Db#{Id => Child},[Id|Acc]);
1160        remove ->
1161            children_map(Fun,Ids,maps:remove(Id,Db),Acc);
1162        {abort,Reason} ->
1163            {error,{lists:reverse(Ids)++[Id|Acc],Db},Reason}
1164    end;
1165children_map(_Fun,[],Db,Acc) ->
1166    {ok,{Acc,Db}}.
1167
1168%% In the order of Ids, map over all children and return the list
1169-spec children_to_list(Fun, children()) -> List when
1170      Fun :: fun((child_id(), child_rec()) -> Elem),
1171      List :: list(Elem),
1172      Elem :: term().
1173children_to_list(Fun,{Ids,Db}) ->
1174    children_to_list(Fun, Ids, Db, []).
1175children_to_list(Fun,[Id|Ids],Db,Acc) ->
1176    children_to_list(Fun,Ids,Db,[Fun(Id,maps:get(Id,Db))|Acc]);
1177children_to_list(_Fun,[],_Db,Acc) ->
1178    lists:reverse(Acc).
1179
1180%% The order is not important - so ignore Ids
1181-spec children_fold(Fun, Acc0, children()) -> Acc1 when
1182      Fun :: fun((child_id(), child_rec(), AccIn) -> AccOut),
1183      Acc0 :: term(),
1184      Acc1 :: term(),
1185      AccIn :: term(),
1186      AccOut :: term().
1187children_fold(Fun,Init,{_Ids,Db}) ->
1188    maps:fold(Fun, Init, Db).
1189
1190-spec append(children(), children()) -> children().
1191append({Ids1,Db1},{Ids2,Db2}) ->
1192    {Ids1++Ids2,maps:merge(Db1,Db2)}.
1193
1194%%-----------------------------------------------------------------
1195%% Func: init_state/4
1196%% Args: SupName = {local, atom()} | {global, atom()} | self
1197%%       Type = {Strategy, MaxIntensity, Period}
1198%%         Strategy = one_for_one | one_for_all | simple_one_for_one |
1199%%                    rest_for_one
1200%%         MaxIntensity = integer() >= 0
1201%%         Period = integer() > 0
1202%%       Mod :== atom()
1203%%       Args :== term()
1204%% Purpose: Check that Type is of correct type (!)
1205%% Returns: {ok, state()} | Error
1206%%-----------------------------------------------------------------
1207init_state(SupName, Type, Mod, Args) ->
1208    set_flags(Type, #state{name = supname(SupName,Mod),
1209			   module = Mod,
1210			   args = Args}).
1211
1212set_flags(Flags, State) ->
1213    try check_flags(Flags) of
1214	#{strategy := Strategy, intensity := MaxIntensity, period := Period} ->
1215	    {ok, State#state{strategy = Strategy,
1216			     intensity = MaxIntensity,
1217			     period = Period}}
1218    catch
1219	Thrown -> Thrown
1220    end.
1221
1222check_flags(SupFlags) when is_map(SupFlags) ->
1223    do_check_flags(maps:merge(?default_flags,SupFlags));
1224check_flags({Strategy, MaxIntensity, Period}) ->
1225    check_flags(#{strategy => Strategy,
1226		  intensity => MaxIntensity,
1227		  period => Period});
1228check_flags(What) ->
1229    throw({invalid_type, What}).
1230
1231do_check_flags(#{strategy := Strategy,
1232		 intensity := MaxIntensity,
1233		 period := Period} = Flags) ->
1234    validStrategy(Strategy),
1235    validIntensity(MaxIntensity),
1236    validPeriod(Period),
1237    Flags.
1238
1239validStrategy(simple_one_for_one) -> true;
1240validStrategy(one_for_one)        -> true;
1241validStrategy(one_for_all)        -> true;
1242validStrategy(rest_for_one)       -> true;
1243validStrategy(What)               -> throw({invalid_strategy, What}).
1244
1245validIntensity(Max) when is_integer(Max),
1246                         Max >=  0 -> true;
1247validIntensity(What)               -> throw({invalid_intensity, What}).
1248
1249validPeriod(Period) when is_integer(Period),
1250                         Period > 0 -> true;
1251validPeriod(What)                   -> throw({invalid_period, What}).
1252
1253supname(self, Mod) -> {self(), Mod};
1254supname(N, _)      -> N.
1255
1256%%% ------------------------------------------------------
1257%%% Check that the children start specification is valid.
1258%%% Input: [child_spec()]
1259%%% Returns: {ok, [child_rec()]} | Error
1260%%% ------------------------------------------------------
1261
1262check_startspec(Children) -> check_startspec(Children, [], #{}).
1263
1264check_startspec([ChildSpec|T], Ids, Db) ->
1265    case check_childspec(ChildSpec) of
1266	{ok, #child{id=Id}=Child} ->
1267	    case maps:is_key(Id, Db) of
1268		%% The error message duplicate_child_name is kept for
1269		%% backwards compatibility, although
1270		%% duplicate_child_id would be more correct.
1271		true -> {duplicate_child_name, Id};
1272		false -> check_startspec(T, [Id | Ids], Db#{Id=>Child})
1273	    end;
1274	Error -> Error
1275    end;
1276check_startspec([], Ids, Db) ->
1277    {ok, {lists:reverse(Ids),Db}}.
1278
1279check_childspec(ChildSpec) when is_map(ChildSpec) ->
1280    catch do_check_childspec(maps:merge(?default_child_spec,ChildSpec));
1281check_childspec({Id, Func, RestartType, Shutdown, ChildType, Mods}) ->
1282    check_childspec(#{id => Id,
1283		      start => Func,
1284		      restart => RestartType,
1285		      shutdown => Shutdown,
1286		      type => ChildType,
1287		      modules => Mods});
1288check_childspec(X) -> {invalid_child_spec, X}.
1289
1290do_check_childspec(#{restart := RestartType,
1291		     type := ChildType} = ChildSpec)->
1292    Id = case ChildSpec of
1293	       #{id := I} -> I;
1294	       _ -> throw(missing_id)
1295	   end,
1296    Func = case ChildSpec of
1297	       #{start := F} -> F;
1298	       _ -> throw(missing_start)
1299	   end,
1300    validId(Id),
1301    validFunc(Func),
1302    validRestartType(RestartType),
1303    validChildType(ChildType),
1304    Shutdown = case ChildSpec of
1305		   #{shutdown := S} -> S;
1306		   #{type := worker} -> 5000;
1307		   #{type := supervisor} -> infinity
1308	       end,
1309    validShutdown(Shutdown),
1310    Mods = case ChildSpec of
1311	       #{modules := Ms} -> Ms;
1312	       _ -> {M,_,_} = Func, [M]
1313	   end,
1314    validMods(Mods),
1315    {ok, #child{id = Id, mfargs = Func, restart_type = RestartType,
1316		shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
1317
1318validChildType(supervisor) -> true;
1319validChildType(worker) -> true;
1320validChildType(What) -> throw({invalid_child_type, What}).
1321
1322validId(_Id) -> true.
1323
1324validFunc({M, F, A}) when is_atom(M), 
1325                          is_atom(F), 
1326                          is_list(A) -> true;
1327validFunc(Func)                      -> throw({invalid_mfa, Func}).
1328
1329validRestartType(permanent)   -> true;
1330validRestartType(temporary)   -> true;
1331validRestartType(transient)   -> true;
1332validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}).
1333
1334validShutdown(Shutdown)
1335  when is_integer(Shutdown), Shutdown > 0 -> true;
1336validShutdown(infinity)             -> true;
1337validShutdown(brutal_kill)          -> true;
1338validShutdown(Shutdown)             -> throw({invalid_shutdown, Shutdown}).
1339
1340validMods(dynamic) -> true;
1341validMods(Mods) when is_list(Mods) ->
1342    lists:foreach(fun(Mod) ->
1343		    if
1344			is_atom(Mod) -> ok;
1345			true -> throw({invalid_module, Mod})
1346		    end
1347		  end,
1348		  Mods);
1349validMods(Mods) -> throw({invalid_modules, Mods}).
1350
1351child_to_spec(#child{id = Id,
1352		    mfargs = Func,
1353		    restart_type = RestartType,
1354		    shutdown = Shutdown,
1355		    child_type = ChildType,
1356		    modules = Mods}) ->
1357    #{id => Id,
1358      start => Func,
1359      restart => RestartType,
1360      shutdown => Shutdown,
1361      type => ChildType,
1362      modules => Mods}.
1363
1364%%% ------------------------------------------------------
1365%%% Add a new restart and calculate if the max restart
1366%%% intensity has been reached (in that case the supervisor
1367%%% shall terminate).
1368%%% All restarts accured inside the period amount of seconds
1369%%% are kept in the #state.restarts list.
1370%%% Returns: {ok, State'} | {terminate, State'}
1371%%% ------------------------------------------------------
1372
1373add_restart(State) ->  
1374    I = State#state.intensity,
1375    P = State#state.period,
1376    R = State#state.restarts,
1377    Now = erlang:monotonic_time(1),
1378    R1 = add_restart([Now|R], Now, P),
1379    State1 = State#state{

Large files files are truncated, but you can click here to view the full file