Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 15 additions & 21 deletions lib/sasl/src/release_handler_1.erl
Original file line number Diff line number Diff line change
Expand Up @@ -483,27 +483,21 @@ get_opt(Tag, EvalState, Default) ->
%% goes for processes that didn't respond to the suspend message.
%%-----------------------------------------------------------------
suspend(Mod, Procs, Timeout) ->
lists:zf(fun({_Sup, _Name, Pid, Mods}) ->
case lists:member(Mod, Mods) of
true ->
case catch sys_suspend(Pid, Timeout) of
ok -> {true, Pid};
_ ->
% If the proc hangs, make sure to
% resume it when it gets suspended!
catch sys:resume(Pid),
false
end;
false ->
false
end
end,
Procs).

sys_suspend(Pid, default) ->
sys:suspend(Pid);
sys_suspend(Pid, Timeout) ->
sys:suspend(Pid, Timeout).
Pids = [Pid || {_Sup, _Name, Pid, Mods} <- Procs,
lists:member(Mod, Mods)],
Results = case Timeout of
default -> sys:multi_suspend(Pids);
_ -> sys:multi_suspend(Pids, Timeout)
end,
lists:filtermap(
fun({Pid, ok}) ->
{true, Pid};
({Pid, _Error}) ->
% If the proc hangs, make sure to
% resume it when it gets suspended!
catch sys:resume(Pid),
false
end, Results).

resume(Pids) ->
lists:foreach(fun(Pid) -> catch sys:resume(Pid) end, Pids).
Expand Down
49 changes: 49 additions & 0 deletions lib/stdlib/src/sys.erl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ the process itself to format these events.

%% External exports
-export([suspend/1, suspend/2, resume/1, resume/2,
multi_suspend/1, multi_suspend/2,
get_status/1, get_status/2,
get_state/1, get_state/2,
replace_state/2, replace_state/3,
Expand Down Expand Up @@ -284,6 +285,29 @@ system messages, but not other messages.
Timeout :: timeout().
suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout).

-doc(#{equiv => multi_suspend(Names, 5000)}).
-spec multi_suspend(Names) -> [{Name, Result}] when
Names :: [name()],
Name :: name(),
Result :: 'ok' | {'error', term()}.
multi_suspend(Names) -> multi_suspend(Names, 5000).

-doc """
Suspends multiple processes concurrently.

Sends suspend requests to all processes in `Names` simultaneously and
collects the results. Returns a list of `{Name, Result}` tuples in
the same order as `Names`, where `Result` is `ok` on success or
`{error, Reason}` on failure.
""".
-spec multi_suspend(Names, Timeout) -> [{Name, Result}] when
Names :: [name()],
Name :: name(),
Timeout :: timeout(),
Result :: 'ok' | {'error', term()}.
multi_suspend(Names, Timeout) ->
send_multi_system_msg(Names, suspend, Timeout).

-doc(#{equiv => resume(Name, 5000)}).
-spec resume(Name) -> 'ok' when
Name :: name().
Expand Down Expand Up @@ -763,6 +787,31 @@ send_system_msg(Name, Request, Timeout) ->
exit({Reason, mfa(Name, Request, Timeout)})
end.

send_multi_system_msg(Names, Request, Timeout) ->
ReqIdCol = lists:foldl(
fun(Name, Acc) ->
gen:send_request(Name, system, Request, Name, Acc)
end, gen:reqids_new(), Names),
ResultMap = collect_multi_responses(ReqIdCol, Timeout, #{}),
[{Name, maps:get(Name, ResultMap)} || Name <- Names].

collect_multi_responses(ReqIdCol, Timeout, Acc) ->
case gen:wait_response(ReqIdCol, Timeout, true) of
{{reply, Reply}, Name, NewReqIdCol} ->
collect_multi_responses(NewReqIdCol, Timeout,
Acc#{Name => Reply});
{{error, {Reason, _Ref}}, Name, NewReqIdCol} ->
collect_multi_responses(NewReqIdCol, Timeout,
Acc#{Name => {error, Reason}});
timeout ->
Remaining = gen:reqids_to_list(ReqIdCol),
lists:foldl(
fun({_ReqId, Name}, A) -> A#{Name => {error, timeout}} end,
Acc, Remaining);
no_request ->
Acc
end.

mfa(Name, {debug, {Func, Arg2}}) ->
{sys, Func, [Name, Arg2]};
mfa(Name, {change_code, Mod, Vsn, Extra}) ->
Expand Down
31 changes: 28 additions & 3 deletions lib/stdlib/test/sys_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
%% %CopyrightEnd%
%%
-module(sys_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,log/1,log_to_file/1,
stats/1,trace/1,suspend/1,install/1,special_process/1]).
stats/1,trace/1,suspend/1,multi_suspend/1,
install/1,special_process/1]).
-export([handle_call/3,terminate/2,init/1]).
-include_lib("common_test/include/ct.hrl").

Expand All @@ -35,7 +36,8 @@
suite() -> [{ct_hooks,[ts_install_cth]}].

all() ->
[log, log_to_file, stats, trace, suspend, install, special_process].
[log, log_to_file, stats, trace, suspend, multi_suspend,
install, special_process].

groups() ->
[].
Expand Down Expand Up @@ -124,6 +126,29 @@ suspend(Config) when is_list(Config) ->
stop(),
ok.

multi_suspend(Config) when is_list(Config) ->
S1 = sys_SUITE_server1,
S2 = sys_SUITE_server2,
{ok,_} = gen_server:start_link({local,S1},?MODULE,[],[]),
{ok,_} = gen_server:start_link({local,S2},?MODULE,[],[]),
[{S1,ok},{S2,ok}] = sys:multi_suspend([S1,S2],1000),
{'EXIT',_} = (catch gen_server:call(S1,{req,48},1000)),
{'EXIT',_} = (catch gen_server:call(S2,{req,48},1000)),
{status,_,_,[_,suspended,_,_,_]} = sys:get_status(S1),
{status,_,_,[_,suspended,_,_,_]} = sys:get_status(S2),
sys:resume(S1),
sys:resume(S2),
{status,_,_,[_,running,_,_,_]} = sys:get_status(S1),
{status,_,_,[_,running,_,_,_]} = sys:get_status(S2),
[] = sys:multi_suspend([],1000),
[{S1,ok},{no_such_process,{error,noproc}}] =
sys:multi_suspend([S1,no_such_process],1000),
{status,_,_,[_,suspended,_,_,_]} = sys:get_status(S1),
sys:resume(S1),
gen_server:call(S1,stop,1000),
gen_server:call(S2,stop,1000),
ok.

install(Config) when is_list(Config) ->
{ok,_Server} = start(),
Master = self(),
Expand Down
Loading