2020
2121-module (supervisor ).
2222
23+ % %-----------------------------------------------------------------------------
24+ % % @doc An implementation of the Erlang/OTP supervisor interface.
25+ % %
26+ % % This module implements a strict subset of the Erlang/OTP supervisor
27+ % % interface, supporting operations for local creation and management of
28+ % % supervisor instances.
29+ % %
30+ % % This module is designed to be API-compatible with supervisor, with exceptions
31+ % % noted below.
32+ % %
33+ % % Caveats:
34+ % % <ul>
35+ % % <li>Support only for locally named procs</li>
36+ % % <li>No support for simple_one_for_one or one_for_rest strategies</li>
37+ % % <li>No support for hibernate</li>
38+ % % <li>No support for automatic shutdown</li>
39+ % % </ul>
40+ % % @end
41+ % %-----------------------------------------------------------------------------
42+
2343-behavior (gen_server ).
2444
2545-export ([
4363
4464-export_type ([
4565 child_spec / 0 ,
66+ startchild_ret / 0 ,
67+ startlink_ret / 0 ,
4668 strategy / 0 ,
4769 sup_flags / 0
4870]).
5375 | temporary
5476 | {terminating , permanent | transient | temporary , gen_server :from ()}.
5577-type shutdown () :: brutal_kill | timeout ().
56- -type child_type () :: worker | supervisor .
78+ -type worker () :: worker | supervisor .
79+ -type child_id () :: term ().
80+ -type child () :: undefined | pid ().
5781
5882-type strategy () :: one_for_all | one_for_one .
5983-type sup_flags () ::
7094 start := {module (), atom (), [any ()]},
7195 restart => restart (),
7296 shutdown => shutdown (),
73- type => child_type (),
97+ type => worker (),
7498 modules => [module ()] | dynamic
7599 }
76100 | {
77101 Id :: any (),
78102 StartFunc :: {module (), atom (), [any ()]},
79103 Restart :: restart (),
80104 Shutdown :: shutdown (),
81- Type :: child_type (),
105+ Type :: worker (),
82106 Modules :: [module ()] | dynamic
83107 }.
84108
109+ -type startlink_ret () :: {ok , pid ()} | ignore | {error , startlink_err ()}.
110+ -type startlink_err () :: {already_started , pid ()} | {shutdown , term ()} | term ().
111+ -type startchild_ret () ::
112+ {ok , Child :: child ()} | {ok , Child :: child (), Info :: term ()} | {error , startchild_err ()}.
113+ -type startchild_err () :: already_present | {already_started , Child :: child ()} | term ().
114+ -type sup_name () :: {local , Name :: atom ()}.
115+ -type sup_ref () ::
116+ (Name :: atom ())
117+ | {Name :: atom (), Node :: node ()}
118+ | {global , Name :: term ()}
119+ | {via , Module :: module (), Name :: any ()}
120+ | pid ().
121+
85122-record (child , {
86123 pid = undefined :: pid () | undefined | {restarting , pid ()} | {restarting , undefined },
87124 id :: any (),
88125 start :: {module (), atom (), [any ()] | undefined },
89126 restart :: restart (),
90127 shutdown :: shutdown (),
91- type :: child_type (),
128+ type :: worker (),
92129 modules = [] :: [module ()] | dynamic
93130}).
94131% % note: the list of children should always be kept in order, with first to start at the head.
108145% % could be used to set a sane default for the platform (OTP uses 1000).
109146-define (STALE_RESTART_LIMIT , 100 ).
110147
148+ -spec start_link (Module :: module (), Args :: [any ()]) -> startlink_ret ().
111149start_link (Module , Args ) ->
112150 gen_server :start_link (? MODULE , {Module , Args }, []).
113151
152+ -spec start_link (SupName :: sup_name (), Module :: module (), Args :: [any ()]) -> startlink_ret ().
114153start_link (SupName , Module , Args ) ->
115154 gen_server :start_link (SupName , ? MODULE , {Module , Args }, []).
116155
156+ -spec start_child (Supervisor :: sup_ref (), ChildSpec :: child_spec ()) -> startchild_ret ().
117157start_child (Supervisor , ChildSpec ) ->
118158 gen_server :call (Supervisor , {start_child , ChildSpec }).
119159
160+ -spec terminate_child (Supervisor :: sup_ref (), ChildId :: any ()) -> ok | {error , not_found }.
120161terminate_child (Supervisor , ChildId ) ->
121162 gen_server :call (Supervisor , {terminate_child , ChildId }).
122163
164+ -spec restart_child (Supervisor :: sup_ref (), ChildId :: any ()) ->
165+ {ok , Child :: child ()}
166+ | {ok , Child :: child (), Info :: term ()}
167+ | {error , Reason :: running | restarting | not_found | term ()}.
123168restart_child (Supervisor , ChildId ) ->
124169 gen_server :call (Supervisor , {restart_child , ChildId }).
125170
171+ -spec delete_child (Supervisor :: sup_ref (), ChildId :: any ()) ->
172+ ok | {error , Reason :: running | restarting | not_found }.
126173delete_child (Supervisor , ChildId ) ->
127174 gen_server :call (Supervisor , {delete_child , ChildId }).
128175
176+ -spec which_children (Supervisor :: sup_ref ()) ->
177+ [
178+ {
179+ Id :: child_id () | undefined ,
180+ Child :: child () | restarting ,
181+ Type :: worker (),
182+ Modules :: [module ()]
183+ }
184+ ].
129185which_children (Supervisor ) ->
130186 gen_server :call (Supervisor , which_children ).
131187
188+ -spec count_children (Supervisor :: sup_ref ()) ->
189+ [
190+ {specs , ChildSpecCount :: non_neg_integer ()}
191+ | {active , ActiveProcessCount :: non_neg_integer ()}
192+ | {supervisors , ChildSupervisorCount :: non_neg_integer ()}
193+ | {workers , ChildWorkerCount :: non_neg_integer ()}
194+ ].
132195count_children (Supervisor ) ->
133196 gen_server :call (Supervisor , count_children ).
134197
198+ % @hidden
199+ -spec init ({Mod :: module (), Args :: [any ()]}) ->
200+ {ok , State :: # state {}} | {stop , {bad_return , {Mod :: module (), init , Reason :: term ()}}}.
135201init ({Mod , Args }) ->
136202 erlang :process_flag (trap_exit , true ),
137203 case Mod :init (Args ) of
@@ -155,6 +221,7 @@ init({Mod, Args}) ->
155221 NewChildren = start_children (State # state .children , []),
156222 {ok , State # state {children = NewChildren }};
157223 Error ->
224+ % TODO: log supervisor init failure
158225 {stop , {bad_return , {Mod , init , Error }}}
159226 end .
160227
@@ -196,13 +263,17 @@ child_spec_to_record(#{id := ChildId, start := MFA} = ChildMap) ->
196263 modules = Modules
197264 }.
198265
266+ % @hidden
267+ -spec init_state (ChildSpecs :: [child_spec ()], State :: # state {}) -> State :: # state {}.
199268init_state ([ChildSpec | T ], State ) ->
200269 Child = child_spec_to_record (ChildSpec ),
201270 NewChildren = [Child | State # state .children ],
202271 init_state (T , State # state {children = NewChildren });
203272init_state ([], State ) ->
204273 State # state {children = lists :reverse (State # state .children )}.
205274
275+ -spec start_children (ChildSpecs :: [child_spec ()], State :: # state {}) ->
276+ ChildSpecs :: [child_spec ()].
206277start_children ([Child | T ], StartedC ) ->
207278 case try_start (Child ) of
208279 {ok , Pid , _Result } ->
@@ -212,6 +283,7 @@ start_children([], StartedC) ->
212283 % % We should always keep the start list in order for later one_for_all restarts.
213284 lists :reverse (StartedC ).
214285
286+ % @hidden
215287handle_call ({start_child , ChildSpec }, _From , # state {children = Children } = State ) ->
216288 Child = child_spec_to_record (ChildSpec ),
217289 # child {id = ID } = Child ,
@@ -286,10 +358,13 @@ handle_call(count_children, _From, #state{children = Children} = State) ->
286358 Reply = [{specs , Specs }, {active , Active }, {supervisors , Supers }, {workers , Workers }],
287359 {reply , Reply , State }.
288360
361+ % @hidden
289362handle_cast (_Msg , State ) ->
290363 {noreply , State }.
291364
365+ % @hidden
292366handle_info ({'EXIT' , Pid , Reason }, State ) ->
367+ % TODO: log crash report
293368 handle_child_exit (Pid , Reason , State );
294369handle_info ({ensure_killed , Pid }, State ) ->
295370 case lists :keyfind (Pid , # child .pid , State # state .children ) of
@@ -327,15 +402,17 @@ handle_info({try_again_restart, Id}, State) ->
327402 ),
328403 {noreply , State1 # state {children = UpdatedChildren }};
329404 {error , {_ , _ }} ->
405+ % TODO: log crash report
330406 {noreply , State1 , {timeout , 0 , {try_again_restart , Id }}}
331407 end ;
332408 {shutdown , State1 } ->
333409 RemainingChildren = lists :keydelete (Id , # child .id , State1 # state .children ),
410+ % TODO: log supervisor shutdown
334411 {stop , shutdown , State1 # state {children = RemainingChildren }}
335412 end
336413 end ;
337414handle_info (_Msg , State ) ->
338- % TODO: log unexpected message
415+ % TODO: log unexpected message to debug
339416 {noreply , State }.
340417
341418% % @hidden
@@ -372,6 +449,7 @@ handle_child_exit(Pid, Reason, State) ->
372449 RemainingChildren = lists :keydelete (
373450 Pid , # child .pid , State1 # state .children
374451 ),
452+ % TODO: log supervisor shutdown
375453 {stop , shutdown , State1 # state {children = RemainingChildren }}
376454 end ;
377455 false ->
0 commit comments