Skip to content

Commit 883b69a

Browse files
committed
Merge pull request #1764 from petermm/sup_count_children
Add count_children/1 to supervisor.erl These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 0a999b2 + e040b9b commit 883b69a

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5252
- Added `erlang:module_loaded/1`
5353
- Added `binary:replace/3`, `binary:replace/4`
5454
- Added `binary:match/2` and `binary:match/3`
55-
- Added `supervisor:which_children/1`
55+
- Added `supervisor:which_children/1` and `supervisor:count_children/1`
5656
- Added `monitored_by` in `process_info/2`
5757
- Added mock implementation for `current_stacktrace` in `process_info`
5858

libs/estdlib/src/supervisor.erl

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
terminate_child/2,
3030
restart_child/2,
3131
delete_child/2,
32-
which_children/1
32+
which_children/1,
33+
count_children/1
3334
]).
3435

3536
-export([
@@ -112,6 +113,9 @@ delete_child(Supervisor, ChildId) ->
112113
which_children(Supervisor) ->
113114
gen_server:call(Supervisor, which_children).
114115

116+
count_children(Supervisor) ->
117+
gen_server:call(Supervisor, count_children).
118+
115119
init({Mod, Args}) ->
116120
erlang:process_flag(trap_exit, true),
117121
case Mod:init(Args) of
@@ -280,7 +284,12 @@ handle_call({delete_child, ID}, _From, #state{children = Children} = State) ->
280284
end;
281285
handle_call(which_children, _From, #state{children = Children} = State) ->
282286
ChildrenInfo = lists:map(fun child_to_info/1, Children),
283-
{reply, ChildrenInfo, State}.
287+
{reply, ChildrenInfo, State};
288+
handle_call(count_children, _From, #state{children = Children} = State) ->
289+
{Specs, Active, Supers, Workers} =
290+
lists:foldl(fun count_child/2, {0, 0, 0, 0}, Children),
291+
Reply = [{specs, Specs}, {active, Active}, {supervisors, Supers}, {workers, Workers}],
292+
{reply, Reply, State}.
284293

285294
handle_cast(_Msg, State) ->
286295
{noreply, State}.
@@ -364,6 +373,17 @@ child_to_info(#child{id = Id, pid = Pid, type = Type, modules = Modules}) ->
364373
end,
365374
{Id, Child, Type, Modules}.
366375

376+
count_child(#child{pid = Pid, type = worker}, {Specs, Active, Supers, Workers}) ->
377+
case is_pid(Pid) andalso is_process_alive(Pid) of
378+
true -> {Specs + 1, Active + 1, Supers, Workers + 1};
379+
false -> {Specs + 1, Active, Supers, Workers + 1}
380+
end;
381+
count_child(#child{pid = Pid, type = supervisor}, {Specs, Active, Supers, Workers}) ->
382+
case is_pid(Pid) andalso is_process_alive(Pid) of
383+
true -> {Specs + 1, Active + 1, Supers + 1, Workers};
384+
false -> {Specs + 1, Active, Supers + 1, Workers}
385+
end.
386+
367387
do_terminate(#child{pid = Pid, shutdown = brutal_kill}) ->
368388
exit(Pid, kill);
369389
do_terminate(#child{pid = Pid, shutdown = infinity}) ->

tests/libs/estdlib/test_supervisor.erl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ test() ->
3434
ok = test_terminate_delete_child(),
3535
ok = test_terminate_timeout(),
3636
ok = test_which_children(),
37+
ok = test_count_children(),
3738
ok.
3839

3940
test_basic_supervisor() ->
@@ -88,6 +89,55 @@ test_start_child() ->
8889
exit(SupPid, shutdown),
8990
ok.
9091

92+
test_count_children() ->
93+
% Test with no children - all counts should be zero
94+
{ok, SupPid} = supervisor:start_link(?MODULE, {test_no_child, self()}),
95+
[{specs, 0}, {active, 0}, {supervisors, 0}, {workers, 0}] = supervisor:count_children(SupPid),
96+
97+
% Add a worker child and verify counts
98+
{ok, _ChildPid} = supervisor:start_child(SupPid, #{
99+
id => test_worker,
100+
start => {ping_pong_server, start_link, [self()]},
101+
restart => permanent,
102+
shutdown => 5000,
103+
type => worker
104+
}),
105+
106+
% Check count_children with one active worker
107+
[{specs, 1}, {active, 1}, {supervisors, 0}, {workers, 1}] = supervisor:count_children(SupPid),
108+
109+
% Add a supervisor child and verify counts
110+
{ok, _SupervisorPid} = supervisor:start_child(SupPid, #{
111+
id => test_supervisor,
112+
start => {?MODULE, start_link, [self()]},
113+
restart => permanent,
114+
shutdown => infinity,
115+
type => supervisor
116+
}),
117+
118+
% Check count_children with one worker and one supervisor
119+
[{specs, 2}, {active, 2}, {supervisors, 1}, {workers, 1}] = supervisor:count_children(SupPid),
120+
121+
% Terminate the worker child - spec remains but child becomes inactive
122+
ok = supervisor:terminate_child(SupPid, test_worker),
123+
[{specs, 2}, {active, 1}, {supervisors, 1}, {workers, 1}] = supervisor:count_children(SupPid),
124+
125+
% Delete the worker child - removes the spec completely
126+
ok = supervisor:delete_child(SupPid, test_worker),
127+
[{specs, 1}, {active, 1}, {supervisors, 1}, {workers, 0}] = supervisor:count_children(SupPid),
128+
129+
% Terminate the supervisor child - spec remains but child becomes inactive
130+
ok = supervisor:terminate_child(SupPid, test_supervisor),
131+
[{specs, 1}, {active, 0}, {supervisors, 1}, {workers, 0}] = supervisor:count_children(SupPid),
132+
133+
% Delete the supervisor child - removes the spec completely
134+
ok = supervisor:delete_child(SupPid, test_supervisor),
135+
[{specs, 0}, {active, 0}, {supervisors, 0}, {workers, 0}] = supervisor:count_children(SupPid),
136+
137+
unlink(SupPid),
138+
exit(SupPid, shutdown),
139+
ok.
140+
91141
test_terminate_delete_child() ->
92142
{ok, SupPid} = supervisor:start_link(?MODULE, {test_no_child, self()}),
93143
{ok, Pid} = supervisor:start_child(SupPid, #{

0 commit comments

Comments
 (0)