1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2006-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%%%------------------------------------------------------------------- 22%%% File : signal_SUITE.erl 23%%% Author : Rickard Green <rickard.s.green@ericsson.com> 24%%% Description : Test signals 25%%% 26%%% Created : 10 Jul 2006 by Rickard Green <rickard.s.green@ericsson.com> 27%%%------------------------------------------------------------------- 28-module(signal_SUITE). 29-author('rickard.s.green@ericsson.com'). 30 31%-define(line_trace, 1). 32-include_lib("common_test/include/ct.hrl"). 33-export([all/0, suite/0,init_per_suite/1, end_per_suite/1]). 34-export([init_per_testcase/2, end_per_testcase/2]). 35 36% Test cases 37-export([xm_sig_order/1, 38 kill2killed/1, 39 contended_signal_handling/1, 40 busy_dist_exit_signal/1, 41 busy_dist_demonitor_signal/1, 42 busy_dist_down_signal/1, 43 busy_dist_spawn_reply_signal/1, 44 busy_dist_unlink_ack_signal/1]). 45 46init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 47 [{testcase, Func}|Config]. 48 49end_per_testcase(_Func, _Config) -> 50 ok. 51 52init_per_suite(Config) -> 53 Config. 54 55end_per_suite(_Config) -> 56 ok. 57 58suite() -> 59 [{ct_hooks,[ts_install_cth]}, 60 {timetrap, {minutes, 2}}]. 61 62all() -> 63 [xm_sig_order, 64 kill2killed, 65 contended_signal_handling, 66 busy_dist_exit_signal, 67 busy_dist_demonitor_signal, 68 busy_dist_down_signal, 69 busy_dist_spawn_reply_signal, 70 busy_dist_unlink_ack_signal]. 71 72%% Test that exit signals and messages are received in correct order 73xm_sig_order(Config) when is_list(Config) -> 74 LNode = node(), 75 repeat(fun () -> xm_sig_order_test(LNode) end, 1000), 76 {ok, RNode} = start_node(Config), 77 repeat(fun () -> xm_sig_order_test(RNode) end, 1000), 78 stop_node(RNode), 79 ok. 80 81 82xm_sig_order_test(Node) -> 83 P = spawn(Node, fun () -> xm_sig_order_proc() end), 84 M = erlang:monitor(process, P), 85 P ! may_reach, 86 P ! may_reach, 87 P ! may_reach, 88 exit(P, good_signal_order), 89 P ! may_not_reach, 90 P ! may_not_reach, 91 P ! may_not_reach, 92 receive 93 {'DOWN', M, process, P, R} -> 94 good_signal_order = R 95 end. 96 97xm_sig_order_proc() -> 98 receive 99 may_not_reach -> exit(bad_signal_order); 100 may_reach -> ok 101 after 0 -> erlang:yield() 102 end, 103 xm_sig_order_proc(). 104 105kill2killed(Config) when is_list(Config) -> 106 process_flag(trap_exit, true), 107 kill2killed_test(node()), 108 {ok, Node} = start_node(Config), 109 kill2killed_test(Node), 110 stop_node(Node), 111 ok. 112 113kill2killed_test(Node) -> 114 if Node == node() -> 115 io:format("Testing against local node", []); 116 true -> 117 io:format("Testing against remote node ~p", [Node]) 118 end, 119 check_exit(Node, other_exit2, 1), 120 check_exit(Node, other_exit2, 2), 121 check_exit(Node, other_exit2, 9), 122 check_exit(Node, other_exit2, 10), 123 check_exit(Node, exit2, 1), 124 check_exit(Node, exit2, 2), 125 check_exit(Node, exit2, 9), 126 check_exit(Node, exit2, 10), 127 check_exit(Node, exit1, 1), 128 check_exit(Node, exit1, 2), 129 check_exit(Node, exit1, 9), 130 check_exit(Node, exit1, 10), 131 ok. 132 133check_exit(Node, Type, N) -> 134 io:format("Testing ~p length ~p~n", [Type, N]), 135 P = spawn_link_line(Node, node(), Type, N, self()), 136 if Type == other_exit2 -> 137 receive 138 {end_of_line, EOL} -> 139 exit(EOL, kill) 140 end; 141 true -> ok 142 end, 143 receive 144 {'EXIT', P, Reason} -> 145 if Type == exit1 -> 146 kill = Reason; 147 true -> 148 killed = Reason 149 end 150 end. 151 152spawn_link_line(_NodeA, _NodeB, other_exit2, 0, Tester) -> 153 Tester ! {end_of_line, self()}, 154 receive after infinity -> ok end; 155spawn_link_line(_NodeA, _NodeB, exit1, 0, _Tester) -> 156 exit(kill); 157spawn_link_line(_NodeA, _NodeB, exit2, 0, _Tester) -> 158 exit(self(), kill); 159spawn_link_line(NodeA, NodeB, Type, N, Tester) -> 160 spawn_link(NodeA, 161 fun () -> 162 spawn_link_line(NodeB, NodeA, Type, N-1, Tester), 163 receive after infinity -> ok end 164 end). 165 166contended_signal_handling(Config) when is_list(Config) -> 167 %% 168 %% Test for a race in signal handling of a process. 169 %% 170 %% When executing dirty, a "dirty signal handler" 171 %% process will handle signals for the process. If 172 %% the process stops executing dirty while the dirty 173 %% signal handler process is handling signals on 174 %% behalf of the process, both the dirty signal handler 175 %% process and the process itself might try to handle 176 %% signals for the process at the same time. There used 177 %% to be a bug that caused both processes to enter the 178 %% signal handling code simultaneously when the main 179 %% lock of the process was temporarily released during 180 %% signal handling (see GH-4885/OTP-17462/PR-4914). 181 %% Currently the main lock is only released when the 182 %% process receives an 'unlock' signal from a port, 183 %% and then responds by sending an 'unlock-ack' signal 184 %% to the port. This testcase tries to massage that 185 %% scenario. It is quite hard to cause a crash even 186 %% when the bug exists, but this testcase at least 187 %% sometimes causes a crash when the bug is present. 188 %% 189 process_flag(priority, high), 190 Drv = unlink_signal_drv, 191 ok = load_driver(Config, Drv), 192 try 193 contended_signal_handling_test(Drv, 250) 194 after 195 ok = erl_ddll:unload_driver(Drv) 196 end, 197 ok. 198 199contended_signal_handling_test(_Drv, 0) -> 200 ok; 201contended_signal_handling_test(Drv, N) -> 202 Ports = contended_signal_handling_make_ports(Drv, 100, []), 203 erlang:yield(), 204 contended_signal_handling_cmd_ports(Ports), 205 erts_debug:dirty_cpu(wait, rand:uniform(5)), 206 wait_until(fun () -> Ports == Ports -- erlang:ports() end), 207 contended_signal_handling_test(Drv, N-1). 208 209contended_signal_handling_cmd_ports([]) -> 210 ok; 211contended_signal_handling_cmd_ports([P|Ps]) -> 212 P ! {self(), {command, ""}}, 213 contended_signal_handling_cmd_ports(Ps). 214 215contended_signal_handling_make_ports(_Drv, 0, Ports) -> 216 Ports; 217contended_signal_handling_make_ports(Drv, N, Ports) -> 218 Port = open_port({spawn, Drv}, []), 219 true = is_port(Port), 220 contended_signal_handling_make_ports(Drv, N-1, [Port|Ports]). 221 222busy_dist_exit_signal(Config) when is_list(Config) -> 223 BusyTime = 1000, 224 {ok, BusyChannelNode} = start_node(Config), 225 {ok, OtherNode} = start_node(Config, "-proto_dist gen_tcp"), 226 Tester = self(), 227 Exiter = spawn(BusyChannelNode, 228 fun () -> 229 pong = net_adm:ping(OtherNode), 230 Tester ! {self(), alive}, 231 receive after infinity -> ok end 232 end), 233 receive {Exiter, alive} -> ok end, 234 Linker = spawn_link(OtherNode, 235 fun () -> 236 process_flag(trap_exit, true), 237 link(Exiter), 238 receive 239 {'EXIT', Exiter, Reason} -> 240 tester_killed_me = Reason, 241 Tester ! {self(), got_exiter_exit_message}; 242 Unexpected -> 243 exit({unexpected_message, Unexpected}) 244 end 245 end), 246 make_busy(BusyChannelNode, OtherNode, 1000), 247 exit(Exiter, tester_killed_me), 248 receive 249 {Linker, got_exiter_exit_message} -> 250 unlink(Linker), 251 ok 252 after 253 BusyTime*2 -> 254 ct:fail(missing_exit_signal) 255 end, 256 stop_node(BusyChannelNode), 257 stop_node(OtherNode), 258 ok. 259 260busy_dist_demonitor_signal(Config) when is_list(Config) -> 261 BusyTime = 1000, 262 {ok, BusyChannelNode} = start_node(Config), 263 {ok, OtherNode} = start_node(Config, "-proto_dist gen_tcp"), 264 Tester = self(), 265 Demonitorer = spawn(BusyChannelNode, 266 fun () -> 267 pong = net_adm:ping(OtherNode), 268 Tester ! {self(), alive}, 269 receive 270 {Tester, monitor, Pid} -> 271 _Mon = erlang:monitor(process, Pid) 272 end, 273 receive after infinity -> ok end 274 end), 275 receive {Demonitorer, alive} -> ok end, 276 Demonitoree = spawn_link(OtherNode, 277 fun () -> 278 wait_until(fun () -> 279 {monitored_by, MB1} 280 = process_info(self(), 281 monitored_by), 282 lists:member(Demonitorer, MB1) 283 end), 284 Tester ! {self(), monitored}, 285 wait_until(fun () -> 286 {monitored_by, MB2} 287 = process_info(self(), 288 monitored_by), 289 not lists:member(Demonitorer, MB2) 290 end), 291 Tester ! {self(), got_demonitorer_demonitor_signal} 292 end), 293 Demonitorer ! {self(), monitor, Demonitoree}, 294 receive {Demonitoree, monitored} -> ok end, 295 make_busy(BusyChannelNode, OtherNode, 1000), 296 exit(Demonitorer, tester_killed_me), 297 receive 298 {Demonitoree, got_demonitorer_demonitor_signal} -> 299 unlink(Demonitoree), 300 ok 301 after 302 BusyTime*2 -> 303 ct:fail(missing_demonitor_signal) 304 end, 305 stop_node(BusyChannelNode), 306 stop_node(OtherNode), 307 ok. 308 309busy_dist_down_signal(Config) when is_list(Config) -> 310 BusyTime = 1000, 311 {ok, BusyChannelNode} = start_node(Config), 312 {ok, OtherNode} = start_node(Config, "-proto_dist gen_tcp"), 313 Tester = self(), 314 Exiter = spawn(BusyChannelNode, 315 fun () -> 316 pong = net_adm:ping(OtherNode), 317 Tester ! {self(), alive}, 318 receive after infinity -> ok end 319 end), 320 receive {Exiter, alive} -> ok end, 321 Monitorer = spawn_link(OtherNode, 322 fun () -> 323 process_flag(trap_exit, true), 324 Mon = erlang:monitor(process, Exiter), 325 receive 326 {'DOWN', Mon, process, Exiter, Reason} -> 327 tester_killed_me = Reason, 328 Tester ! {self(), got_exiter_down_message}; 329 Unexpected -> 330 exit({unexpected_message, Unexpected}) 331 end 332 end), 333 make_busy(BusyChannelNode, OtherNode, 1000), 334 exit(Exiter, tester_killed_me), 335 receive 336 {Monitorer, got_exiter_down_message} -> 337 unlink(Monitorer), 338 ok 339 after 340 BusyTime*2 -> 341 ct:fail(missing_down_signal) 342 end, 343 stop_node(BusyChannelNode), 344 stop_node(OtherNode), 345 ok. 346 347busy_dist_spawn_reply_signal(Config) when is_list(Config) -> 348 BusyTime = 1000, 349 {ok, BusyChannelNode} = start_node(Config), 350 {ok, OtherNode} = start_node(Config, "-proto_dist gen_tcp"), 351 Tester = self(), 352 Spawner = spawn_link(OtherNode, 353 fun () -> 354 pong = net_adm:ping(BusyChannelNode), 355 Tester ! {self(), ready}, 356 receive {Tester, go} -> ok end, 357 ReqID = spawn_request(BusyChannelNode, 358 fun () -> ok end, 359 []), 360 receive 361 {spawn_reply, ReqID, Result, _Pid} -> 362 ok = Result, 363 Tester ! {self(), got_spawn_reply_message}; 364 Unexpected -> 365 exit({unexpected_message, Unexpected}) 366 end 367 end), 368 receive {Spawner, ready} -> ok end, 369 make_busy(BusyChannelNode, OtherNode, 1000), 370 Spawner ! {self(), go}, 371 receive 372 {Spawner, got_spawn_reply_message} -> 373 unlink(Spawner), 374 ok 375 after 376 BusyTime*2 -> 377 ct:fail(missing_spawn_reply_signal) 378 end, 379 stop_node(BusyChannelNode), 380 stop_node(OtherNode), 381 ok. 382 383-record(erl_link, {type, % process | port | dist_process 384 pid = [], 385 state, % linked | unlinking 386 id}). 387 388busy_dist_unlink_ack_signal(Config) when is_list(Config) -> 389 BusyTime = 1000, 390 {ok, BusyChannelNode} = start_node(Config), 391 {ok, OtherNode} = start_node(Config, "-proto_dist gen_tcp"), 392 Tester = self(), 393 Unlinkee = spawn(BusyChannelNode, 394 fun () -> 395 pong = net_adm:ping(OtherNode), 396 Tester ! {self(), alive}, 397 receive after infinity -> ok end 398 end), 399 receive {Unlinkee, alive} -> ok end, 400 Unlinker = spawn_link(OtherNode, 401 fun () -> 402 erts_debug:set_internal_state(available_internal_state, true), 403 link(Unlinkee), 404 #erl_link{type = dist_process, 405 pid = Unlinkee, 406 state = linked} = find_proc_link(self(), 407 Unlinkee), 408 Tester ! {self(), ready}, 409 receive {Tester, go} -> ok end, 410 unlink(Unlinkee), 411 #erl_link{type = dist_process, 412 pid = Unlinkee, 413 state = unlinking} = find_proc_link(self(), 414 Unlinkee), 415 wait_until(fun () -> 416 false == find_proc_link(self(), 417 Unlinkee) 418 end), 419 Tester ! {self(), got_unlink_ack_signal} 420 end), 421 receive {Unlinker, ready} -> ok end, 422 make_busy(BusyChannelNode, OtherNode, 1000), 423 Unlinker ! {self(), go}, 424 receive 425 {Unlinker, got_unlink_ack_signal} -> 426 unlink(Unlinker), 427 ok 428 after 429 BusyTime*2 -> 430 ct:fail(missing_unlink_ack_signal) 431 end, 432 stop_node(BusyChannelNode), 433 stop_node(OtherNode), 434 ok. 435 436%% 437%% -- Internal utils -------------------------------------------------------- 438%% 439 440load_driver(Config, Driver) -> 441 DataDir = proplists:get_value(data_dir, Config), 442 case erl_ddll:load_driver(DataDir, Driver) of 443 ok -> 444 ok; 445 {error, Error} = Res -> 446 io:format("~s\n", [erl_ddll:format_error(Error)]), 447 Res 448 end. 449 450wait_until(Fun) -> 451 case (catch Fun()) of 452 true -> 453 ok; 454 _ -> 455 receive after 1 -> ok end, 456 wait_until(Fun) 457 end. 458 459find_proc_link(Pid, To) when is_pid(Pid), is_pid(To) -> 460 lists:keyfind(To, 461 #erl_link.pid, 462 erts_debug:get_internal_state({link_list, Pid})). 463 464make_busy(OnNode, ToNode, Time) -> 465 Parent = self(), 466 Fun = fun () -> 467 Proxy = self(), 468 Sspndr = spawn_link( 469 ToNode, 470 fun () -> 471 IC = find_gen_tcp_input_cntrlr(OnNode), 472 erlang:suspend_process(IC), 473 Proxy ! {self(), input_cntrlr_suspended}, 474 receive 475 {Proxy, resume_input_cntrlr} -> 476 erlang:resume_process(IC) 477 end, 478 Proxy ! {self(), input_cntrlr_resumed} 479 end), 480 receive 481 {Sspndr, input_cntrlr_suspended} -> 482 ok 483 end, 484 Spammer = spawn_link( 485 OnNode, 486 fun () -> 487 spammed = spam(ToNode), 488 Proxy ! {self(), channel_busy}, 489 receive 490 after Time -> ok 491 end, 492 Proxy ! {self(), timeout} 493 end), 494 receive 495 {Spammer, channel_busy} -> 496 Parent ! {self(), channel_busy} 497 end, 498 receive 499 {Spammer, timeout} -> 500 Sspndr ! {self(), resume_input_cntrlr} 501 end, 502 receive 503 {Sspndr, input_cntrlr_resumed} -> 504 ok 505 end 506 end, 507 Proxy = spawn_link(Fun), 508 receive 509 {Proxy, channel_busy} -> 510 ok 511 end, 512 Proxy. 513 514find_gen_tcp_input_cntrlr(Node) when is_atom(Node) -> 515 case lists:keyfind(Node, 1, erlang:system_info(dist_ctrl)) of 516 {Node, DistCtrl} -> 517 find_gen_tcp_input_cntrlr(DistCtrl); 518 false -> 519 undefined 520 end; 521find_gen_tcp_input_cntrlr(DistCtrl) when is_pid(DistCtrl) -> 522 {links, LList} = process_info(DistCtrl, links), 523 try 524 lists:foreach(fun (Pid) -> 525 case process_info(Pid, initial_call) of 526 {initial_call, 527 {gen_tcp_dist,dist_cntrlr_input_setup,3}} -> 528 throw({input_ctrlr, Pid}); 529 _ -> 530 ok 531 end 532 end, 533 LList), 534 undefined 535 catch 536 throw:{input_ctrlr, DistInputCtrlr} -> 537 DistInputCtrlr 538 end. 539 540spam(Node) -> 541 To = {'__a_name_hopefully_not_registered__', Node}, 542 Data = lists:seq(1, 100), 543 spam(To, Data). 544 545spam(To, Data) -> 546 case erlang:send(To, Data, [nosuspend]) of 547 nosuspend -> 548 spammed; 549 _ -> 550 spam(To, Data) 551 end. 552 553repeat(_Fun, N) when is_integer(N), N =< 0 -> 554 ok; 555repeat(Fun, N) when is_integer(N) -> 556 Fun(), 557 repeat(Fun, N-1). 558 559start_node(Config, Args) -> 560 Name = list_to_atom(atom_to_list(?MODULE) 561 ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) 562 ++ "-" ++ integer_to_list(erlang:system_time(second)) 563 ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), 564 Pa = filename:dirname(code:which(?MODULE)), 565 test_server:start_node(Name, slave, [{args, "-pa " ++ Pa ++ " " ++ Args}]). 566 567start_node(Config) -> 568 start_node(Config, ""). 569 570stop_node(Node) -> 571 test_server:stop_node(Node). 572