diff options
author | Rickard Green <rickard@erlang.org> | 2023-01-12 09:55:51 +0100 |
---|---|---|
committer | Rickard Green <rickard@erlang.org> | 2023-01-12 19:23:16 +0100 |
commit | 9c6c7b014d155f03b13bdb30aec1fa52eea6d993 (patch) | |
tree | 5c5487e585dfb9b4c036afc685f780b81930107d /erts/emulator/test | |
parent | c3603bd7c0d5f2928c68157e843955c7bf25a9ef (diff) | |
download | erlang-9c6c7b014d155f03b13bdb30aec1fa52eea6d993.tar.gz |
[erts] Better testcase for dirty signal handling race
Diffstat (limited to 'erts/emulator/test')
-rw-r--r-- | erts/emulator/test/signal_SUITE.erl | 117 |
1 files changed, 116 insertions, 1 deletions
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 076d9f9885..31b4d3431e 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -37,6 +37,7 @@ -export([xm_sig_order/1, kill2killed/1, contended_signal_handling/1, + dirty_signal_handling_race/1, busy_dist_exit_signal/1, busy_dist_demonitor_signal/1, busy_dist_down_signal/1, @@ -63,6 +64,7 @@ all() -> [xm_sig_order, kill2killed, contended_signal_handling, + dirty_signal_handling_race, busy_dist_exit_signal, busy_dist_demonitor_signal, busy_dist_down_signal, @@ -186,7 +188,15 @@ contended_signal_handling(Config) when is_list(Config) -> %% when the bug exists, but this testcase at least %% sometimes causes a crash when the bug is present. %% + move_dirty_signal_handlers_to_first_scheduler(), process_flag(priority, high), + case erlang:system_info(schedulers_online) of + 1 -> + ok; + SOnln -> + process_flag(scheduler, SOnln), + ok + end, Drv = unlink_signal_drv, ok = load_driver(Config, Drv), try @@ -209,7 +219,7 @@ contended_signal_handling_test(Drv, N) -> contended_signal_handling_cmd_ports([]) -> ok; contended_signal_handling_cmd_ports([P|Ps]) -> - P ! {self(), {command, ""}}, + P ! {self(), {command, "c"}}, contended_signal_handling_cmd_ports(Ps). contended_signal_handling_make_ports(_Drv, 0, Ports) -> @@ -219,6 +229,111 @@ contended_signal_handling_make_ports(Drv, N, Ports) -> true = is_port(Port), contended_signal_handling_make_ports(Drv, N-1, [Port|Ports]). +dirty_signal_handling_race(Config) -> + %% This test case trigger more or less the same + %% problematic scenario as the contended_signal_handling + %% test case is trying to trigger. This test case triggers + %% it via another signal and is also much more likely + %% (close to 100%) to trigger the problematic schenario. + Tester = self(), + move_dirty_signal_handlers_to_first_scheduler(), + {S0, S1} = case erlang:system_info(schedulers_online) of + 1 -> {1, 1}; + 2 -> {2, 1}; + SOnln -> {SOnln, SOnln-1} + end, + process_flag(priority, high), + process_flag(scheduler, S0), + erts_debug:set_internal_state(available_internal_state, true), + Drv = unlink_signal_drv, + ok = load_driver(Config, Drv), + try + %% {parallelism, true} option will ensure that each + %% signal to the port from a process is scheduled which + %% forces the process to release its main lock when + %% sending the signal... + Port = open_port({spawn, Drv}, [{parallelism, true}]), + true = is_port(Port), + %% The {alias, reply_demonitor} option will trigger a + %% 'demonitor' signal from Tester to the port when an + %% alias message sent using the alias is received by + %% Tester... + MA1 = erlang:monitor(port, Port, [{alias, reply_demonitor}]), + MA2 = erlang:monitor(port, Port, [{alias, reply_demonitor}]), + Pid = spawn_opt(fun () -> + Tester ! go, + receive after 500 -> ok end, + %% The 'proc_sig_block' test signal will cause + %% dirty signal handling to start and be + %% blocked in the signal handling. + erts_debug:set_internal_state(proc_sig_block, + {Tester, 1000}), + %% Tester will be stuck waiting for main lock + %% when being scheduled out from its dirty + %% execution. When this alias message is + %% by the dirty signal handler Tester will be + %% able to aquire the main lock and complete + %% the schedule out operation. + MA1 ! {MA1, trigger_demonitor_port_please}, + erts_debug:set_internal_state(proc_sig_block, + {Tester, 100}), + %% Tester will have been selected for + %% execution, but stuck waiting for main lock. + %% When this alias message is handled by the + %% dirty signal handler, Tester will be able + %% to aquire the main lock which will let it + %% enter the problematic scenario. That is, + %% ongoing dirty signal handling while it + %% begins executing. + MA2 ! {MA2, trigger_demonitor_port_please}, + erts_debug:set_internal_state(proc_sig_block, + {Tester, 500}), + ok + end, [link, {scheduler, S1}]), + receive go -> ok end, + receive {'DOWN', MA1, port, Port, _} -> ct:fail(unexpected_port_down) + after 0 -> ok + end, + receive {'DOWN', MA2, port, Port, _} -> ct:fail(unexpected_port_down) + after 0 -> ok + end, + erts_debug:dirty_cpu(wait, 1000), + receive + {MA1, trigger_demonitor_port_please} -> ok + end, + receive + {MA2, trigger_demonitor_port_please} -> ok + end, + unlink(Pid), + unlink(Port), + exit(Pid, kill), + exit(Port, kill), + false = erlang:is_process_alive(Pid) + after + ok = erl_ddll:unload_driver(Drv) + end, + ok. + +move_dirty_signal_handlers_to_first_scheduler() -> + SOnln = erlang:system_flag(schedulers_online, 1), + try + true = lists:foldl( + fun (Pid, FoundOne) -> + case process_info(Pid, initial_call) of + {initial_call, {erts_dirty_process_signal_handler,start,0}} -> + Pid ! please_execute_a_bit, + true; + _ -> + FoundOne + end + end, + false, + processes()) + after + erlang:system_flag(schedulers_online, SOnln) + end, + ok. + busy_dist_exit_signal(Config) when is_list(Config) -> BusyTime = 1000, {ok, BusyChannelNode} = start_node(Config), |