aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/signal_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/signal_SUITE.erl')
-rw-r--r--erts/emulator/test/signal_SUITE.erl544
1 files changed, 544 insertions, 0 deletions
diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl
new file mode 100644
index 0000000000..e9103ca3c1
--- /dev/null
+++ b/erts/emulator/test/signal_SUITE.erl
@@ -0,0 +1,544 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File : signal_SUITE.erl
+%%% Author : Rickard Green <[email protected]>
+%%% Description : Test signals
+%%%
+%%% Created : 10 Jul 2006 by Rickard Green <[email protected]>
+%%%-------------------------------------------------------------------
+-module(signal_SUITE).
+-author('[email protected]').
+
+-define(DEFAULT_TIMEOUT_SECONDS, 120).
+
+%-define(line_trace, 1).
+-include("test_server.hrl").
+-export([all/1]).
+
+% Test cases
+-export([xm_sig_order/1,
+ pending_exit_unlink_process/1,
+ pending_exit_unlink_dist_process/1,
+ pending_exit_unlink_port/1,
+ pending_exit_trap_exit/1,
+ pending_exit_receive/1,
+ pending_exit_exit/1,
+ pending_exit_gc/1,
+ pending_exit_is_process_alive/1,
+ pending_exit_process_display/1,
+ pending_exit_process_info_1/1,
+ pending_exit_process_info_2/1,
+ pending_exit_group_leader/1,
+ exit_before_pending_exit/1]).
+
+-export([init_per_testcase/2, fin_per_testcase/2, end_per_suite/1]).
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMEOUT_SECONDS)),
+ available_internal_state(true),
+ ?line [{testcase, Func},{watchdog, Dog}|Config].
+
+fin_per_testcase(_Func, Config) ->
+ ?line Dog = ?config(watchdog, Config),
+ ?line ?t:timetrap_cancel(Dog).
+
+end_per_suite(_Config) ->
+ available_internal_state(true),
+ erts_debug:set_internal_state(not_running_optimization, true),
+ available_internal_state(false).
+
+all(suite) ->
+ [xm_sig_order,
+ pending_exit_unlink_process,
+ pending_exit_unlink_dist_process,
+ pending_exit_unlink_port,
+ pending_exit_trap_exit,
+ pending_exit_receive,
+ pending_exit_trap_exit,
+ pending_exit_gc,
+ pending_exit_is_process_alive,
+ pending_exit_process_display,
+ pending_exit_process_info_1,
+ pending_exit_process_info_2,
+ pending_exit_group_leader,
+ exit_before_pending_exit].
+
+xm_sig_order(doc) -> ["Test that exit signals and messages are received "
+ "in correct order"];
+xm_sig_order(suite) -> [];
+xm_sig_order(Config) when is_list(Config) ->
+ ?line LNode = node(),
+ ?line repeat(fun () -> xm_sig_order_test(LNode) end, 1000),
+ ?line {ok, RNode} = start_node(Config),
+ ?line repeat(fun () -> xm_sig_order_test(RNode) end, 1000),
+ ?line stop_node(RNode),
+ ?line ok.
+
+
+xm_sig_order_test(Node) ->
+ ?line P = spawn(Node, fun () -> xm_sig_order_proc() end),
+ ?line M = erlang:monitor(process, P),
+ ?line P ! may_reach,
+ ?line P ! may_reach,
+ ?line P ! may_reach,
+ ?line exit(P, good_signal_order),
+ ?line P ! may_not_reach,
+ ?line P ! may_not_reach,
+ ?line P ! may_not_reach,
+ ?line receive
+ {'DOWN', M, process, P, R} ->
+ ?line good_signal_order = R
+ end.
+
+xm_sig_order_proc() ->
+ receive
+ may_not_reach -> exit(bad_signal_order);
+ may_reach -> ok
+ after 0 -> ok
+ end,
+ xm_sig_order_proc().
+
+pending_exit_unlink_process(doc) -> [];
+pending_exit_unlink_process(suite) -> [];
+pending_exit_unlink_process(Config) when is_list(Config) ->
+ ?line pending_exit_test(self(), unlink).
+
+pending_exit_unlink_dist_process(doc) -> [];
+pending_exit_unlink_dist_process(suite) -> [];
+pending_exit_unlink_dist_process(Config) when is_list(Config) ->
+ ?line {ok, Node} = start_node(Config),
+ ?line From = spawn(Node, fun () -> receive after infinity -> ok end end),
+ ?line Res = pending_exit_test(From, unlink),
+ ?line stop_node(Node),
+ ?line Res.
+
+pending_exit_unlink_port(doc) -> [];
+pending_exit_unlink_port(suite) -> [];
+pending_exit_unlink_port(Config) when is_list(Config) ->
+ ?line pending_exit_test(hd(erlang:ports()), unlink).
+
+pending_exit_trap_exit(doc) -> [];
+pending_exit_trap_exit(suite) -> [];
+pending_exit_trap_exit(Config) when is_list(Config) ->
+ ?line pending_exit_test(self(), trap_exit).
+
+pending_exit_receive(doc) -> [];
+pending_exit_receive(suite) -> [];
+pending_exit_receive(Config) when is_list(Config) ->
+ ?line pending_exit_test(self(), 'receive').
+
+pending_exit_exit(doc) -> [];
+pending_exit_exit(suite) -> [];
+pending_exit_exit(Config) when is_list(Config) ->
+ ?line pending_exit_test(self(), exit).
+
+pending_exit_gc(doc) -> [];
+pending_exit_gc(suite) -> [];
+pending_exit_gc(Config) when is_list(Config) ->
+ ?line pending_exit_test(self(), gc).
+
+pending_exit_test(From, Type) ->
+ ?line case catch erlang:system_info(smp_support) of
+ true ->
+ ?line OTE = process_flag(trap_exit, true),
+ ?line Ref = make_ref(),
+ ?line Master = self(),
+ ?line ExitBySignal = case Type of
+ gc ->
+ lists:duplicate(10000,
+ exit_by_signal);
+ _ ->
+ exit_by_signal
+ end,
+ ?line Pid = spawn_link(
+ fun () ->
+ receive go -> ok end,
+ false = have_pending_exit(),
+ exit = fake_exit(From,
+ self(),
+ ExitBySignal),
+ true = have_pending_exit(),
+ Master ! {self(), Ref, Type},
+ case Type of
+ gc ->
+ force_gc(),
+ erlang:yield();
+ unlink ->
+ unlink(From);
+ trap_exit ->
+ process_flag(trap_exit, true);
+ 'receive' ->
+ receive _ -> ok
+ after 0 -> ok
+ end;
+ exit ->
+ ok
+ end,
+ exit(exit_by_myself)
+ end),
+ ?line Mon = erlang:monitor(process, Pid),
+ ?line Pid ! go,
+ ?line Reason = receive
+ {'DOWN', Mon, process, Pid, R} ->
+ ?line receive
+ {Pid, Ref, Type} ->
+ ?line ok
+ after 0 ->
+ ?line ?t:fail(premature_exit)
+ end,
+ ?line case Type of
+ exit ->
+ ?line exit_by_myself = R;
+ _ ->
+ ?line ExitBySignal = R
+ end
+ end,
+ ?line receive
+ {'EXIT', Pid, R2} ->
+ ?line Reason = R2
+ end,
+ ?line process_flag(trap_exit, OTE),
+ ?line ok,
+ {comment,
+ "Test only valid with current SMP emulator."};
+ _ ->
+ {skipped,
+ "SMP support not enabled. "
+ "Test only valid with current SMP emulator."}
+ end.
+
+
+
+exit_before_pending_exit(doc) -> [];
+exit_before_pending_exit(suite) -> [];
+exit_before_pending_exit(Config) when is_list(Config) ->
+ %% This is a testcase testcase very specific to the smp
+ %% implementation as it is of the time of writing.
+ %%
+ %% The testcase tries to check that a process can
+ %% exit by itself even though it has a pending exit.
+ ?line OTE = process_flag(trap_exit, true),
+ ?line Master = self(),
+ ?line Tester = spawn_link(
+ fun () ->
+ Opts = case {erlang:system_info(run_queues),
+ erlang:system_info(schedulers_online)} of
+ {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
+ _ ->
+ process_flag(scheduler, 1),
+ [{scheduler, 2}]
+ end,
+ P = self(),
+ Exiter = spawn_opt(fun () ->
+ receive
+ {exit_me, P, R} ->
+ exit(P, R)
+ end
+ end, Opts),
+ erlang:yield(),
+ Exiter ! {exit_me, self(), exited_by_exiter},
+ %% We want to get a pending exit
+ %% before we exit ourselves. We
+ %% don't want to be scheduled out
+ %% since we will then see the
+ %% pending exit.
+ %%
+ %% Do something that takes
+ %% relatively long time but
+ %% consumes few reductions...
+ repeat(fun() -> erlang:system_info(procs) end,10),
+ %% ... then exit.
+ Master ! {self(),
+ pending_exit,
+ have_pending_exit()},
+ exit(exited_by_myself)
+ end),
+ ?line PendingExit = receive {Tester, pending_exit, PE} -> PE end,
+ ?line receive
+ {'EXIT', Tester, exited_by_myself} ->
+ ?line process_flag(trap_exit, OTE),
+ ?line ok;
+ Msg ->
+ ?line ?t:fail({unexpected_message, Msg})
+ end,
+ NoScheds = integer_to_list(erlang:system_info(schedulers_online)),
+ {comment,
+ "Was "
+ ++ case PendingExit of
+ true -> "";
+ false ->"*not*"
+ end ++ " able to trigger a pending exit. "
+ ++ "Running on " ++ NoScheds ++ " scheduler(s). "
+ ++ "This test is only interesting with at least two schedulers."}.
+
+-define(PE_INFO_REPEAT, 100).
+
+pending_exit_is_process_alive(Config) when is_list(Config) ->
+ ?line S = exit_op_test_init(),
+ ?line TestFun = fun (P) -> false = is_process_alive(P) end,
+ ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ ?line verify_pending_exit_success(S),
+ ?line comment().
+
+pending_exit_process_info_1(Config) when is_list(Config) ->
+ ?line S = exit_op_test_init(),
+ ?line TestFun = fun (P) ->
+ undefined = process_info(P)
+ end,
+ ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ ?line verify_pending_exit_success(S),
+ ?line comment().
+
+pending_exit_process_info_2(Config) when is_list(Config) ->
+ ?line S0 = exit_op_test_init(),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, messages)
+ end, ?PE_INFO_REPEAT),
+ ?line S1 = verify_pending_exit_success(S0),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, status)
+ end, ?PE_INFO_REPEAT),
+ ?line S2 = verify_pending_exit_success(S1),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, links)
+ end, ?PE_INFO_REPEAT),
+ ?line S3 = verify_pending_exit_success(S2),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [messages])
+ end, ?PE_INFO_REPEAT),
+ ?line S4 = verify_pending_exit_success(S3),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [status])
+ end, ?PE_INFO_REPEAT),
+ ?line S5 = verify_pending_exit_success(S4),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [links])
+ end, ?PE_INFO_REPEAT),
+ ?line S6 = verify_pending_exit_success(S5),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [status,
+ links])
+ end, ?PE_INFO_REPEAT),
+ ?line S7 = verify_pending_exit_success(S6),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [messages,
+ status])
+ end, ?PE_INFO_REPEAT),
+ ?line S8 = verify_pending_exit_success(S7),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [messages,
+ links])
+ end, ?PE_INFO_REPEAT),
+ ?line S9 = verify_pending_exit_success(S8),
+ ?line repeated_exit_op_test(
+ fun (P) ->
+ undefined = process_info(P, [message_queue_len,
+ status])
+ end, ?PE_INFO_REPEAT),
+ ?line S10 = verify_pending_exit_success(S9),
+ ?line repeated_exit_op_test(fun (P) ->
+ undefined = process_info(P, [messages,
+ links,
+ status])
+ end, ?PE_INFO_REPEAT),
+ ?line verify_pending_exit_success(S10),
+ ?line comment().
+
+pending_exit_process_display(Config) when is_list(Config) ->
+ ?line S = exit_op_test_init(),
+ ?line TestFun = fun (P) ->
+ badarg = try
+ erlang:process_display(P, backtrace)
+ catch
+ error:badarg -> badarg
+ end
+ end,
+ ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ ?line verify_pending_exit_success(S),
+ ?line comment().
+
+pending_exit_group_leader(Config) when is_list(Config) ->
+ ?line S = exit_op_test_init(),
+ ?line TestFun = fun (P) ->
+ badarg = try
+ group_leader(self(), P)
+ catch
+ error:badarg -> badarg
+ end
+ end,
+ ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
+ ?line verify_pending_exit_success(S),
+ ?line comment().
+
+%%
+%% -- Internal utils --------------------------------------------------------
+%%
+exit_op_test_init() ->
+ put(no_pending_exit_success, 0),
+ put(no_pending_exit_tries, 0),
+ {case {erlang:system_info(run_queues),
+ erlang:system_info(schedulers_online)} of
+ {RQ, SO} when RQ =:= 1; SO =:= 1 -> false;
+ _ -> true
+ end, 0, 0}.
+
+verify_pending_exit_success({false, _, _} = S) ->
+ S;
+verify_pending_exit_success({true, S, T}) ->
+ NewS = get(no_pending_exit_success),
+ NewT = get(no_pending_exit_tries),
+ case NewT =:= T of
+ true -> ok;
+ _ -> case NewS > S of
+ true -> ok;
+ _ -> exit(no_pending_exits)
+ end
+ end,
+ {true, NewS, NewT}.
+
+comment() ->
+ {comment,
+ "Pending exit trigger ratio "
+ ++ integer_to_list(get(no_pending_exit_success))
+ ++ "/"
+ ++ integer_to_list(get(no_pending_exit_tries))
+ ++ "."
+ ++ case get(not_running_opt_test) of
+ true -> " No 'not running optimization' to disable.";
+ _ -> ""
+ end}.
+
+repeated_exit_op_test(TestFun, N) ->
+ WorkFun0 = fun () ->
+ lists:sort(lists:reverse(lists:seq(1, 1000)))
+ end,
+ repeat(fun () -> exit_op_test(TestFun, WorkFun0) end, N),
+ try erts_debug:set_internal_state(not_running_optimization, false) of
+ Bool when Bool == true; Bool == false ->
+ WorkFun1 = fun () ->
+ erts_debug:set_internal_state(sleep, 0),
+ lists:sort(lists:reverse(lists:seq(1, 1000)))
+ end,
+ repeat(fun () ->
+ exit_op_test(TestFun, WorkFun1)
+ end, N)
+ catch
+ error:notsup -> put(not_running_opt_test, true)
+ after
+ catch erts_debug:set_internal_state(not_running_optimization, true)
+ end.
+
+exit_op_test(TestFun, WorkFun) ->
+ Opts = case {erlang:system_info(run_queues),
+ erlang:system_info(schedulers_online)} of
+ {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
+ _ ->
+ process_flag(scheduler, 1),
+ [{scheduler, 2}]
+ end,
+ Master = self(),
+ Going = make_ref(),
+ P = spawn_opt(fun () ->
+ loop(10, WorkFun),
+ Master ! Going,
+ loop(infinity, WorkFun)
+ end, Opts),
+ receive Going -> ok end,
+ loop(10, WorkFun),
+ erlang:yield(),
+ exit(P, bang),
+ PE0 = have_pending_exit(P),
+ TestFun(P),
+ PE = case PE0 of
+ true -> true;
+ _ -> false
+ end,
+ case {PE, get(no_pending_exit_success), get(no_pending_exit_tries)} of
+ {true, undefined, undefined} ->
+ put(no_pending_exit_success, 1),
+ put(no_pending_exit_tries, 1);
+ {false, undefined, undefined} ->
+ put(no_pending_exit_success, 0),
+ put(no_pending_exit_tries, 1);
+ {true, S, T} ->
+ put(no_pending_exit_success, S+1),
+ put(no_pending_exit_tries, T+1);
+ {false, _S, T} ->
+ put(no_pending_exit_tries, T+1)
+ end,
+ ok.
+
+loop(infinity, WorkFun) ->
+ do_loop(infinity, WorkFun);
+loop(0, _WorkFun) ->
+ ok;
+loop(N, WorkFun) when is_integer(N) ->
+ do_loop(N-1, WorkFun).
+
+do_loop(N, WorkFun) ->
+ WorkFun(),
+ loop(N, WorkFun).
+
+repeat(_Fun, N) when is_integer(N), N =< 0 ->
+ ok;
+repeat(Fun, N) when is_integer(N) ->
+ Fun(),
+ repeat(Fun, N-1).
+
+start_node(Config) ->
+ {A, B, C} = now(),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(?config(testcase, Config))
+ ++ "-" ++ integer_to_list(A)
+ ++ "-" ++ integer_to_list(B)
+ ++ "-" ++ integer_to_list(C)),
+ Pa = filename:dirname(code:which(?MODULE)),
+ ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]).
+
+stop_node(Node) ->
+ ?t:stop_node(Node).
+
+have_pending_exit() ->
+ have_pending_exit(self()).
+
+have_pending_exit(Pid) ->
+ erts_debug:get_internal_state({have_pending_exit, Pid}).
+
+force_gc() ->
+ erts_debug:set_internal_state(force_gc, self()).
+
+fake_exit(From, To, Reason) ->
+ erts_debug:set_internal_state(send_fake_exit_signal, {From, To, Reason}).
+
+available_internal_state(Bool) when Bool == true; Bool == false ->
+ case {Bool,
+ (catch erts_debug:get_internal_state(available_internal_state))} of
+ {true, true} ->
+ true;
+ {false, true} ->
+ erts_debug:set_internal_state(available_internal_state, false),
+ true;
+ {true, _} ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ false;
+ {false, _} ->
+ false
+ end.