Skip to content

Commit bf4d8c2

Browse files
committed
sasl: suspend processes concurrently during release upgrades
Add sys:multi_suspend/1,2 and use it in release_handler_1 to send suspend requests in parallel instead of one at a time.
1 parent 22a5f20 commit bf4d8c2

2 files changed

Lines changed: 64 additions & 21 deletions

File tree

lib/sasl/src/release_handler_1.erl

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -483,27 +483,21 @@ get_opt(Tag, EvalState, Default) ->
483483
%% goes for processes that didn't respond to the suspend message.
484484
%%-----------------------------------------------------------------
485485
suspend(Mod, Procs, Timeout) ->
486-
lists:zf(fun({_Sup, _Name, Pid, Mods}) ->
487-
case lists:member(Mod, Mods) of
488-
true ->
489-
case catch sys_suspend(Pid, Timeout) of
490-
ok -> {true, Pid};
491-
_ ->
492-
% If the proc hangs, make sure to
493-
% resume it when it gets suspended!
494-
catch sys:resume(Pid),
495-
false
496-
end;
497-
false ->
498-
false
499-
end
500-
end,
501-
Procs).
502-
503-
sys_suspend(Pid, default) ->
504-
sys:suspend(Pid);
505-
sys_suspend(Pid, Timeout) ->
506-
sys:suspend(Pid, Timeout).
486+
Pids = [Pid || {_Sup, _Name, Pid, Mods} <- Procs,
487+
lists:member(Mod, Mods)],
488+
Results = case Timeout of
489+
default -> sys:multi_suspend(Pids);
490+
_ -> sys:multi_suspend(Pids, Timeout)
491+
end,
492+
lists:filtermap(
493+
fun({Pid, ok}) ->
494+
{true, Pid};
495+
({Pid, _Error}) ->
496+
% If the proc hangs, make sure to
497+
% resume it when it gets suspended!
498+
catch sys:resume(Pid),
499+
false
500+
end, Results).
507501

508502
resume(Pids) ->
509503
lists:foreach(fun(Pid) -> catch sys:resume(Pid) end, Pids).

lib/stdlib/src/sys.erl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ the process itself to format these events.
8080

8181
%% External exports
8282
-export([suspend/1, suspend/2, resume/1, resume/2,
83+
multi_suspend/1, multi_suspend/2,
8384
get_status/1, get_status/2,
8485
get_state/1, get_state/2,
8586
replace_state/2, replace_state/3,
@@ -284,6 +285,29 @@ system messages, but not other messages.
284285
Timeout :: timeout().
285286
suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout).
286287

288+
-doc(#{equiv => multi_suspend(Names, 5000)}).
289+
-spec multi_suspend(Names) -> [{Name, Result}] when
290+
Names :: [name()],
291+
Name :: name(),
292+
Result :: 'ok' | {'error', term()}.
293+
multi_suspend(Names) -> multi_suspend(Names, 5000).
294+
295+
-doc """
296+
Suspends multiple processes concurrently.
297+
298+
Sends suspend requests to all processes in `Names` simultaneously and
299+
collects the results. Returns a list of `{Name, Result}` tuples in
300+
the same order as `Names`, where `Result` is `ok` on success or
301+
`{error, Reason}` on failure.
302+
""".
303+
-spec multi_suspend(Names, Timeout) -> [{Name, Result}] when
304+
Names :: [name()],
305+
Name :: name(),
306+
Timeout :: timeout(),
307+
Result :: 'ok' | {'error', term()}.
308+
multi_suspend(Names, Timeout) ->
309+
send_multi_system_msg(Names, suspend, Timeout).
310+
287311
-doc(#{equiv => resume(Name, 5000)}).
288312
-spec resume(Name) -> 'ok' when
289313
Name :: name().
@@ -763,6 +787,31 @@ send_system_msg(Name, Request, Timeout) ->
763787
exit({Reason, mfa(Name, Request, Timeout)})
764788
end.
765789

790+
send_multi_system_msg(Names, Request, Timeout) ->
791+
ReqIdCol = lists:foldl(
792+
fun(Name, Acc) ->
793+
gen:send_request(Name, system, Request, Name, Acc)
794+
end, gen:reqids_new(), Names),
795+
ResultMap = collect_multi_responses(ReqIdCol, Timeout, #{}),
796+
[{Name, maps:get(Name, ResultMap)} || Name <- Names].
797+
798+
collect_multi_responses(ReqIdCol, Timeout, Acc) ->
799+
case gen:wait_response(ReqIdCol, Timeout, true) of
800+
{{reply, Reply}, Name, NewReqIdCol} ->
801+
collect_multi_responses(NewReqIdCol, Timeout,
802+
Acc#{Name => Reply});
803+
{{error, {Reason, _Ref}}, Name, NewReqIdCol} ->
804+
collect_multi_responses(NewReqIdCol, Timeout,
805+
Acc#{Name => {error, Reason}});
806+
timeout ->
807+
Remaining = gen:reqids_to_list(ReqIdCol),
808+
lists:foldl(
809+
fun({_ReqId, Name}, A) -> A#{Name => {error, timeout}} end,
810+
Acc, Remaining);
811+
no_request ->
812+
Acc
813+
end.
814+
766815
mfa(Name, {debug, {Func, Arg2}}) ->
767816
{sys, Func, [Name, Arg2]};
768817
mfa(Name, {change_code, Mod, Vsn, Extra}) ->

0 commit comments

Comments
 (0)