PageRenderTime 67ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

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