1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 2006-2011. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%%-------------------------------------------------------------------
   21: %%% File    : signal_SUITE.erl
   22: %%% Author  : Rickard Green <rickard.s.green@ericsson.com>
   23: %%% Description : Test signals
   24: %%%
   25: %%% Created : 10 Jul 2006 by Rickard Green <rickard.s.green@ericsson.com>
   26: %%%-------------------------------------------------------------------
   27: -module(signal_SUITE).
   28: -author('rickard.s.green@ericsson.com').
   29: 
   30: -define(DEFAULT_TIMEOUT_SECONDS, 120).
   31: 
   32: %-define(line_trace, 1).
   33: -include_lib("test_server/include/test_server.hrl").
   34: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   35: 	 init_per_group/2,end_per_group/2]).
   36: 
   37: % Test cases
   38: -export([xm_sig_order/1,
   39: 	 pending_exit_unlink_process/1,
   40: 	 pending_exit_unlink_dist_process/1,
   41: 	 pending_exit_unlink_port/1,
   42: 	 pending_exit_trap_exit/1,
   43: 	 pending_exit_receive/1,
   44: 	 pending_exit_exit/1,
   45: 	 pending_exit_gc/1,
   46: 	 pending_exit_is_process_alive/1,
   47: 	 pending_exit_process_display/1,
   48: 	 pending_exit_process_info_1/1,
   49: 	 pending_exit_process_info_2/1,
   50: 	 pending_exit_group_leader/1,
   51: 	 exit_before_pending_exit/1]).
   52: 
   53: -export([init_per_testcase/2, end_per_testcase/2]).
   54: 
   55: init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
   56:     ?line Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMEOUT_SECONDS)),
   57:     available_internal_state(true),
   58:     ?line [{testcase, Func},{watchdog, Dog}|Config].
   59: 
   60: end_per_testcase(_Func, Config) ->
   61:     ?line Dog = ?config(watchdog, Config),
   62:     ?line ?t:timetrap_cancel(Dog).
   63: 
   64: init_per_suite(Config) ->
   65:     Config.
   66: 
   67: end_per_suite(_Config) ->
   68:     available_internal_state(true),
   69:     catch erts_debug:set_internal_state(not_running_optimization, true),
   70:     available_internal_state(false).
   71: 
   72: suite() -> [{ct_hooks,[ts_install_cth]}].
   73: 
   74: all() -> 
   75:     [xm_sig_order, pending_exit_unlink_process,
   76:      pending_exit_unlink_dist_process,
   77:      pending_exit_unlink_port, pending_exit_trap_exit,
   78:      pending_exit_receive, pending_exit_trap_exit,
   79:      pending_exit_gc, pending_exit_is_process_alive,
   80:      pending_exit_process_display,
   81:      pending_exit_process_info_1,
   82:      pending_exit_process_info_2, pending_exit_group_leader,
   83:      exit_before_pending_exit].
   84: 
   85: groups() -> 
   86:     [].
   87: 
   88: init_per_group(_GroupName, Config) ->
   89:     Config.
   90: 
   91: end_per_group(_GroupName, Config) ->
   92:     Config.
   93: 
   94: 
   95: xm_sig_order(doc) -> ["Test that exit signals and messages are received "
   96: 		      "in correct order"];
   97: xm_sig_order(suite) -> [];
   98: xm_sig_order(Config) when is_list(Config) ->
   99:     ?line LNode = node(),
  100:     ?line repeat(fun () -> xm_sig_order_test(LNode) end, 1000),
  101:     ?line {ok, RNode} = start_node(Config),
  102:     ?line repeat(fun () -> xm_sig_order_test(RNode) end, 1000),
  103:     ?line stop_node(RNode),
  104:     ?line ok.
  105:     
  106: 
  107: xm_sig_order_test(Node) ->
  108:     ?line P = spawn(Node, fun () -> xm_sig_order_proc() end),
  109:     ?line M = erlang:monitor(process, P),
  110:     ?line P ! may_reach,
  111:     ?line P ! may_reach,
  112:     ?line P ! may_reach,
  113:     ?line exit(P, good_signal_order),
  114:     ?line P ! may_not_reach,
  115:     ?line P ! may_not_reach,
  116:     ?line P ! may_not_reach,
  117:     ?line receive
  118: 	      {'DOWN', M, process, P, R} ->
  119: 		  ?line good_signal_order = R
  120: 	  end.
  121: 
  122: xm_sig_order_proc() ->
  123:     receive
  124: 	may_not_reach -> exit(bad_signal_order);
  125: 	may_reach -> ok
  126:     after 0 -> ok
  127:     end,
  128:     xm_sig_order_proc().
  129: 
  130: pending_exit_unlink_process(doc) -> [];
  131: pending_exit_unlink_process(suite) -> [];
  132: pending_exit_unlink_process(Config) when is_list(Config) ->
  133:     ?line pending_exit_test(self(), unlink).
  134: 
  135: pending_exit_unlink_dist_process(doc) -> [];
  136: pending_exit_unlink_dist_process(suite) -> [];
  137: pending_exit_unlink_dist_process(Config) when is_list(Config) ->
  138:     ?line {ok, Node} = start_node(Config),
  139:     ?line From = spawn(Node, fun () -> receive after infinity -> ok end end),
  140:     ?line Res = pending_exit_test(From, unlink),
  141:     ?line stop_node(Node),
  142:     ?line Res.
  143: 
  144: pending_exit_unlink_port(doc) -> [];
  145: pending_exit_unlink_port(suite) -> [];
  146: pending_exit_unlink_port(Config) when is_list(Config) ->
  147:     ?line pending_exit_test(hd(erlang:ports()), unlink).
  148: 
  149: pending_exit_trap_exit(doc) -> [];
  150: pending_exit_trap_exit(suite) -> [];
  151: pending_exit_trap_exit(Config) when is_list(Config) ->
  152:     ?line pending_exit_test(self(), trap_exit).
  153: 
  154: pending_exit_receive(doc) -> [];
  155: pending_exit_receive(suite) -> [];
  156: pending_exit_receive(Config) when is_list(Config) ->
  157:     ?line pending_exit_test(self(), 'receive').
  158: 
  159: pending_exit_exit(doc) -> [];
  160: pending_exit_exit(suite) -> [];
  161: pending_exit_exit(Config) when is_list(Config) ->
  162:     ?line pending_exit_test(self(), exit).
  163: 
  164: pending_exit_gc(doc) -> [];
  165: pending_exit_gc(suite) -> [];
  166: pending_exit_gc(Config) when is_list(Config) ->
  167:     ?line pending_exit_test(self(), gc).
  168: 
  169: pending_exit_test(From, Type) ->
  170:     ?line case catch erlang:system_info(smp_support) of
  171: 	      true ->
  172: 		  ?line OTE = process_flag(trap_exit, true),
  173: 		  ?line Ref = make_ref(),
  174: 		  ?line Master = self(),
  175: 		  ?line ExitBySignal = case Type of
  176: 					   gc ->
  177: 					       lists:duplicate(10000,
  178: 							       exit_by_signal);
  179: 					   _ ->
  180: 					       exit_by_signal
  181: 				       end,
  182: 		  ?line Pid = spawn_link(
  183: 				fun () ->
  184: 					receive go -> ok end,
  185: 					false = have_pending_exit(),
  186: 					exit = fake_exit(From,
  187: 							 self(),
  188: 							 ExitBySignal),
  189: 					true = have_pending_exit(),
  190: 					Master ! {self(), Ref, Type},
  191: 					case Type of
  192: 					    gc ->
  193: 						force_gc(),
  194: 						erlang:yield();
  195: 					    unlink ->
  196: 						unlink(From);
  197: 					    trap_exit ->
  198: 						process_flag(trap_exit, true);
  199: 					    'receive' ->
  200: 						receive _ -> ok
  201: 						after 0 -> ok
  202: 						end;
  203: 					    exit ->
  204: 						ok
  205: 					end,
  206: 					exit(exit_by_myself)
  207: 				end),
  208: 		  ?line Mon = erlang:monitor(process, Pid),
  209: 		  ?line Pid ! go,
  210: 		  ?line Reason = receive
  211: 				     {'DOWN', Mon, process, Pid, R} ->
  212: 					 ?line receive
  213: 						   {Pid, Ref, Type} ->
  214: 						       ?line ok
  215: 					       after 0 ->
  216: 						       ?line ?t:fail(premature_exit)
  217: 					       end,
  218: 					 ?line case Type of
  219: 						   exit ->
  220: 						       ?line exit_by_myself = R;
  221: 						   _ ->
  222: 						       ?line ExitBySignal = R
  223: 					       end
  224: 				 end,
  225: 		  ?line receive
  226: 			    {'EXIT', Pid, R2} ->
  227: 				?line Reason = R2
  228: 			end,
  229: 		  ?line process_flag(trap_exit, OTE),
  230: 		  ?line ok,
  231: 		  {comment,
  232: 		   "Test only valid with current SMP emulator."};
  233: 	      _ ->
  234: 		  {skipped,
  235: 		   "SMP support not enabled. "
  236: 		   "Test only valid with current SMP emulator."}
  237: 	  end.
  238: 
  239: 
  240: 
  241: exit_before_pending_exit(doc) -> [];
  242: exit_before_pending_exit(suite) -> [];
  243: exit_before_pending_exit(Config) when is_list(Config) ->
  244:     %% This is a testcase testcase very specific to the smp
  245:     %% implementation as it is of the time of writing.
  246:     %%
  247:     %% The testcase tries to check that a process can
  248:     %% exit by itself even though it has a pending exit.
  249:     ?line OTE = process_flag(trap_exit, true),
  250:     ?line Master = self(),
  251:     ?line Tester = spawn_link(
  252: 		     fun () ->
  253: 			     Opts = case {erlang:system_info(run_queues),
  254: 					  erlang:system_info(schedulers_online)} of
  255: 					{RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
  256: 					_ ->
  257: 					    process_flag(scheduler, 1),
  258: 					    [{scheduler, 2}]
  259: 				    end,
  260: 			     P = self(),
  261: 			     Exiter = spawn_opt(fun () ->
  262: 							receive
  263: 							    {exit_me, P, R} ->
  264: 								exit(P, R)
  265: 							end
  266: 						end, Opts),
  267: 			     erlang:yield(),
  268: 			     Exiter ! {exit_me, self(), exited_by_exiter},
  269: 			     %% We want to get a pending exit
  270: 			     %% before we exit ourselves. We
  271: 			     %% don't want to be scheduled out
  272: 			     %% since we will then see the
  273: 			     %% pending exit.
  274: 			     %%
  275: 			     %% Do something that takes
  276: 			     %% relatively long time but
  277: 			     %% consumes few reductions...
  278: 			     repeat(fun() -> erlang:system_info(procs) end,10),
  279: 			     %% ... then exit.
  280: 			     Master ! {self(),
  281: 				       pending_exit,
  282: 				       have_pending_exit()},
  283: 			     exit(exited_by_myself)
  284: 		     end),
  285:     ?line PendingExit = receive {Tester, pending_exit, PE} -> PE end,
  286:     ?line receive
  287: 	      {'EXIT', Tester, exited_by_myself} ->
  288: 		  ?line process_flag(trap_exit, OTE),
  289: 		  ?line ok;
  290: 	      Msg ->
  291: 		  ?line ?t:fail({unexpected_message, Msg})
  292: 	  end,
  293:     NoScheds = integer_to_list(erlang:system_info(schedulers_online)),
  294:     {comment,
  295:      "Was "
  296:      ++ case PendingExit of
  297: 	    true -> "";
  298: 	    false ->"*not*"
  299: 	end ++ " able to trigger a pending exit. "
  300:      ++ "Running on " ++ NoScheds ++ " scheduler(s). "
  301:      ++ "This test is only interesting with at least two schedulers."}.
  302: 
  303: -define(PE_INFO_REPEAT, 100).
  304: 
  305: pending_exit_is_process_alive(Config) when is_list(Config) ->
  306:     ?line S = exit_op_test_init(),
  307:     ?line TestFun = fun (P) -> false = is_process_alive(P) end,
  308:     ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
  309:     ?line verify_pending_exit_success(S),
  310:     ?line comment().
  311: 
  312: pending_exit_process_info_1(Config) when is_list(Config) ->
  313:     ?line S = exit_op_test_init(),
  314:     ?line TestFun = fun (P) ->
  315: 			    undefined = process_info(P)
  316: 		    end,
  317:     ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
  318:     ?line verify_pending_exit_success(S),
  319:     ?line comment().
  320: 
  321: pending_exit_process_info_2(Config) when is_list(Config) ->
  322:     ?line S0 = exit_op_test_init(),
  323:     ?line repeated_exit_op_test(fun (P) ->
  324: 					undefined = process_info(P, messages)
  325: 				end, ?PE_INFO_REPEAT),
  326:     ?line S1 = verify_pending_exit_success(S0),
  327:     ?line repeated_exit_op_test(fun (P) ->
  328: 					undefined = process_info(P, status)
  329: 				end, ?PE_INFO_REPEAT),
  330:     ?line S2 = verify_pending_exit_success(S1),
  331:     ?line repeated_exit_op_test(fun (P) ->
  332: 					undefined = process_info(P, links)
  333: 				end, ?PE_INFO_REPEAT),
  334:     ?line S3 = verify_pending_exit_success(S2),
  335:     ?line repeated_exit_op_test(fun (P) ->
  336: 					undefined = process_info(P, [messages])
  337: 				end, ?PE_INFO_REPEAT),
  338:     ?line S4 = verify_pending_exit_success(S3),
  339:     ?line repeated_exit_op_test(fun (P) ->
  340: 					undefined = process_info(P, [status])
  341: 				end, ?PE_INFO_REPEAT),
  342:     ?line S5 = verify_pending_exit_success(S4),
  343:     ?line repeated_exit_op_test(fun (P) ->
  344: 					undefined = process_info(P, [links])
  345: 				end, ?PE_INFO_REPEAT),
  346:     ?line S6 = verify_pending_exit_success(S5),
  347:     ?line repeated_exit_op_test(fun (P) ->
  348: 					undefined = process_info(P, [status,
  349: 								     links])
  350: 				end, ?PE_INFO_REPEAT),
  351:     ?line S7 = verify_pending_exit_success(S6),
  352:     ?line repeated_exit_op_test(fun (P) ->
  353: 					undefined = process_info(P, [messages,
  354: 								     status])
  355: 				end, ?PE_INFO_REPEAT),
  356:     ?line S8 = verify_pending_exit_success(S7),
  357:     ?line repeated_exit_op_test(fun (P) ->
  358: 					undefined = process_info(P, [messages,
  359: 								     links])
  360: 				end, ?PE_INFO_REPEAT),
  361:     ?line S9 = verify_pending_exit_success(S8),
  362:     ?line repeated_exit_op_test(
  363: 	    fun (P) ->
  364: 		    undefined = process_info(P, [message_queue_len,
  365: 						 status])
  366: 	    end, ?PE_INFO_REPEAT),
  367:     ?line S10 = verify_pending_exit_success(S9),
  368:     ?line repeated_exit_op_test(fun (P) ->
  369: 					undefined = process_info(P, [messages,
  370: 								     links,
  371: 								     status])
  372: 				end, ?PE_INFO_REPEAT),
  373:     ?line verify_pending_exit_success(S10),
  374:     ?line comment().
  375: 
  376: pending_exit_process_display(Config) when is_list(Config) ->
  377:     ?line S = exit_op_test_init(),
  378:     ?line TestFun = fun (P) ->
  379: 			    badarg = try
  380: 					 erlang:process_display(P, backtrace)
  381: 				     catch
  382: 					 error:badarg -> badarg
  383: 				     end
  384: 		    end,
  385:     ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
  386:     ?line verify_pending_exit_success(S),
  387:     ?line comment().
  388: 
  389: pending_exit_group_leader(Config) when is_list(Config) ->
  390:     ?line S = exit_op_test_init(),
  391:     ?line TestFun = fun (P) ->
  392: 			    badarg = try
  393: 					 group_leader(self(), P)
  394: 				     catch
  395: 					 error:badarg -> badarg
  396: 				     end
  397: 		    end,
  398:     ?line repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT),
  399:     ?line verify_pending_exit_success(S),
  400:     ?line comment().
  401: 
  402: %%
  403: %% -- Internal utils --------------------------------------------------------
  404: %%
  405: exit_op_test_init() ->
  406:     put(no_pending_exit_success, 0),
  407:     put(no_pending_exit_tries, 0),
  408:     {case {erlang:system_info(run_queues),
  409: 	   erlang:system_info(schedulers_online)} of
  410: 	 {RQ, SO} when RQ =:= 1; SO =:= 1 -> false;
  411: 	 _ -> true
  412:      end, 0, 0}.
  413: 
  414: verify_pending_exit_success({false, _, _} = S) ->
  415:     S;
  416: verify_pending_exit_success({true, S, T}) ->
  417:     NewS = get(no_pending_exit_success),
  418:     NewT = get(no_pending_exit_tries),
  419:     case NewT =:= T of
  420: 	true -> ok;
  421: 	_ -> case NewS > S of
  422: 		 true -> ok;
  423: 		 _ -> exit(no_pending_exits)
  424: 	     end
  425:     end,
  426:     {true, NewS, NewT}.
  427: 
  428: comment() ->
  429:     {comment,
  430:      "Pending exit trigger ratio "
  431:      ++ integer_to_list(get(no_pending_exit_success))
  432:      ++ "/"
  433:      ++ integer_to_list(get(no_pending_exit_tries))
  434:      ++ "."
  435:      ++ case get(not_running_opt_test) of
  436: 	    true -> " No 'not running optimization' to disable.";
  437: 	    _ -> ""
  438: 	end}.
  439: 
  440: repeated_exit_op_test(TestFun, N) ->
  441:     WorkFun0 = fun () ->
  442: 		       lists:sort(lists:reverse(lists:seq(1, 1000)))
  443: 	       end,
  444:     repeat(fun () -> exit_op_test(TestFun, WorkFun0) end, N),
  445:     try erts_debug:set_internal_state(not_running_optimization, false) of
  446: 	Bool when Bool == true; Bool == false -> 
  447: 	    WorkFun1 = fun () ->
  448: 			       erts_debug:set_internal_state(sleep, 0),
  449: 			       lists:sort(lists:reverse(lists:seq(1, 1000)))
  450: 			    end,
  451: 	    repeat(fun () ->
  452: 			   exit_op_test(TestFun, WorkFun1)
  453: 		   end, N)
  454:     catch
  455: 	error:notsup -> put(not_running_opt_test, true)
  456:     after
  457: 	catch erts_debug:set_internal_state(not_running_optimization, true)
  458:     end.
  459: 
  460: exit_op_test(TestFun, WorkFun) ->
  461:     Opts = case {erlang:system_info(run_queues),
  462: 		 erlang:system_info(schedulers_online)} of
  463: 	       {RQ, SO} when RQ =:= 1; SO =:= 1 -> [];
  464: 	       _ ->
  465: 		   process_flag(scheduler, 1),
  466: 		   [{scheduler, 2}]
  467: 	   end,
  468:     Master = self(),
  469:     Going = make_ref(),
  470:     P = spawn_opt(fun () ->
  471: 			  loop(10, WorkFun),
  472: 			  Master ! Going,
  473: 			  loop(infinity, WorkFun)
  474: 		  end, Opts),
  475:     receive Going -> ok end,
  476:     loop(10, WorkFun),
  477:     erlang:yield(),
  478:     exit(P, bang),
  479:     PE0 = have_pending_exit(P),
  480:     TestFun(P),
  481:     PE = case PE0 of
  482: 	     true -> true;
  483: 	     _ -> false
  484: 	 end,
  485:     case {PE, get(no_pending_exit_success), get(no_pending_exit_tries)} of
  486: 	{true, undefined, undefined} ->
  487: 	    put(no_pending_exit_success, 1),
  488: 	    put(no_pending_exit_tries, 1);
  489: 	{false, undefined, undefined} ->
  490: 	    put(no_pending_exit_success, 0),
  491: 	    put(no_pending_exit_tries, 1);
  492: 	{true, S, T} ->
  493: 	    put(no_pending_exit_success, S+1),
  494: 	    put(no_pending_exit_tries, T+1);
  495: 	{false, _S, T} ->
  496: 	    put(no_pending_exit_tries, T+1)
  497:     end,
  498:     ok.
  499: 
  500: loop(infinity, WorkFun) ->
  501:     do_loop(infinity, WorkFun);
  502: loop(0, _WorkFun) ->
  503:     ok;
  504: loop(N, WorkFun) when is_integer(N) ->
  505:     do_loop(N-1, WorkFun).
  506: 
  507: do_loop(N, WorkFun) ->
  508:     WorkFun(),
  509:     loop(N, WorkFun).
  510: 
  511: repeat(_Fun, N) when is_integer(N), N =< 0 ->
  512:     ok;
  513: repeat(Fun, N) when is_integer(N)  ->
  514:     Fun(),
  515:     repeat(Fun, N-1).
  516: 
  517: start_node(Config) ->
  518:     {A, B, C} = now(),
  519:     Name = list_to_atom(atom_to_list(?MODULE)
  520: 			++ "-" ++ atom_to_list(?config(testcase, Config))
  521: 			++ "-" ++ integer_to_list(A)
  522: 			++ "-" ++ integer_to_list(B)
  523: 			++ "-" ++ integer_to_list(C)),
  524:     Pa = filename:dirname(code:which(?MODULE)),
  525:     ?t:start_node(Name, slave, [{args,  "-pa " ++ Pa}]).
  526: 
  527: stop_node(Node) ->
  528:     ?t:stop_node(Node).
  529: 
  530: have_pending_exit() ->
  531:     have_pending_exit(self()).
  532: 
  533: have_pending_exit(Pid) ->
  534:     erts_debug:get_internal_state({have_pending_exit, Pid}).
  535: 
  536: force_gc() ->
  537:     erts_debug:set_internal_state(force_gc, self()).
  538: 
  539: fake_exit(From, To, Reason) ->
  540:     erts_debug:set_internal_state(send_fake_exit_signal, {From, To, Reason}).
  541: 
  542: available_internal_state(Bool) when Bool == true; Bool == false ->
  543:     case {Bool,
  544: 	  (catch erts_debug:get_internal_state(available_internal_state))} of
  545: 	{true, true} ->
  546: 	    true;
  547: 	{false, true} ->
  548: 	    erts_debug:set_internal_state(available_internal_state, false),
  549: 	    true;
  550: 	{true, _} ->
  551: 	    erts_debug:set_internal_state(available_internal_state, true),
  552: 	    false;
  553: 	{false, _} ->
  554: 	    false
  555:     end.