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 ,
68+ startlink_err / 0 ,
4669 strategy / 0 ,
47- sup_flags / 0
70+ sup_flags / 0 ,
71+ sup_name / 0 ,
72+ sup_ref / 0
4873]).
4974
5075-type restart () ::
5378 | temporary
5479 | {terminating , permanent | transient | temporary , gen_server :from ()}.
5580-type shutdown () :: brutal_kill | timeout ().
56- -type child_type () :: worker | supervisor .
81+ -type worker () :: worker | supervisor .
82+ -type child_id () :: term ().
83+ -type child () :: undefined | pid ().
5784
5885-type strategy () :: one_for_all | one_for_one .
5986-type sup_flags () ::
7097 start := {module (), atom (), [any ()]},
7198 restart => restart (),
7299 shutdown => shutdown (),
73- type => child_type (),
100+ type => worker (),
74101 modules => [module ()] | dynamic
75102 }
76103 | {
77104 Id :: any (),
78105 StartFunc :: {module (), atom (), [any ()]},
79106 Restart :: restart (),
80107 Shutdown :: shutdown (),
81- Type :: child_type (),
108+ Type :: worker (),
82109 Modules :: [module ()] | dynamic
83110 }.
84111
112+ -type startlink_ret () :: {ok , pid ()} | ignore | {error , startlink_err ()}.
113+ -type startlink_err () :: {already_started , pid ()} | {shutdown , term ()} | term ().
114+ -type startchild_ret () ::
115+ {ok , Child :: child ()} | {ok , Child :: child (), Info :: term ()} | {error , startchild_err ()}.
116+ -type startchild_err () :: already_present | {already_started , Child :: child ()} | term ().
117+ -type sup_name () :: {local , Name :: atom ()}.
118+ -type sup_ref () ::
119+ (Name :: atom ())
120+ | {Name :: atom (), Node :: node ()}
121+ | pid ().
122+
85123-record (child , {
86124 pid = undefined :: pid () | undefined | {restarting , pid ()} | {restarting , undefined },
87125 id :: any (),
88126 start :: {module (), atom (), [any ()] | undefined },
89127 restart :: restart (),
90128 shutdown :: shutdown (),
91- type :: child_type (),
129+ type :: worker (),
92130 modules = [] :: [module ()] | dynamic
93131}).
94132
111149% % could be used to set a sane default for the platform (OTP uses 1000).
112150-define (STALE_RESTART_LIMIT , 100 ).
113151
152+ -spec start_link (Module :: module (), Args :: [any ()]) -> startlink_ret ().
114153start_link (Module , Args ) ->
115154 gen_server :start_link (? MODULE , {Module , Args }, []).
116155
156+ -spec start_link (SupName :: sup_name (), Module :: module (), Args :: [any ()]) -> startlink_ret ().
117157start_link (SupName , Module , Args ) ->
118158 gen_server :start_link (SupName , ? MODULE , {Module , Args }, []).
119159
160+ -spec start_child (Supervisor :: sup_ref (), ChildSpec :: child_spec ()) -> startchild_ret ().
120161start_child (Supervisor , ChildSpec ) ->
121162 gen_server :call (Supervisor , {start_child , ChildSpec }).
122163
164+ -spec terminate_child (Supervisor :: sup_ref (), ChildId :: any ()) -> ok | {error , not_found }.
123165terminate_child (Supervisor , ChildId ) ->
124166 gen_server :call (Supervisor , {terminate_child , ChildId }).
125167
168+ -spec restart_child (Supervisor :: sup_ref (), ChildId :: any ()) ->
169+ {ok , Child :: child ()}
170+ | {ok , Child :: child (), Info :: term ()}
171+ | {error , Reason :: running | restarting | not_found | term ()}.
126172restart_child (Supervisor , ChildId ) ->
127173 gen_server :call (Supervisor , {restart_child , ChildId }).
128174
175+ -spec delete_child (Supervisor :: sup_ref (), ChildId :: any ()) ->
176+ ok | {error , Reason :: running | restarting | not_found }.
129177delete_child (Supervisor , ChildId ) ->
130178 gen_server :call (Supervisor , {delete_child , ChildId }).
131179
180+ -spec which_children (Supervisor :: sup_ref ()) ->
181+ [
182+ {
183+ Id :: child_id () | undefined ,
184+ Child :: child () | restarting ,
185+ Type :: worker (),
186+ Modules :: [module ()]
187+ }
188+ ].
132189which_children (Supervisor ) ->
133190 gen_server :call (Supervisor , which_children ).
134191
192+ -spec count_children (Supervisor :: sup_ref ()) ->
193+ [
194+ {specs , ChildSpecCount :: non_neg_integer ()}
195+ | {active , ActiveProcessCount :: non_neg_integer ()}
196+ | {supervisors , ChildSupervisorCount :: non_neg_integer ()}
197+ | {workers , ChildWorkerCount :: non_neg_integer ()}
198+ ].
135199count_children (Supervisor ) ->
136200 gen_server :call (Supervisor , count_children ).
137201
202+ % @hidden
203+ -spec init ({Mod :: module (), Args :: [any ()]}) ->
204+ {ok , State :: # state {}} | {stop , {bad_return , {Mod :: module (), init , Reason :: term ()}}}.
138205init ({Mod , Args }) ->
139206 erlang :process_flag (trap_exit , true ),
140207 case Mod :init (Args ) of
@@ -158,6 +225,7 @@ init({Mod, Args}) ->
158225 NewChildren = start_children (State # state .children , []),
159226 {ok , State # state {children = NewChildren }};
160227 Error ->
228+ % TODO: log supervisor init failure
161229 {stop , {bad_return , {Mod , init , Error }}}
162230 end .
163231
@@ -199,13 +267,17 @@ child_spec_to_record(#{id := ChildId, start := MFA} = ChildMap) ->
199267 modules = Modules
200268 }.
201269
270+ % @hidden
271+ -spec init_state (ChildSpecs :: [child_spec ()], State :: # state {}) -> State :: # state {}.
202272init_state ([ChildSpec | T ], State ) ->
203273 Child = child_spec_to_record (ChildSpec ),
204274 NewChildren = [Child | State # state .children ],
205275 init_state (T , State # state {children = NewChildren });
206276init_state ([], State ) ->
207277 State # state {children = lists :reverse (State # state .children )}.
208278
279+ -spec start_children (ChildSpecs :: [child_spec ()], State :: # state {}) ->
280+ ChildSpecs :: [child_spec ()].
209281start_children ([Child | T ], StartedC ) ->
210282 case try_start (Child ) of
211283 {ok , Pid , _Result } ->
@@ -214,6 +286,7 @@ start_children([Child | T], StartedC) ->
214286start_children ([], StartedC ) ->
215287 StartedC .
216288
289+ % @hidden
217290handle_call ({start_child , ChildSpec }, _From , # state {children = Children } = State ) ->
218291 Child = child_spec_to_record (ChildSpec ),
219292 # child {id = ID } = Child ,
@@ -288,10 +361,13 @@ handle_call(count_children, _From, #state{children = Children} = State) ->
288361 Reply = [{specs , Specs }, {active , Active }, {supervisors , Supers }, {workers , Workers }],
289362 {reply , Reply , State }.
290363
364+ % @hidden
291365handle_cast (_Msg , State ) ->
292366 {noreply , State }.
293367
368+ % @hidden
294369handle_info ({'EXIT' , Pid , Reason }, State ) ->
370+ % TODO: log crash report
295371 handle_child_exit (Pid , Reason , State );
296372handle_info ({ensure_killed , Pid }, State ) ->
297373 case lists :keyfind (Pid , # child .pid , State # state .children ) of
@@ -331,17 +407,19 @@ handle_info({try_again_restart, Id}, State) ->
331407 ),
332408 {noreply , State1 # state {children = UpdatedChildren }};
333409 {error , {_ , _ }} ->
410+ % TODO: log crash report
334411 {noreply , State1 , {timeout , 0 , {try_again_restart , Id }}}
335412 end ;
336413 {shutdown , State1 } ->
337414 RemainingChildren = lists :keydelete (Id , # child .id , State1 # state .children ),
415+ % TODO: log supervisor shutdown
338416 {stop , shutdown , State1 # state {children = RemainingChildren }}
339417 end
340418 end ;
341419handle_info ({restart_many_children , [# child {pid = undefined } = _Child | Children ]}, State ) ->
342420 {noreply , State , {timeout , 0 , {restart_many_children , Children }}};
343421handle_info (_Msg , State ) ->
344- % TODO: log unexpected message
422+ % TODO: log unexpected message to debug
345423 {noreply , State }.
346424
347425% % @hidden
@@ -378,6 +456,7 @@ handle_child_exit(Pid, Reason, State) ->
378456 RemainingChildren = lists :keydelete (
379457 Pid , # child .pid , State1 # state .children
380458 ),
459+ % TODO: log supervisor shutdown
381460 {stop , shutdown , State1 # state {children = RemainingChildren }}
382461 end ;
383462 false ->
0 commit comments