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% % note: the list of children should always be kept in order, with last to start at the head.
110148-define (DEFAULT_INTENSITY , 1 ).
111149-define (DEFAULT_PERIOD , 5 ).
112150
151+ -spec start_link (Module :: module (), Args :: [any ()]) -> startlink_ret ().
113152start_link (Module , Args ) ->
114153 gen_server :start_link (? MODULE , {Module , Args }, []).
115154
155+ -spec start_link (SupName :: sup_name (), Module :: module (), Args :: [any ()]) -> startlink_ret ().
116156start_link (SupName , Module , Args ) ->
117157 gen_server :start_link (SupName , ? MODULE , {Module , Args }, []).
118158
159+ -spec start_child (Supervisor :: sup_ref (), ChildSpec :: child_spec ()) -> startchild_ret ().
119160start_child (Supervisor , ChildSpec ) ->
120161 gen_server :call (Supervisor , {start_child , ChildSpec }).
121162
163+ -spec terminate_child (Supervisor :: sup_ref (), ChildId :: any ()) -> ok | {error , not_found }.
122164terminate_child (Supervisor , ChildId ) ->
123165 gen_server :call (Supervisor , {terminate_child , ChildId }).
124166
167+ -spec restart_child (Supervisor :: sup_ref (), ChildId :: any ()) ->
168+ {ok , Child :: child ()}
169+ | {ok , Child :: child (), Info :: term ()}
170+ | {error , Reason :: running | restarting | not_found | term ()}.
125171restart_child (Supervisor , ChildId ) ->
126172 gen_server :call (Supervisor , {restart_child , ChildId }).
127173
174+ -spec delete_child (Supervisor :: sup_ref (), ChildId :: any ()) ->
175+ ok | {error , Reason :: running | restarting | not_found }.
128176delete_child (Supervisor , ChildId ) ->
129177 gen_server :call (Supervisor , {delete_child , ChildId }).
130178
179+ -spec which_children (Supervisor :: sup_ref ()) ->
180+ [
181+ {
182+ Id :: child_id () | undefined ,
183+ Child :: child () | restarting ,
184+ Type :: worker (),
185+ Modules :: [module ()]
186+ }
187+ ].
131188which_children (Supervisor ) ->
132189 gen_server :call (Supervisor , which_children ).
133190
191+ -spec count_children (Supervisor :: sup_ref ()) ->
192+ [
193+ {specs , ChildSpecCount :: non_neg_integer ()}
194+ | {active , ActiveProcessCount :: non_neg_integer ()}
195+ | {supervisors , ChildSupervisorCount :: non_neg_integer ()}
196+ | {workers , ChildWorkerCount :: non_neg_integer ()}
197+ ].
134198count_children (Supervisor ) ->
135199 gen_server :call (Supervisor , count_children ).
136200
201+ % @hidden
202+ -spec init ({Mod :: module (), Args :: [any ()]}) ->
203+ {ok , State :: # state {}} | {stop , {bad_return , {Mod :: module (), init , Reason :: term ()}}}.
137204init ({Mod , Args }) ->
138205 erlang :process_flag (trap_exit , true ),
139206 case Mod :init (Args ) of
@@ -157,6 +224,7 @@ init({Mod, Args}) ->
157224 NewChildren = start_children (State # state .children , []),
158225 {ok , State # state {children = NewChildren }};
159226 Error ->
227+ % TODO: log supervisor init failure
160228 {stop , {bad_return , {Mod , init , Error }}}
161229 end .
162230
@@ -198,13 +266,17 @@ child_spec_to_record(#{id := ChildId, start := MFA} = ChildMap) ->
198266 modules = Modules
199267 }.
200268
269+ % @hidden
270+ -spec init_state (ChildSpecs :: [child_spec ()], State :: # state {}) -> State :: # state {}.
201271init_state ([ChildSpec | T ], State ) ->
202272 Child = child_spec_to_record (ChildSpec ),
203273 NewChildren = [Child | State # state .children ],
204274 init_state (T , State # state {children = NewChildren });
205275init_state ([], State ) ->
206276 State # state {children = lists :reverse (State # state .children )}.
207277
278+ -spec start_children (ChildSpecs :: [child_spec ()], State :: # state {}) ->
279+ ChildSpecs :: [child_spec ()].
208280start_children ([Child | T ], StartedC ) ->
209281 case try_start (Child ) of
210282 {ok , Pid , _Result } ->
@@ -213,6 +285,7 @@ start_children([Child | T], StartedC) ->
213285start_children ([], StartedC ) ->
214286 StartedC .
215287
288+ % @hidden
216289handle_call ({start_child , ChildSpec }, _From , # state {children = Children } = State ) ->
217290 Child = child_spec_to_record (ChildSpec ),
218291 # child {id = ID } = Child ,
@@ -287,10 +360,13 @@ handle_call(count_children, _From, #state{children = Children} = State) ->
287360 Reply = [{specs , Specs }, {active , Active }, {supervisors , Supers }, {workers , Workers }],
288361 {reply , Reply , State }.
289362
363+ % @hidden
290364handle_cast (_Msg , State ) ->
291365 {noreply , State }.
292366
367+ % @hidden
293368handle_info ({'EXIT' , Pid , Reason }, State ) ->
369+ % TODO: log crash report
294370 handle_child_exit (Pid , Reason , State );
295371handle_info ({ensure_killed , Pid }, State ) ->
296372 case lists :keyfind (Pid , # child .pid , State # state .children ) of
@@ -330,17 +406,19 @@ handle_info({try_again_restart, Id}, State) ->
330406 ),
331407 {noreply , State1 # state {children = UpdatedChildren }};
332408 {error , {_ , _ }} ->
409+ % TODO: log crash report
333410 {noreply , State1 , {timeout , 0 , {try_again_restart , Id }}}
334411 end ;
335412 {shutdown , State1 } ->
336413 RemainingChildren = lists :keydelete (Id , # child .id , State1 # state .children ),
414+ % TODO: log supervisor shutdown
337415 {stop , shutdown , State1 # state {children = RemainingChildren }}
338416 end
339417 end ;
340418handle_info ({restart_many_children , [# child {pid = undefined } = _Child | Children ]}, State ) ->
341419 {noreply , State , {timeout , 0 , {restart_many_children , Children }}};
342420handle_info (_Msg , State ) ->
343- % TODO: log unexpected message
421+ % TODO: log unexpected message to debug
344422 {noreply , State }.
345423
346424% % @hidden
@@ -377,6 +455,7 @@ handle_child_exit(Pid, Reason, State) ->
377455 RemainingChildren = lists :keydelete (
378456 Pid , # child .pid , State1 # state .children
379457 ),
458+ % TODO: log supervisor shutdown
380459 {stop , shutdown , State1 # state {children = RemainingChildren }}
381460 end ;
382461 false ->
0 commit comments