1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-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-module(process_SUITE). 22 23%% Tests processes, trapping exit messages and the BIFs: 24%% exit/1 25%% exit/2 26%% process_info/1,2 27%% register/2 (partially) 28 29-include_lib("common_test/include/ct.hrl"). 30 31-define(heap_binary_size, 64). 32 33-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 34 init_per_group/2,end_per_group/2, spawn_with_binaries/1, 35 t_exit_1/1, t_exit_2_other/1, t_exit_2_other_normal/1, 36 self_exit/1, normal_suicide_exit/1, abnormal_suicide_exit/1, 37 t_exit_2_catch/1, trap_exit_badarg/1, trap_exit_badarg_in_bif/1, 38 exit_and_timeout/1, exit_twice/1, 39 t_process_info/1, process_info_other/1, process_info_other_msg/1, 40 process_info_other_dist_msg/1, 41 process_info_other_status/1, 42 process_info_2_list/1, process_info_lock_reschedule/1, 43 process_info_lock_reschedule2/1, 44 process_info_lock_reschedule3/1, 45 process_info_garbage_collection/1, 46 process_info_smoke_all/1, 47 process_info_status_handled_signal/1, 48 process_info_reductions/1, 49 bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1, 50 otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1, 51 process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1, 52 spawn_opt_heap_size/1, spawn_opt_max_heap_size/1, 53 processes_large_tab/1, processes_default_tab/1, processes_small_tab/1, 54 processes_this_tab/1, processes_apply_trap/1, 55 processes_last_call_trap/1, processes_gc_trap/1, 56 processes_term_proc_list/1, 57 otp_7738_waiting/1, otp_7738_suspended/1, 58 otp_7738_resume/1, 59 garb_other_running/1, 60 no_priority_inversion/1, 61 no_priority_inversion2/1, 62 system_task_blast/1, 63 system_task_on_suspended/1, 64 system_task_failed_enqueue/1, 65 gc_request_when_gc_disabled/1, 66 gc_request_blast_when_gc_disabled/1, 67 otp_16436/1, 68 otp_16642/1, 69 spawn_huge_arglist/1, 70 spawn_request_bif/1, 71 spawn_request_monitor_demonitor/1, 72 spawn_request_monitor_child_exit/1, 73 spawn_request_link_child_exit/1, 74 spawn_request_link_parent_exit/1, 75 spawn_request_abandon_bif/1, 76 dist_spawn_monitor/1, 77 spawn_old_node/1, 78 spawn_new_node/1, 79 spawn_request_reply_option/1]). 80-export([prio_server/2, prio_client/2, init/1, handle_event/2]). 81 82-export([init_per_testcase/2, end_per_testcase/2]). 83 84-export([hangaround/2, processes_bif_test/0, do_processes/1, 85 processes_term_proc_list_test/1, huge_arglist_child/255]). 86 87suite() -> 88 [{ct_hooks,[ts_install_cth]}, 89 {timetrap, {minutes, 9}}]. 90 91all() -> 92 [spawn_with_binaries, t_exit_1, {group, t_exit_2}, 93 trap_exit_badarg, trap_exit_badarg_in_bif, 94 t_process_info, process_info_other, process_info_other_msg, 95 process_info_other_dist_msg, process_info_other_status, 96 process_info_2_list, 97 process_info_lock_reschedule, 98 process_info_lock_reschedule2, 99 process_info_lock_reschedule3, 100 process_info_garbage_collection, 101 process_info_smoke_all, 102 process_info_status_handled_signal, 103 process_info_reductions, 104 bump_reductions, low_prio, yield, yield2, otp_4725, 105 bad_register, garbage_collect, process_info_messages, 106 process_flag_badarg, process_flag_heap_size, 107 spawn_opt_heap_size, spawn_opt_max_heap_size, 108 spawn_huge_arglist, 109 spawn_request_bif, 110 spawn_request_monitor_demonitor, 111 spawn_request_monitor_child_exit, 112 spawn_request_link_child_exit, 113 spawn_request_link_parent_exit, 114 spawn_request_abandon_bif, 115 dist_spawn_monitor, 116 spawn_old_node, 117 spawn_new_node, 118 spawn_request_reply_option, 119 otp_6237, 120 {group, processes_bif}, 121 {group, otp_7738}, garb_other_running, 122 {group, system_task}]. 123 124groups() -> 125 [{t_exit_2, [], 126 [t_exit_2_other, t_exit_2_other_normal, self_exit, 127 normal_suicide_exit, abnormal_suicide_exit, 128 t_exit_2_catch, exit_and_timeout, exit_twice]}, 129 {processes_bif, [], 130 [processes_large_tab, processes_default_tab, 131 processes_small_tab, processes_this_tab, 132 processes_last_call_trap, processes_apply_trap, 133 processes_gc_trap, processes_term_proc_list]}, 134 {otp_7738, [], 135 [otp_7738_waiting, otp_7738_suspended, 136 otp_7738_resume]}, 137 {system_task, [], 138 [no_priority_inversion, no_priority_inversion2, 139 system_task_blast, system_task_on_suspended, system_task_failed_enqueue, 140 gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled, 141 otp_16436, otp_16642]}]. 142 143init_per_suite(Config) -> 144 A0 = case application:start(sasl) of 145 ok -> [sasl]; 146 _ -> [] 147 end, 148 A = case application:start(os_mon) of 149 ok -> [os_mon|A0]; 150 _ -> A0 151 end, 152 [{started_apps, A}|Config]. 153 154end_per_suite(Config) -> 155 As = proplists:get_value(started_apps, Config), 156 lists:foreach(fun (A) -> application:stop(A) end, As), 157 catch erts_debug:set_internal_state(available_internal_state, false), 158 Config. 159 160init_per_group(_GroupName, Config) -> 161 Config. 162 163end_per_group(_GroupName, Config) -> 164 Config. 165 166init_per_testcase(Func, Config) 167 when Func =:= processes_default_tab; 168 Func =:= processes_this_tab -> 169 case erlang:system_info(debug_compiled) of 170 true -> 171 {skip, "Don't run in debug"}; 172 false -> 173 [{testcase, Func} | Config] 174 end; 175init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 176 [{testcase, Func}|Config]. 177 178end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 179 %% Restore max_heap_size to default value. 180 erlang:system_flag(max_heap_size, 181 #{size => 0, 182 kill => true, 183 error_logger => true}), 184 ok. 185 186fun_spawn(Fun) -> 187 spawn_link(erlang, apply, [Fun, []]). 188 189%% Tests that binaries as arguments to spawn/3 doesn't leak 190%% (unclear if this test case will actually prove anything on 191%% a modern computer with lots of memory). 192spawn_with_binaries(Config) when is_list(Config) -> 193 L = lists:duplicate(2048, 42), 194 TwoMeg = lists:duplicate(1024, L), 195 Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]), 196 receive after 1 -> ok end end, 197 Iter = case test_server:is_valgrind() of 198 true -> 10; 199 false -> 150 200 end, 201 test_server:do_times(Iter, Fun), 202 ok. 203 204binary_owner(Bin) when is_binary(Bin) -> 205 ok. 206 207%% Tests exit/1 with a big message. 208t_exit_1(Config) when is_list(Config) -> 209 ct:timetrap({seconds, 20}), 210 start_spawner(), 211 process_flag(trap_exit, true), 212 test_server:do_times(10, fun t_exit_1/0), 213 stop_spawner(), 214 ok. 215 216t_exit_1() -> 217 Pid = fun_spawn(fun() -> exit(kb_128()) end), 218 Garbage = kb_128(), 219 receive 220 {'EXIT', Pid, Garbage} -> ok 221 end. 222 223 224%% Tests exit/2 with a lot of data in the exit message. 225t_exit_2_other(Config) when is_list(Config) -> 226 ct:timetrap({seconds, 20}), 227 start_spawner(), 228 process_flag(trap_exit, true), 229 test_server:do_times(10, fun t_exit_2_other/0), 230 stop_spawner(), 231 ok. 232 233t_exit_2_other() -> 234 Pid = fun_spawn(fun() -> receive x -> ok end end), 235 Garbage = kb_128(), 236 exit(Pid, Garbage), 237 receive 238 {'EXIT', Pid, Garbage} -> ok 239 end. 240 241%% Tests that exit(Pid, normal) does not kill another process.; 242t_exit_2_other_normal(Config) when is_list(Config) -> 243 ct:timetrap({seconds, 20}), 244 process_flag(trap_exit, true), 245 Pid = fun_spawn(fun() -> receive x -> ok end end), 246 exit(Pid, normal), 247 receive 248 {'EXIT', Pid, Reason} -> 249 ct:fail({process_died, Reason}) 250 after 1000 -> 251 ok 252 end, 253 case process_info(Pid) of 254 undefined -> 255 ct:fail(process_died_on_normal); 256 List when is_list(List) -> 257 ok 258 end, 259 exit(Pid, kill), 260 ok. 261 262%% Tests that we can trap an exit message sent with exit/2 from 263%% the same process. 264self_exit(Config) when is_list(Config) -> 265 ct:timetrap({seconds, 10}), 266 start_spawner(), 267 process_flag(trap_exit, true), 268 test_server:do_times(200, fun self_exit/0), 269 stop_spawner(), 270 ok. 271 272self_exit() -> 273 Garbage = eight_kb(), 274 P = self(), 275 true = exit(P, Garbage), 276 receive 277 {'EXIT', P, Garbage} -> ok 278 end. 279 280%% Tests exit(self(), normal) is equivalent to exit(normal) for a process 281%% that doesn't trap exits. 282normal_suicide_exit(Config) when is_list(Config) -> 283 process_flag(trap_exit, true), 284 Pid = fun_spawn(fun() -> exit(self(), normal) end), 285 receive 286 {'EXIT', Pid, normal} -> ok; 287 Other -> ct:fail({bad_message, Other}) 288 end. 289 290%% Tests exit(self(), Term) is equivalent to exit(Term) for a process 291%% that doesn't trap exits."; 292abnormal_suicide_exit(Config) when is_list(Config) -> 293 Garbage = eight_kb(), 294 process_flag(trap_exit, true), 295 Pid = fun_spawn(fun() -> exit(self(), Garbage) end), 296 receive 297 {'EXIT', Pid, Garbage} -> ok; 298 Other -> ct:fail({bad_message, Other}) 299 end. 300 301%% Tests that exit(self(), die) cannot be catched. 302t_exit_2_catch(Config) when is_list(Config) -> 303 process_flag(trap_exit, true), 304 Pid = fun_spawn(fun() -> catch exit(self(), die) end), 305 receive 306 {'EXIT', Pid, normal} -> 307 ct:fail(catch_worked); 308 {'EXIT', Pid, die} -> 309 ok; 310 Other -> 311 ct:fail({bad_message, Other}) 312 end. 313 314%% Tests trapping of an 'EXIT' message generated by a bad argument to 315%% the abs/1 bif. The 'EXIT' message will intentionally be very big. 316trap_exit_badarg(Config) when is_list(Config) -> 317 ct:timetrap({seconds, 10}), 318 start_spawner(), 319 process_flag(trap_exit, true), 320 test_server:do_times(10, fun trap_exit_badarg/0), 321 stop_spawner(), 322 ok. 323 324trap_exit_badarg() -> 325 Pid = fun_spawn(fun() -> bad_guy(kb_128()) end), 326 Garbage = kb_128(), 327 receive 328 {'EXIT',Pid,{badarg,[{erlang,abs,[Garbage],Loc1}, 329 {?MODULE,bad_guy,1,Loc2}|_]}} 330 when is_list(Loc1), is_list(Loc2) -> 331 ok; 332 Other -> 333 ok = io:format("Bad EXIT message: ~P", [Other, 30]), 334 ct:fail(bad_exit_message) 335 end. 336 337bad_guy(Arg) -> 338 abs(Arg). 339 340 341kb_128() -> 342 Eight = eight_kb(), 343 {big_binary(), 344 Eight, Eight, Eight, Eight, Eight, Eight, Eight, Eight, 345 big_binary(), 346 Eight, Eight, Eight, Eight, Eight, Eight, Eight, Eight, 347 big_binary()}. 348 349eight_kb() -> 350 B64 = lists:seq(1, 64), 351 B512 = {<<1>>,B64,<<2,3>>,B64,make_unaligned_sub_binary(<<4,5,6,7,8,9>>), 352 B64,make_sub_binary([1,2,3,4,5,6]), 353 B64,make_sub_binary(lists:seq(1, ?heap_binary_size+1)), 354 B64,B64,B64,B64,big_binary()}, 355 lists:duplicate(8, {B512,B512}). 356 357big_binary() -> 358 big_binary(10, [42]). 359big_binary(0, Acc) -> 360 list_to_binary(Acc); 361big_binary(N, Acc) -> 362 big_binary(N-1, [Acc|Acc]). 363 364%% Test receiving an EXIT message when spawning a BIF with bad arguments. 365trap_exit_badarg_in_bif(Config) when is_list(Config) -> 366 ct:timetrap({seconds, 10}), 367 process_flag(trap_exit, true), 368 test_server:do_times(10, fun trap_exit_badarg_bif/0), 369 ok. 370 371trap_exit_badarg_bif() -> 372 Pid = spawn_link(erlang, node, [1]), 373 receive 374 {'EXIT', Pid, {badarg, _}} -> 375 ok; 376 Other -> 377 ct:fail({unexpected, Other}) 378 end. 379 380%% The following sequences of events have crasched Beam. 381%% 382%% 1) An exit is sent to a process which is currently not running. 383%% The exit reason will (on purpose) overwrite the message queue 384%% pointer. 385%% 2) Before the process is scheduled in, it receives a timeout (from 386%% a 'receive after'). 387%% 3) The process will crash the next time it executes 'receive'. 388 389exit_and_timeout(Config) when is_list(Config) -> 390 ct:timetrap({seconds, 20}), 391 392 process_flag(trap_exit, true), 393 Parent = self(), 394 Low = fun_spawn(fun() -> eat_low(Parent) end), 395 High = fun_spawn(fun() -> eat_high(Low) end), 396 eat_wait_for(Low, High), 397 ok. 398 399 400eat_wait_for(Low, High) -> 401 receive 402 {'EXIT', Low, {you, are, dead}} -> 403 ok; 404 {'EXIT', High, normal} -> 405 eat_wait_for(Low, High); 406 Other -> 407 ct:fail({bad_message, Other}) 408 end. 409 410eat_low(_Parent) -> 411 receive 412 after 2500 -> 413 ok 414 end, 415 receive 416 Any -> 417 io:format("Received: ~p\n", [Any]) 418 after 1000 -> 419 ok 420 end. 421 422eat_high(Low) -> 423 process_flag(priority, high), 424 receive after 1000 -> ok end, 425 exit(Low, {you, are, dead}), 426 loop(erlang:monotonic_time() + erlang:convert_time_unit(5,second,native)). 427 428%% Busy loop for 5 seconds. 429 430loop(StopTime) -> 431 case StopTime >= erlang:monotonic_time() of 432 true -> ok; 433 false -> loop(StopTime) 434 end. 435 436 437%% Tries to send two different exit messages to a process. 438%% (The second one should be ignored.) 439exit_twice(Config) when is_list(Config) -> 440 ct:timetrap({seconds, 20}), 441 442 process_flag(trap_exit, true), 443 Low = fun_spawn(fun etwice_low/0), 444 High = fun_spawn(fun() -> etwice_high(Low) end), 445 etwice_wait_for(Low, High), 446 ok. 447 448etwice_wait_for(Low, High) -> 449 receive 450 {'EXIT', Low, first} -> 451 ok; 452 {'EXIT', Low, Other} -> 453 ct:fail({wrong_exit_reason, Other}); 454 {'EXIT', High, normal} -> 455 etwice_wait_for(Low, High); 456 Other -> 457 ct:fail({bad_message, Other}) 458 end. 459 460etwice_low() -> 461 etwice_low(). 462 463etwice_high(Low) -> 464 process_flag(priority, high), 465 exit(Low, first), 466 exit(Low, second). 467 468%% Tests the process_info/2 BIF. 469t_process_info(Config) when is_list(Config) -> 470 [] = process_info(self(), registered_name), 471 register(my_name, self()), 472 {registered_name, my_name} = process_info(self(), registered_name), 473 {status, running} = process_info(self(), status), 474 {min_heap_size, 233} = process_info(self(), min_heap_size), 475 {min_bin_vheap_size,46422} = process_info(self(), min_bin_vheap_size), 476 {max_heap_size, #{ size := 0, kill := true, error_logger := true}} = 477 process_info(self(), max_heap_size), 478 {current_function,{?MODULE,t_process_info,1}} = 479 process_info(self(), current_function), 480 {current_function,{?MODULE,t_process_info,1}} = 481 apply(erlang, process_info, [self(),current_function]), 482 483 %% current_location and current_stacktrace 484 {Line1,Res1} = {?LINE,process_info(self(), current_location)}, 485 verify_loc(Line1, Res1), 486 {Line2,Res2} = {?LINE,apply(erlang, process_info, 487 [self(),current_location])}, 488 verify_loc(Line2, Res2), 489 pi_stacktrace([{?MODULE,t_process_info,1,?LINE}]), 490 491 verify_stacktrace_depth(), 492 493 Gleader = group_leader(), 494 {group_leader, Gleader} = process_info(self(), group_leader), 495 {'EXIT',{badarg,_Info}} = (catch process_info('not_a_pid')), 496 ok. 497 498verify_stacktrace_depth() -> 499 CS = current_stacktrace, 500 OldDepth = erlang:system_flag(backtrace_depth, 0), 501 {CS,[]} = erlang:process_info(self(), CS), 502 _ = erlang:system_flag(backtrace_depth, 8), 503 {CS,[{?MODULE,verify_stacktrace_depth,0,_},_|_]} = 504 erlang:process_info(self(), CS), 505 _ = erlang:system_flag(backtrace_depth, OldDepth). 506 507pi_stacktrace(Expected0) -> 508 {Line,Res} = {?LINE,erlang:process_info(self(), current_stacktrace)}, 509 {current_stacktrace,Stack} = Res, 510 Expected = [{?MODULE,pi_stacktrace,1,Line}|Expected0], 511 pi_stacktrace_1(Stack, Expected). 512 513pi_stacktrace_1([{M,F,A,Loc}|Stk], [{M,F,A,Line}|Exp]) -> 514 case Loc of 515 [] -> 516 %% No location info for some reason (+L, native code). 517 io:format("Missing location information for ~w:~w/~w", 518 [M,F,A]), 519 ok; 520 [_|_] -> 521 Line = proplists:get_value(line, Loc), 522 File = proplists:get_value(file, Loc), 523 File = ?MODULE_STRING ++ ".erl" 524 end, 525 pi_stacktrace_1(Stk, Exp); 526pi_stacktrace_1([_|_], []) -> ok. 527 528verify_loc(Line, {current_location,{?MODULE,t_process_info=F,1=A,Loc}}) -> 529 case Loc of 530 [] -> 531 %% No location info for some reason (+L, native code). 532 io:format("Missing location information for ~w:~w/~w", 533 [?MODULE,F,A]), 534 ok; 535 [_|_] -> 536 Line = proplists:get_value(line, Loc), 537 File = proplists:get_value(file, Loc), 538 File = ?MODULE_STRING ++ ".erl" 539 end. 540 541process_info_other(Config) when is_list(Config) -> 542 Self = self(), 543 Pid = spawn_link(fun() -> process_info_looper(Self) end), 544 receive after 1 -> ok end, 545 pio_current_location(10000, Pid, 0, 0), 546 pio_current_stacktrace(). 547 548pio_current_location(0, _, Pi, Looper) -> 549 io:format("~w call(s) to erlang:process_info/2", [Pi]), 550 io:format("~w call(s) to ~w:process_info_looper/1", [Looper,?MODULE]); 551pio_current_location(N, Pid, Pi, Looper) -> 552 erlang:yield(), 553 {current_location,Where} = process_info(Pid, current_location), 554 case Where of 555 {erlang,process_info,2,[]} -> 556 pio_current_location(N-1, Pid, Pi+1, Looper); 557 {erts_internal,await_result,1, Loc} when is_list(Loc) -> 558 pio_current_location(N-1, Pid, Pi+1, Looper); 559 {?MODULE,process_info_looper,1,Loc} when is_list(Loc) -> 560 pio_current_location(N-1, Pid, Pi, Looper+1); 561 _ -> 562 exit({unexpected_location, Where}) 563 end. 564 565pio_current_stacktrace() -> 566 L = [begin 567 case process_info(P, current_stacktrace) of 568 {current_stacktrace, Stk} -> {P,Stk}; 569 undefined -> {P, []} 570 end 571 end || P <- processes()], 572 [erlang:garbage_collect(P) || {P,_} <- L], 573 erlang:garbage_collect(), 574 [verify_stacktrace(Stk) || {_,Stk} <- L], 575 ok. 576 577verify_stacktrace([{M,F,A,Loc}|T]) 578 when is_atom(M), 579 is_atom(F), 580 is_integer(A), 581 is_list(Loc) -> 582 verify_stacktrace(T); 583verify_stacktrace([]) -> ok. 584 585process_info_looper(Parent) -> 586 process_info(Parent, current_location), 587 process_info_looper(Parent). 588 589%% Tests the process_info/1 BIF on another process with messages. 590process_info_other_msg(Config) when is_list(Config) -> 591 Self = self(), 592 Pid = spawn_link(fun() -> other_process(Self) end), 593 receive 594 {go_ahead,Pid} -> ok 595 end, 596 597 Own = {my,own,message}, 598 599 {messages,[Own]} = process_info(Pid, messages), 600 601 Garbage = kb_128(), 602 MsgA = {a,Garbage}, 603 MsgB = {b,Garbage}, 604 MsgC = {c,Garbage}, 605 MsgD = {d,Garbage}, 606 MsgE = {e,Garbage}, 607 608 Pid ! MsgA, 609 {messages,[Own,MsgA]} = process_info(Pid, messages), 610 Pid ! MsgB, 611 {messages,[Own,MsgA,MsgB]} = process_info(Pid, messages), 612 Pid ! MsgC, 613 {messages,[Own,MsgA,MsgB,MsgC]} = process_info(Pid, messages), 614 Pid ! MsgD, 615 {messages,[Own,MsgA,MsgB,MsgC,MsgD]} = process_info(Pid, messages), 616 Pid ! MsgE, 617 {messages,[Own,MsgA,MsgB,MsgC,MsgD,MsgE]=All} = process_info(Pid, messages), 618 {memory,BytesOther} = process_info(Pid, memory), 619 {memory,BytesSelf} = process_info(self(), memory), 620 621 io:format("Memory ~p: ~p\n", [Pid,BytesOther]), 622 io:format("Memory ~p (self): ~p\n", [self(),BytesSelf]), 623 624 [Own,MsgA,MsgB,MsgC,MsgD,MsgE] = All, 625 626 Pid ! {self(),empty}, 627 receive 628 empty -> ok 629 end, 630 {messages,[]} = process_info(Pid, messages), 631 632 {min_heap_size, 233} = process_info(Pid, min_heap_size), 633 {min_bin_vheap_size, 46422} = process_info(Pid, min_bin_vheap_size), 634 {max_heap_size, #{ size := 0, kill := true, error_logger := true}} = 635 process_info(self(), max_heap_size), 636 637 Pid ! stop, 638 ok. 639 640process_info_other_dist_msg(Config) when is_list(Config) -> 641 %% 642 %% Check that process_info can handle messages that have not been 643 %% decoded yet. 644 %% 645 {ok, Node} = start_node(Config), 646 Self = self(), 647 Pid = spawn_link(fun() -> other_process(Self) end), 648 receive {go_ahead,Pid} -> ok end, 649 650 Own = {my,own,message}, 651 652 {messages,[Own]} = process_info(Pid, messages), 653 Garbage = kb_128(), 654 MsgA = {a,self(),Garbage}, 655 MsgB = {b,self(),Garbage}, 656 MsgC = {c,self(),Garbage}, 657 MsgD = {d,self(),Garbage}, 658 MsgE = {e,self(),Garbage}, 659 660 %% We don't want the other process to decode messages itself 661 %% therefore we suspend it. 662 true = erlang:suspend_process(Pid), 663 spawn_link(Node, fun () -> 664 Pid ! MsgA, 665 Pid ! MsgB, 666 Pid ! MsgC, 667 Self ! check_abc 668 end), 669 receive check_abc -> ok end, 670 [{status,suspended}, 671 {messages,[Own,MsgA,MsgB,MsgC]}, 672 {status,suspended}]= process_info(Pid, [status,messages,status]), 673 spawn_link(Node, fun () -> 674 Pid ! MsgD, 675 Pid ! MsgE, 676 Self ! check_de 677 end), 678 receive check_de -> ok end, 679 {messages,[Own,MsgA,MsgB,MsgC,MsgD,MsgE]=All} = process_info(Pid, messages), 680 true = erlang:resume_process(Pid), 681 Pid ! {self(), get_all_messages}, 682 receive 683 {all_messages, AllMsgs} -> 684 All = AllMsgs 685 end, 686 {messages,[]} = process_info(Pid, messages), 687 Pid ! stop, 688 stop_node(Node), 689 ok. 690 691process_info_other_status(Config) when is_list(Config) -> 692 %% OTP-17628: status was erroneously reported as 'running', 693 %% when it should be 'waiting', when the priority of the 694 %% caller exceeded the priority of the processes being 695 %% checked (due to prio elevation). 696 Self = self(), 697 Other = spawn_link(fun () -> other_process(Self) end), 698 receive {go_ahead, Other} -> ok end, 699 receive after 100 -> ok end, 700 {status, waiting} = process_info(Other, status), 701 process_flag(priority, high), 702 {status, waiting} = process_info(Other, status), 703 process_flag(priority, max), 704 {status, waiting} = process_info(Other, status), 705 Other ! stop, 706 ok. 707 708other_process(Parent) -> 709 self() ! {my,own,message}, 710 Parent ! {go_ahead,self()}, 711 other_process_1(). 712 713other_process_1() -> 714 receive 715 {Parent,get_all_messages} -> 716 Parent ! {all_messages, get_all_messages()}, 717 other_process_1(); 718 {Parent,empty} -> 719 receive_all(), 720 Parent ! empty, 721 other_process_1(); 722 stop -> ok 723 end. 724 725get_all_messages() -> 726 get_all_messages([]). 727 728get_all_messages(Msgs) -> 729 receive 730 Msg -> 731 get_all_messages([Msg|Msgs]) 732 after 0 -> 733 lists:reverse(Msgs) 734 end. 735 736receive_all() -> 737 receive 738 _ -> receive_all() 739 after 0 -> ok 740 end. 741 742chk_pi_order([],[]) -> 743 ok; 744chk_pi_order([{Arg, _}| Values], [Arg|Args]) -> 745 chk_pi_order(Values, Args). 746 747process_info_2_list(Config) when is_list(Config) -> 748 Proc = spawn_link(fun () -> receive after infinity -> ok end end), 749 register(process_SUITE_process_info_2_list1, self()), 750 register(process_SUITE_process_info_2_list2, Proc), 751 erts_debug:set_internal_state(available_internal_state,true), 752 AllArgs = erts_debug:get_internal_state(process_info_args), 753 A1 = lists:sort(AllArgs) ++ [status] ++ lists:reverse(AllArgs), 754 755 %% Verify that argument is accepted as single atom 756 lists:foreach(fun (A) -> 757 {A, _} = process_info(Proc, A), 758 {A, _} = process_info(self(), A) 759 end, A1), 760 761 %% Verify that order is preserved 762 ok = chk_pi_order(process_info(self(), A1), A1), 763 ok = chk_pi_order(process_info(Proc, A1), A1), 764 765 %% Small arg list 766 A2 = [status, stack_size, trap_exit, priority], 767 [{status, _}, {stack_size, _}, {trap_exit, _}, {priority, _}] 768 = process_info(Proc, A2), 769 [{status, _}, {stack_size, _}, {trap_exit, _}, {priority, _}] 770 = process_info(self(), A2), 771 772 %% Huge arg list (note values are shared) 773 A3 = lists:duplicate(5000,backtrace), 774 V3 = process_info(Proc, A3), 775 5000 = length(V3), 776 lists:foreach(fun ({backtrace, _}) -> ok end, V3), 777 ok. 778 779process_info_lock_reschedule(Config) when is_list(Config) -> 780 %% We need a process that is running and an item that requires 781 %% process_info to take the main process lock. 782 Target1 = spawn_link(fun tok_loop/0), 783 Name1 = process_info_lock_reschedule_running, 784 register(Name1, Target1), 785 Target2 = spawn_link(fun () -> receive after infinity -> ok end end), 786 Name2 = process_info_lock_reschedule_waiting, 787 register(Name2, Target2), 788 PI = fun(_) -> 789 erlang:yield(), 790 [{registered_name, Name1}] = process_info(Target1, [registered_name]), 791 [{registered_name, Name2}] = process_info(Target2, [registered_name]), 792 erlang:yield(), 793 {registered_name, Name1} = process_info(Target1, registered_name), 794 {registered_name, Name2} = process_info(Target2, registered_name), 795 erlang:yield(), 796 [{registered_name, Name1}| _] = process_info(Target1), 797 [{registered_name, Name2}| _] = process_info(Target2) 798 end, 799 lists:foreach(PI, lists:seq(1,1000)), 800 %% Make sure Target1 still is willing to "tok loop" 801 case process_info(Target1, status) of 802 {status, OkStatus} when OkStatus == runnable; 803 OkStatus == running; 804 OkStatus == garbage_collecting -> 805 unlink(Target1), 806 unlink(Target2), 807 exit(Target1, bang), 808 exit(Target2, bang), 809 OkStatus; 810 {status, BadStatus} -> 811 ct:fail(BadStatus) 812 end. 813 814pi_loop(_Name, _Pid, 0) -> 815 ok; 816pi_loop(Name, Pid, N) -> 817 {registered_name, Name} = process_info(Pid, registered_name), 818 pi_loop(Name, Pid, N-1). 819 820process_info_lock_reschedule2(Config) when is_list(Config) -> 821 Parent = self(), 822 Fun = fun () -> 823 receive {go, Name, Pid} -> ok end, 824 pi_loop(Name, Pid, 10000), 825 Parent ! {done, self()}, 826 receive after infinity -> ok end 827 end, 828 P1 = spawn_link(Fun), 829 N1 = process_info_lock_reschedule2_1, 830 true = register(N1, P1), 831 P2 = spawn_link(Fun), 832 N2 = process_info_lock_reschedule2_2, 833 true = register(N2, P2), 834 P3 = spawn_link(Fun), 835 N3 = process_info_lock_reschedule2_3, 836 true = register(N3, P3), 837 P4 = spawn_link(Fun), 838 N4 = process_info_lock_reschedule2_4, 839 true = register(N4, P4), 840 P5 = spawn_link(Fun), 841 N5 = process_info_lock_reschedule2_5, 842 true = register(N5, P5), 843 P6 = spawn_link(Fun), 844 N6 = process_info_lock_reschedule2_6, 845 true = register(N6, P6), 846 P1 ! {go, N2, P2}, 847 P2 ! {go, N1, P1}, 848 P3 ! {go, N1, P1}, 849 P4 ! {go, N1, P1}, 850 P5 ! {go, N6, P6}, 851 P6 ! {go, N5, P5}, 852 receive {done, P1} -> ok end, 853 receive {done, P2} -> ok end, 854 receive {done, P3} -> ok end, 855 receive {done, P4} -> ok end, 856 receive {done, P5} -> ok end, 857 receive {done, P6} -> ok end, 858 unlink(P1), exit(P1, bang), 859 unlink(P2), exit(P2, bang), 860 unlink(P3), exit(P3, bang), 861 unlink(P4), exit(P4, bang), 862 unlink(P5), exit(P5, bang), 863 unlink(P6), exit(P6, bang), 864 ok. 865 866many_args(0,_B,_C,_D,_E,_F,_G,_H,_I,_J) -> 867 ok; 868many_args(A,B,C,D,E,F,G,H,I,J) -> 869 many_args(A-1,B,C,D,E,F,G,H,I,J). 870 871do_pi_msg_len(PT, AT) -> 872 lists:map(fun (_) -> ok end, [a,b,c,d]), 873 {message_queue_len, _} = process_info(element(2,PT), element(2,AT)). 874 875process_info_lock_reschedule3(Config) when is_list(Config) -> 876 %% We need a process that is running and an item that requires 877 %% process_info to take the main process lock. 878 Target1 = spawn_link(fun tok_loop/0), 879 Name1 = process_info_lock_reschedule_running, 880 register(Name1, Target1), 881 Target2 = spawn_link(fun () -> receive after infinity -> ok end end), 882 Name2 = process_info_lock_reschedule_waiting, 883 register(Name2, Target2), 884 PI = fun(N) -> 885 case N rem 10 of 886 0 -> erlang:yield(); 887 _ -> ok 888 end, 889 do_pi_msg_len({proc, Target1}, 890 {arg, message_queue_len}) 891 end, 892 many_args(100000,1,2,3,4,5,6,7,8,9), 893 lists:foreach(PI, lists:seq(1,1000000)), 894 %% Make sure Target1 still is willing to "tok loop" 895 case process_info(Target1, status) of 896 {status, OkStatus} when OkStatus == runnable; 897 OkStatus == running; 898 OkStatus == garbage_collecting -> 899 unlink(Target1), 900 unlink(Target2), 901 exit(Target1, bang), 902 exit(Target2, bang), 903 OkStatus; 904 {status, BadStatus} -> 905 ct:fail(BadStatus) 906 end. 907 908otp_4725(Config) when is_list(Config) -> 909 Tester = self(), 910 Ref1 = make_ref(), 911 Pid1 = spawn_opt(fun () -> 912 Tester ! {Ref1, process_info(self())}, 913 receive 914 Ref1 -> bye 915 end 916 end, [link, {priority, max}, {fullsweep_after, 600}]), 917 receive 918 {Ref1, ProcInfo1A} -> 919 ProcInfo1B = process_info(Pid1), 920 Pid1 ! Ref1, 921 check_proc_infos(ProcInfo1A, ProcInfo1B) 922 end, 923 Ref2 = make_ref(), 924 Pid2 = spawn_opt(fun () -> 925 Tester ! {Ref2, process_info(self())}, 926 receive 927 Ref2 -> bye 928 end 929 end, 930 []), 931 receive 932 {Ref2, ProcInfo2A} -> 933 ProcInfo2B = process_info(Pid2), 934 Pid2 ! Ref2, 935 check_proc_infos(ProcInfo2A, ProcInfo2B) 936 end, 937 ok. 938 939check_proc_infos(A, B) -> 940 IC = lists:keysearch(initial_call, 1, A), 941 IC = lists:keysearch(initial_call, 1, B), 942 943 L = lists:keysearch(links, 1, A), 944 L = lists:keysearch(links, 1, B), 945 946 D = lists:keysearch(dictionary, 1, A), 947 D = lists:keysearch(dictionary, 1, B), 948 949 TE = lists:keysearch(trap_exit, 1, A), 950 TE = lists:keysearch(trap_exit, 1, B), 951 952 EH = lists:keysearch(error_handler, 1, A), 953 EH = lists:keysearch(error_handler, 1, B), 954 955 P = lists:keysearch(priority, 1, A), 956 P = lists:keysearch(priority, 1, B), 957 958 GL = lists:keysearch(group_leader, 1, A), 959 GL = lists:keysearch(group_leader, 1, B), 960 961 GC = lists:keysearch(garbage_collection, 1, A), 962 GC = lists:keysearch(garbage_collection, 1, B), 963 964 ok. 965 966 967%% Dummies. 968 969start_spawner() -> 970 ok. 971 972stop_spawner() -> 973 ok. 974 975%% Tests erlang:process_info(Pid, garbage_collection_info) 976process_info_garbage_collection(_Config) -> 977 Parent = self(), 978 Pid = spawn_link( 979 fun() -> 980 %% We set mqd to off_heap and send an tuple 981 %% to process in order to force mbuf_size 982 %% to be used 983 process_flag(message_queue_data, off_heap), 984 receive go -> ok end, 985 (fun F(0) -> 986 Parent ! deep, 987 receive {ok,_} -> ok end, 988 []; 989 F(N) -> 990 timer:sleep(1), 991 [lists:seq(1,100) | F(N-1)] 992 end)(1000), 993 Parent ! shallow, 994 receive done -> ok end 995 end), 996 [{garbage_collection_info, Before},{total_heap_size, THSBefore}] = 997 erlang:process_info(Pid, [garbage_collection_info, total_heap_size]), 998 Pid ! go, receive deep -> ok end, 999 [{_, Deep},{_,THSDeep}] = 1000 erlang:process_info(Pid, [garbage_collection_info, total_heap_size]), 1001 Pid ! {ok, make_ref()}, receive shallow -> ok end, 1002 [{_, After},{_, THSAfter}] = 1003 erlang:process_info(Pid, [garbage_collection_info, total_heap_size]), 1004 Pid ! done, 1005 1006 %% Do some general checks to see if everything seems to be roughly correct 1007 ct:log("Before: ~p",[Before]), 1008 ct:log("Deep: ~p",[Deep]), 1009 ct:log("After: ~p",[After]), 1010 ct:log("Before THS: ~p",[THSBefore]), 1011 ct:log("Deep THS: ~p",[THSDeep]), 1012 ct:log("After THS: ~p",[THSAfter]), 1013 1014 %% Check stack_size 1015 true = gv(stack_size, Before) < gv(stack_size, Deep), 1016 true = gv(stack_size, After) < gv(stack_size, Deep), 1017 1018 %% Check used heap size 1019 true = gv(heap_size, Before) + gv(old_heap_size, Before) 1020 < gv(heap_size, Deep) + gv(old_heap_size, Deep), 1021 true = gv(heap_size, Before) + gv(old_heap_size, Before) 1022 < gv(heap_size, After) + gv(old_heap_size, After), 1023 1024 %% Check that total_heap_size == heap_block_size + old_heap_block_size + mbuf_size 1025 THSBefore = gv(heap_block_size, Before) 1026 + gv(old_heap_block_size, Before) 1027 + gv(mbuf_size, Before), 1028 1029 THSDeep = gv(heap_block_size, Deep) 1030 + gv(old_heap_block_size, Deep) 1031 + gv(mbuf_size, Deep), 1032 1033 THSAfter = gv(heap_block_size, After) 1034 + gv(old_heap_block_size, After) 1035 + gv(mbuf_size, After), 1036 1037 ok. 1038 1039gv(Key,List) -> 1040 proplists:get_value(Key,List). 1041 1042process_info_smoke_all_tester() -> 1043 register(process_info_smoke_all_tester, self()), 1044 put(ets_ref, ets:new(blupp, [])), 1045 put(binary, [list_to_binary(lists:duplicate(1000, 1)), 1046 list_to_binary(lists:duplicate(1000, 2))]), 1047 process_info_smoke_all_tester_loop(). 1048 1049process_info_smoke_all_tester_loop() -> 1050 receive 1051 {other_process, Pid} -> 1052 case get(procs) of 1053 undefined -> put(procs, [Pid]); 1054 Procs -> put(procs, [Pid|Procs]) 1055 end, 1056 erlang:monitor(process, Pid), 1057 link(Pid), 1058 process_info_smoke_all_tester_loop() 1059 end. 1060 1061process_info_smoke_all(Config) when is_list(Config) -> 1062 AllPIOptions = [registered_name, 1063 current_function, 1064 initial_call, 1065 messages, 1066 message_queue_len, 1067 links, 1068 monitors, 1069 monitored_by, 1070 dictionary, 1071 trap_exit, 1072 error_handler, 1073 heap_size, 1074 stack_size, 1075 memory, 1076 garbage_collection, 1077 group_leader, 1078 reductions, 1079 priority, 1080 trace, 1081 binary, 1082 sequential_trace_token, 1083 catchlevel, 1084 backtrace, 1085 last_calls, 1086 total_heap_size, 1087 suspending, 1088 min_heap_size, 1089 min_bin_vheap_size, 1090 max_heap_size, 1091 current_location, 1092 current_stacktrace, 1093 message_queue_data, 1094 garbage_collection_info, 1095 magic_ref, 1096 fullsweep_after], 1097 1098 {ok, Node} = start_node(Config, ""), 1099 RP = spawn_link(Node, fun process_info_smoke_all_tester/0), 1100 LP = spawn_link(fun process_info_smoke_all_tester/0), 1101 RP ! {other_process, LP}, 1102 LP ! {other_process, RP}, 1103 LP ! {other_process, self()}, 1104 LP ! ets:new(blapp, []), 1105 LP ! ets:new(blipp, []), 1106 LP ! list_to_binary(lists:duplicate(1000, 3)), 1107 receive after 1000 -> ok end, 1108 _MLP = erlang:monitor(process, LP), 1109 true = is_process_alive(LP), 1110 PI = process_info(LP, AllPIOptions), 1111 io:format("~p~n", [PI]), 1112 garbage_collect(), 1113 unlink(RP), 1114 unlink(LP), 1115 exit(RP, kill), 1116 exit(LP, kill), 1117 false = is_process_alive(LP), 1118 stop_node(Node), 1119 ok. 1120 1121process_info_status_handled_signal(Config) when is_list(Config) -> 1122 P = spawn_link(fun () -> 1123 receive after infinity -> ok end 1124 end), 1125 wait_until(fun () -> 1126 process_info(P, status) == {status, waiting} 1127 end), 1128 %% 1129 %% The 'messages' option will force a process-info-request 1130 %% signal to be scheduled on the process. Ensure that status 1131 %% 'waiting' is reported even though it is actually running 1132 %% when handling the request. We want it to report the status 1133 %% it would have had if it had not been handling the 1134 %% process-info-request... 1135 %% 1136 [{status, waiting}, {messages, []}] = process_info(P, [status, messages]), 1137 unlink(P), 1138 exit(P, kill), 1139 false = erlang:is_process_alive(P), 1140 ok. 1141 1142%% OTP-15709 1143%% Provoke a bug where process_info(reductions) returned wrong result 1144%% because REDS_IN (def_arg_reg[5]) is read when the process in not running. 1145%% 1146%% And a bug where process_info(reductions) on a process which was releasing its 1147%% main lock during execution could result in negative reduction diffs. 1148process_info_reductions(Config) when is_list(Config) -> 1149 {S1, S2} = case erlang:system_info(schedulers_online) of 1150 1 -> {1,1}; 1151 _ -> {1,2} 1152 end, 1153 io:format("Run on schedulers ~p and ~p\n", [S1,S2]), 1154 Boss = self(), 1155 Doer = spawn_opt(fun () -> 1156 pi_reductions_tester(true, 10, fun pi_reductions_spinnloop/0, S2), 1157 pi_reductions_tester(true, 10, fun pi_reductions_recvloop/0, S2), 1158 pi_reductions_tester(false, 100, fun pi_reductions_main_unlocker/0, S2), 1159 Boss ! {self(), done} 1160 end, 1161 [link, {scheduler, S1}]), 1162 1163 {Doer, done} = receive M -> M end, 1164 ok. 1165 1166pi_reductions_tester(ForceSignal, MaxCalls, Fun, S2) -> 1167 Pid = spawn_opt(Fun, [link, {scheduler,S2}]), 1168 Extra = case ForceSignal of 1169 true -> 1170 %% Add another item that force sending the request 1171 %% as a signal, like 'current_function'. 1172 [current_function]; 1173 false -> 1174 [] 1175 end, 1176 LoopFun = fun Me(Calls, Prev, Acc0) -> 1177 PI = process_info(Pid, [reductions | Extra]), 1178 [{reductions,Reds} | _] = PI, 1179 Diff = Reds - Prev, 1180 %% Verify we get sane non-negative reduction diffs 1181 {Diff, true} = {Diff, (Diff >= 0)}, 1182 {Diff, true} = {Diff, (Diff =< 1000*1000)}, 1183 Acc1 = [Diff | Acc0], 1184 case Calls >= MaxCalls of 1185 true -> Acc1; 1186 false -> Me(Calls+1, Reds, Acc1) 1187 end 1188 end, 1189 DiffList = LoopFun(0, 0, []), 1190 unlink(Pid), 1191 exit(Pid,kill), 1192 io:format("Reduction diffs: ~p\n", [lists:reverse(DiffList)]), 1193 ok. 1194 1195pi_reductions_spinnloop() -> 1196 %% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN 1197 pi_reductions_spinnloop(999*1000, atom, "hej", self(), make_ref(), 3.14). 1198 1199pi_reductions_spinnloop(N,A,B,C,D,E) when N > 0 -> 1200 pi_reductions_spinnloop(N-1,B,C,D,E,A); 1201pi_reductions_spinnloop(0,_,_,_,_,_) -> 1202 %% Stop to limit max number of reductions consumed 1203 pi_reductions_recvloop(). 1204 1205pi_reductions_recvloop() -> 1206 receive 1207 "a free lunch" -> false 1208 end. 1209 1210pi_reductions_main_unlocker() -> 1211 Other = spawn_link(fun() -> receive die -> ok end end), 1212 pi_reductions_main_unlocker_loop(Other). 1213 1214pi_reductions_main_unlocker_loop(Other) -> 1215 %% Assumption: register(OtherPid, Name) will unlock main lock of calling 1216 %% process during execution. 1217 register(pi_reductions_main_unlocker, Other), 1218 unregister(pi_reductions_main_unlocker), 1219 1220 %% Yield in order to increase probability of process_info sometimes probing 1221 %% this process when it's not RUNNING. 1222 erlang:yield(), 1223 pi_reductions_main_unlocker_loop(Other). 1224 1225 1226%% Tests erlang:bump_reductions/1. 1227bump_reductions(Config) when is_list(Config) -> 1228 erlang:garbage_collect(), 1229 erlang:yield(), % Clear reductions. 1230 {reductions,R1} = process_info(self(), reductions), 1231 true = erlang:bump_reductions(100), 1232 {reductions,R2} = process_info(self(), reductions), 1233 case R2-R1 of 1234 Diff when Diff < 100 -> 1235 ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]), 1236 ct:fail({small_diff, Diff}); 1237 Diff when Diff > 110 -> 1238 ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]), 1239 ct:fail({big_diff, Diff}); 1240 Diff -> 1241 io:format("~p\n", [Diff]), 1242 ok 1243 end, 1244 1245 %% Make sure that a bignum reduction doesn't crash the emulator (32-bit CPU). 1246 bump_big(R2, 16#08000000). 1247 1248bump_big(Prev, Limit) -> 1249 true = erlang:bump_reductions(100000), %Limited to CONTEXT_REDUCTIONS. 1250 case process_info(self(), reductions) of 1251 {reductions,Big} when is_integer(Big), Big > Limit -> 1252 erlang:garbage_collect(), 1253 io:format("~p\n", [Big]); 1254 {reductions,R} when is_integer(R), R > Prev -> 1255 bump_big(R, Limit) 1256 end, 1257 ok. 1258 1259%% Priority 'low' should be mixed with 'normal' using a factor of 1260%% about 8. (OTP-2644) 1261low_prio(Config) when is_list(Config) -> 1262 erlang:system_flag(multi_scheduling, block_normal), 1263 Prop = low_prio_test(Config), 1264 erlang:system_flag(multi_scheduling, unblock_normal), 1265 Str = lists:flatten(io_lib:format("Low/high proportion is ~.3f", 1266 [Prop])), 1267 {comment,Str}. 1268 1269low_prio_test(Config) when is_list(Config) -> 1270 process_flag(trap_exit, true), 1271 1272 %% Spawn the server running with high priority. The server must 1273 %% not run at normal priority as that would skew the results for 1274 %% two reasons: 1275 %% 1276 %% 1. There would be one more normal-priority processes than 1277 %% low-priority processes. 1278 %% 1279 %% 2. The receive queue would grow faster than the server process 1280 %% could process it. That would in turn trigger the reduction 1281 %% punishment for the clients. 1282 S = spawn_opt(?MODULE, prio_server, [0, 0], [link,{priority,high}]), 1283 1284 %% Spawn the clients and let them run for a while. 1285 PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)), 1286 ct:sleep({seconds,2}), 1287 lists:foreach(fun (P) -> exit(P, kill) end, PCs), 1288 1289 %% Stop the server and retrieve the result. 1290 S ! exit, 1291 receive 1292 {'EXIT', S, {A, B}} -> 1293 check_prio(A, B) 1294 end. 1295 1296check_prio(A, B) -> 1297 Prop = A/B, 1298 ok = io:format("Low=~p, High=~p, Prop=~p\n", [A, B, Prop]), 1299 1300 %% Prop is expected to be appr. 1/8. Allow a reasonable margin. 1301 true = Prop < 1/4, 1302 true = Prop > 1/16, 1303 Prop. 1304 1305prio_server(A, B) -> 1306 receive 1307 low -> 1308 prio_server(A+1, B); 1309 normal -> 1310 prio_server(A, B+1); 1311 exit -> 1312 exit({A, B}) 1313 end. 1314 1315spawn_prio_clients(_, 0) -> 1316 []; 1317spawn_prio_clients(S, N) -> 1318 [spawn_opt(?MODULE, prio_client, [S, normal], [link, {priority,normal}]), 1319 spawn_opt(?MODULE, prio_client, [S, low], [link, {priority,low}]) 1320 | spawn_prio_clients(S, N-1)]. 1321 1322prio_client(S, Prio) -> 1323 S ! Prio, 1324 prio_client(S, Prio). 1325 1326make_sub_binary(Bin) when is_binary(Bin) -> 1327 {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), 1328 B; 1329make_sub_binary(List) -> 1330 make_sub_binary(list_to_binary(List)). 1331 1332make_unaligned_sub_binary(Bin0) -> 1333 Bin1 = <<0:3,Bin0/binary,31:5>>, 1334 Sz = size(Bin0), 1335 <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), 1336 Bin. 1337 1338%% Tests erlang:yield/1 1339yield(Config) when is_list(Config) -> 1340 case catch erlang:system_info(modified_timing_level) of 1341 Level when is_integer(Level) -> 1342 {skipped, 1343 "Modified timing (level " ++ integer_to_list(Level) 1344 ++ ") is enabled. Testcase gets messed up by modfied " 1345 "timing."}; 1346 _ -> 1347 MS = erlang:system_flag(multi_scheduling, block_normal), 1348 yield_test(), 1349 erlang:system_flag(multi_scheduling, unblock_normal), 1350 case MS of 1351 blocked -> 1352 {comment, 1353 "Multi-scheduling blocked during test. This test-case " 1354 "was not written to work with multiple schedulers (the " 1355 "yield2 test-case tests almost the same thing)."}; 1356 _ -> 1357 ok 1358 end 1359 end. 1360 1361yield_test() -> 1362 erlang:garbage_collect(), 1363 receive after 1 -> ok end, % Clear reductions. 1364 SC = schedcnt(start), 1365 {reductions, R1} = process_info(self(), reductions), 1366 {ok, true} = call_yield(middle), 1367 true = call_yield(final), 1368 true = call_yield(), 1369 true = apply(erlang, yield, []), 1370 {reductions, R2} = process_info(self(), reductions), 1371 Schedcnt = schedcnt(stop, SC), 1372 case {R2-R1, Schedcnt} of 1373 {Diff, 4} when Diff < 30 -> 1374 ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w", 1375 [R1, R2, Schedcnt]); 1376 {Diff, _} -> 1377 ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w", 1378 [R1, R2, Schedcnt]), 1379 ct:fail({measurement_error, Diff, Schedcnt}) 1380 end. 1381 1382call_yield() -> 1383 erlang:yield(). 1384 1385call_yield(middle) -> 1386 {ok, erlang:yield()}; 1387call_yield(final) -> 1388 case self() of 1389 Self when is_pid(Self) -> 1390 ok 1391 end, 1392 erlang:yield(). 1393 1394schedcnt(start) -> 1395 Ref = make_ref(), 1396 Fun = 1397 fun (F, Cnt) -> 1398 receive 1399 {Ref, Parent} -> 1400 Parent ! {Ref, Cnt} 1401 after 0 -> 1402 erlang:yield(), 1403 F(F, Cnt+1) 1404 end 1405 end, 1406 Pid = spawn_link(fun () -> Fun(Fun, 0) end), 1407 {Ref, Pid}. 1408 1409schedcnt(stop, {Ref, Pid}) when is_reference(Ref), is_pid(Pid) -> 1410 Pid ! {Ref, self()}, 1411 receive 1412 {Ref, Cnt} -> 1413 Cnt 1414 end. 1415 1416yield2(Config) when is_list(Config) -> 1417 Me = self(), 1418 Go = make_ref(), 1419 RedDiff = make_ref(), 1420 Done = make_ref(), 1421 P = spawn(fun () -> 1422 receive Go -> ok end, 1423 {reductions, R1} = process_info(self(), reductions), 1424 {ok, true} = call_yield(middle), 1425 true = call_yield(final), 1426 true = call_yield(), 1427 true = apply(erlang, yield, []), 1428 {reductions, R2} = process_info(self(), reductions), 1429 Me ! {RedDiff, R2 - R1}, 1430 exit(Done) 1431 end), 1432 erlang:yield(), 1433 1434 1 = erlang:trace(P, true, [running, procs, {tracer, self()}]), 1435 1436 P ! Go, 1437 1438 %% receive Go -> ok end, 1439 {trace, P, in, _} = next_tmsg(P), 1440 1441 %% {ok, true} = call_yield(middle), 1442 {trace, P, out, _} = next_tmsg(P), 1443 {trace, P, in, _} = next_tmsg(P), 1444 1445 %% true = call_yield(final), 1446 {trace, P, out, _} = next_tmsg(P), 1447 {trace, P, in, _} = next_tmsg(P), 1448 1449 %% true = call_yield(), 1450 {trace, P, out, _} = next_tmsg(P), 1451 {trace, P, in, _} = next_tmsg(P), 1452 1453 %% true = apply(erlang, yield, []), 1454 {trace, P, out, _} = next_tmsg(P), 1455 {trace, P, in, _} = next_tmsg(P), 1456 1457 %% exit(Done) 1458 {trace, P, exit, Done} = next_tmsg(P), 1459 1460 1461 receive 1462 {RedDiff, Reductions} when Reductions < 30, Reductions > 0 -> 1463 io:format("Reductions = ~p~n", [Reductions]), 1464 ok; 1465 {RedDiff, Reductions} -> 1466 ct:fail({unexpected_reduction_count, Reductions}) 1467 end, 1468 1469 none = next_tmsg(P), 1470 1471 ok. 1472 1473next_tmsg(Pid) -> 1474 receive 1475 TMsg when is_tuple(TMsg), 1476 element(1, TMsg) == trace, 1477 element(2, TMsg) == Pid -> 1478 TMsg 1479 after 100 -> 1480 none 1481 end. 1482 1483%% Test that bad arguments to register/2 cause an exception. 1484bad_register(Config) when is_list(Config) -> 1485 Name = a_long_and_unused_name, 1486 1487 {'EXIT',{badarg,_}} = (catch register({bad,name}, self())), 1488 fail_register(undefined, self()), 1489 fail_register([bad,name], self()), 1490 1491 {Dead,Mref} = spawn_monitor(fun() -> true end), 1492 receive 1493 {'DOWN',Mref,process,Dead,_} -> ok 1494 end, 1495 fail_register(Name, Dead), 1496 fail_register(Name, make_ref()), 1497 fail_register(Name, []), 1498 fail_register(Name, {bad,process}), 1499 fail_register(Name, <<>>), 1500 ok. 1501 1502fail_register(Name, Process) -> 1503 {'EXIT',{badarg,_}} = (catch register(Name, Process)), 1504 {'EXIT',{badarg,_}} = (catch Name ! anything_goes), 1505 ok. 1506 1507garbage_collect(Config) when is_list(Config) -> 1508 Prio = process_flag(priority, high), 1509 true = erlang:garbage_collect(), 1510 1511 TokLoopers = lists:map(fun (_) -> 1512 spawn_opt(fun tok_loop/0, [{priority, low}, link]) 1513 end, lists:seq(1, 10)), 1514 1515 lists:foreach(fun (Pid) -> 1516 Mon = erlang:monitor(process, Pid), 1517 DownBefore = receive 1518 {'DOWN', Mon, _, _, _} -> 1519 true 1520 after 0 -> 1521 false 1522 end, 1523 GC = erlang:garbage_collect(Pid), 1524 DownAfter = receive 1525 {'DOWN', Mon, _, _, _} -> 1526 true 1527 after 0 -> 1528 false 1529 end, 1530 true = erlang:demonitor(Mon), 1531 case {DownBefore, DownAfter} of 1532 {true, _} -> false = GC; 1533 {false, false} -> true = GC; 1534 _ -> GC 1535 end 1536 end, processes()), 1537 1538 lists:foreach(fun (Pid) -> 1539 unlink(Pid), 1540 exit(Pid, bang) 1541 end, TokLoopers), 1542 process_flag(priority, Prio), 1543 ok. 1544 1545%% This used to cause the nofrag emulator to dump core 1546process_info_messages(Config) when is_list(Config) -> 1547 process_info_messages_test(), 1548 ok. 1549 1550process_info_messages_loop(0) -> ok; 1551process_info_messages_loop(N) -> process_info_messages_loop(N-1). 1552 1553process_info_messages_send_my_msgs_to(Rcvr) -> 1554 receive 1555 Msg -> 1556 Rcvr ! Msg, 1557 process_info_messages_send_my_msgs_to(Rcvr) 1558 after 0 -> 1559 ok 1560 end. 1561 1562process_info_messages_test() -> 1563 Go = make_ref(), 1564 Done = make_ref(), 1565 Rcvr = self(), 1566 Rcvr2 = spawn_link(fun () -> 1567 receive {Go, Rcvr} -> ok end, 1568 garbage_collect(), 1569 Rcvr ! {Done, self()} 1570 end), 1571 Sndrs = lists:map( 1572 fun (_) -> 1573 spawn_link(fun () -> 1574 Rcvr ! {Go, self()}, 1575 receive {Go, Rcvr} -> ok end, 1576 BigData = lists:seq(1, 1000), 1577 Rcvr ! BigData, 1578 Rcvr ! BigData, 1579 Rcvr ! BigData, 1580 Rcvr ! {Done, self()} 1581 end) 1582 end, lists:seq(1, 10)), 1583 lists:foreach(fun (Sndr) -> receive {Go, Sndr} -> ok end end, 1584 Sndrs), 1585 garbage_collect(), 1586 erlang:yield(), 1587 lists:foreach(fun (Sndr) -> Sndr ! {Go, self()} end, Sndrs), 1588 process_info_messages_loop(100000000), 1589 Msgs = process_info(self(), messages), 1590 lists:foreach(fun (Sndr) -> receive {Done, Sndr} -> ok end end, 1591 Sndrs), 1592 garbage_collect(), 1593 Rcvr2 ! Msgs, 1594 process_info_messages_send_my_msgs_to(Rcvr2), 1595 Rcvr2 ! {Go, self()}, 1596 garbage_collect(), 1597 receive {Done, Rcvr2} -> ok end, 1598 Msgs. 1599 1600chk_badarg(Fun) -> 1601 try Fun(), exit(no_badarg) catch error:badarg -> ok end. 1602 1603process_flag_badarg(Config) when is_list(Config) -> 1604 chk_badarg(fun () -> process_flag(gurka, banan) end), 1605 chk_badarg(fun () -> process_flag(trap_exit, gurka) end), 1606 chk_badarg(fun () -> process_flag(error_handler, 1) end), 1607 chk_badarg(fun () -> process_flag(min_heap_size, gurka) end), 1608 chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end), 1609 chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end), 1610 1611 chk_badarg(fun () -> process_flag(max_heap_size, gurka) end), 1612 chk_badarg(fun () -> process_flag(max_heap_size, -1) end), 1613 chk_badarg(fun () -> 1614 {_,Min} = process_info(self(), min_heap_size), 1615 process_flag(max_heap_size, Min - 1) 1616 end), 1617 chk_badarg(fun () -> 1618 {_,Min} = process_info(self(), min_heap_size), 1619 process_flag(max_heap_size, #{size => Min - 1}) 1620 end), 1621 chk_badarg(fun () -> process_flag(max_heap_size, #{}) end), 1622 chk_badarg(fun () -> process_flag(max_heap_size, #{ kill => true }) end), 1623 chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233, 1624 kill => gurka }) end), 1625 chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233, 1626 error_logger => gurka }) end), 1627 chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233, 1628 kill => true, 1629 error_logger => gurka }) end), 1630 chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 1 bsl 64 }) end), 1631 1632 chk_badarg(fun () -> process_flag(priority, 4711) end), 1633 chk_badarg(fun () -> process_flag(save_calls, hmmm) end), 1634 {P,Mref} = spawn_monitor(fun () -> receive "in vain" -> no end end), 1635 chk_badarg(fun () -> process_flag(P, save_calls, hmmm) end), 1636 chk_badarg(fun () -> process_flag(gurka, save_calls, hmmm) end), 1637 exit(P, die), 1638 chk_badarg(fun () -> process_flag(P, save_calls, 0) end), 1639 {'DOWN', Mref, process, P, die} = receive M -> M end, 1640 chk_badarg(fun () -> process_flag(P, save_calls, 0) end), 1641 ok. 1642 1643-include_lib("stdlib/include/ms_transform.hrl"). 1644 1645otp_6237(Config) when is_list(Config) -> 1646 Slctrs = lists:map(fun (_) -> 1647 spawn_link(fun () -> 1648 otp_6237_select_loop() 1649 end) 1650 end, 1651 lists:seq(1,5)), 1652 lists:foreach(fun (_) -> otp_6237_test() end, lists:seq(1, 100)), 1653 lists:foreach(fun (S) -> unlink(S),exit(S, kill) end, Slctrs), 1654 ok. 1655 1656otp_6237_test() -> 1657 Parent = self(), 1658 Inited = make_ref(), 1659 Die = make_ref(), 1660 Pid = spawn_link(fun () -> 1661 register(otp_6237,self()), 1662 otp_6237 = ets:new(otp_6237, 1663 [named_table, 1664 ordered_set]), 1665 ets:insert(otp_6237, 1666 [{I,I} 1667 || I <- lists:seq(1, 100)]), 1668 %% Inserting a lot of bif timers 1669 %% increase the possibility that 1670 %% the test will fail when the 1671 %% original cleanup order is used 1672 lists:foreach( fun (_) -> 1673 erlang:send_after(1000000, self(), {a,b,c}) 1674 end, lists:seq(1,1000)), 1675 Parent ! Inited, 1676 receive Die -> bye end 1677 end), 1678 receive 1679 Inited -> ok 1680 end, 1681 Pid ! Die, 1682 otp_6237_whereis_loop(). 1683 1684otp_6237_whereis_loop() -> 1685 case whereis(otp_6237) of 1686 undefined -> 1687 otp_6237 = ets:new(otp_6237, 1688 [named_table,ordered_set]), 1689 ets:delete(otp_6237), 1690 ok; 1691 _ -> 1692 otp_6237_whereis_loop() 1693 end. 1694 1695otp_6237_select_loop() -> 1696 catch ets:select(otp_6237, ets:fun2ms(fun({K, does_not_exist}) -> K end)), 1697 otp_6237_select_loop(). 1698 1699 1700-define(NoTestProcs, 10000). 1701-record(ptab_list_bif_info, {min_start_reds, 1702 tab_chunks, 1703 tab_chunks_size, 1704 tab_indices_per_red, 1705 free_term_proc_reds, 1706 term_procs_per_red, 1707 term_procs_max_reds, 1708 conses_per_red, 1709 debug_level}). 1710 1711processes_large_tab(Config) when is_list(Config) -> 1712 sys_mem_cond_run(2048, fun () -> processes_large_tab_test(Config) end). 1713 1714processes_large_tab_test(Config) -> 1715 enable_internal_state(), 1716 MaxDbgLvl = 20, 1717 MinProcTabSize = 2*(1 bsl 15), 1718 ProcTabSize0 = 1000000, 1719 ProcTabSize1 = case {erlang:system_info(schedulers_online), 1720 erlang:system_info(logical_processors)} of 1721 {Schdlrs, Cpus} when is_integer(Cpus), 1722 Schdlrs =< Cpus -> 1723 ProcTabSize0; 1724 _ -> 1725 ProcTabSize0 div 4 1726 end, 1727 ProcTabSize2 = case erlang:system_info(debug_compiled) of 1728 true -> ProcTabSize1 - 500000; 1729 false -> ProcTabSize1 1730 end, 1731 %% With high debug levels this test takes so long time that 1732 %% the connection times out; therefore, shrink the test on 1733 %% high debug levels. 1734 DbgLvl = case erts_debug:get_internal_state(processes_bif_info) of 1735 #ptab_list_bif_info{debug_level = Lvl} when Lvl > MaxDbgLvl -> 1736 20; 1737 #ptab_list_bif_info{debug_level = Lvl} when Lvl < 0 -> 1738 ct:fail({debug_level, Lvl}); 1739 #ptab_list_bif_info{debug_level = Lvl} -> 1740 Lvl 1741 end, 1742 ProcTabSize3 = ProcTabSize2 - (1300000 * DbgLvl div MaxDbgLvl), 1743 ProcTabSize = case ProcTabSize3 < MinProcTabSize of 1744 true -> MinProcTabSize; 1745 false -> ProcTabSize3 1746 end, 1747 {ok, LargeNode} = start_node(Config, 1748 "+P " ++ integer_to_list(ProcTabSize)), 1749 Res = rpc:call(LargeNode, ?MODULE, processes_bif_test, []), 1750 case rpc:call(LargeNode, 1751 erts_debug, 1752 get_internal_state, 1753 [processes_bif_info]) of 1754 #ptab_list_bif_info{tab_chunks = Chunks} when is_integer(Chunks), 1755 Chunks > 1 -> ok; 1756 PBInfo -> ct:fail(PBInfo) 1757 end, 1758 stop_node(LargeNode), 1759 chk_processes_bif_test_res(Res). 1760 1761processes_default_tab(Config) when is_list(Config) -> 1762 sys_mem_cond_run(1024, fun () -> processes_default_tab_test(Config) end). 1763 1764processes_default_tab_test(Config) -> 1765 {ok, DefaultNode} = start_node(Config, ""), 1766 Res = rpc:call(DefaultNode, ?MODULE, processes_bif_test, []), 1767 stop_node(DefaultNode), 1768 chk_processes_bif_test_res(Res). 1769 1770processes_small_tab(Config) when is_list(Config) -> 1771 {ok, SmallNode} = start_node(Config, "+P 1024"), 1772 Res = rpc:call(SmallNode, ?MODULE, processes_bif_test, []), 1773 PBInfo = rpc:call(SmallNode, erts_debug, get_internal_state, [processes_bif_info]), 1774 stop_node(SmallNode), 1775 true = PBInfo#ptab_list_bif_info.tab_chunks < 10, 1776 chk_processes_bif_test_res(Res). 1777 1778processes_this_tab(Config) when is_list(Config) -> 1779 Mem = case {erlang:system_info(build_type), 1780 erlang:system_info(allocator)} of 1781 {lcnt, {_, _Vsn, [sys_alloc], _Opts}} -> 1782 %% When running +Mea min + lcnt we may need more memory 1783 1024 * 4; 1784 _ -> 1785 1024 1786 end, 1787 sys_mem_cond_run(Mem, fun () -> chk_processes_bif_test_res(processes_bif_test()) end). 1788 1789chk_processes_bif_test_res(ok) -> ok; 1790chk_processes_bif_test_res({comment, _} = Comment) -> Comment; 1791chk_processes_bif_test_res(Failure) -> ct:fail(Failure). 1792 1793print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds, 1794 tab_chunks = TabChunks, 1795 tab_chunks_size = TabChunksSize, 1796 tab_indices_per_red = TabIndPerRed, 1797 free_term_proc_reds = FreeTPReds, 1798 term_procs_per_red = TPPerRed, 1799 term_procs_max_reds = TPMaxReds, 1800 conses_per_red = ConsesPerRed, 1801 debug_level = DbgLvl}) -> 1802 io:format("processes/0 bif info on node ~p:~n" 1803 "Min start reductions = ~p~n" 1804 "Process table chunks = ~p~n" 1805 "Process table chunks size = ~p~n" 1806 "Process table indices per reduction = ~p~n" 1807 "Reduction cost for free() on terminated process struct = ~p~n" 1808 "Inspect terminated processes per reduction = ~p~n" 1809 "Max reductions during inspection of terminated processes = ~p~n" 1810 "Create cons-cells per reduction = ~p~n" 1811 "Debug level = ~p~n", 1812 [node(), 1813 MinStartReds, 1814 TabChunks, 1815 TabChunksSize, 1816 TabIndPerRed, 1817 FreeTPReds, 1818 TPPerRed, 1819 TPMaxReds, 1820 ConsesPerRed, 1821 DbgLvl]). 1822 1823processes_bif_cleaner() -> 1824 receive {'EXIT', _, _} -> ok end, 1825 processes_bif_cleaner(). 1826 1827spawn_initial_hangarounds(Cleaner) -> 1828 TabSz = erlang:system_info(process_limit), 1829 erts_debug:set_internal_state(next_pid,TabSz), 1830 spawn_initial_hangarounds(Cleaner, 1831 TabSz, 1832 TabSz*2, 1833 0, 1834 []). 1835 1836processes_unexpected_result(CorrectProcs, Procs) -> 1837 ProcInfo = [registered_name, 1838 initial_call, 1839 current_function, 1840 status, 1841 priority], 1842 MissingProcs = CorrectProcs -- Procs, 1843 io:format("Missing processes: ~p", 1844 [lists:map(fun (Pid) -> 1845 [{pid, Pid} 1846 | case process_info(Pid, ProcInfo) of 1847 undefined -> []; 1848 Res -> Res 1849 end] 1850 end, 1851 MissingProcs)]), 1852 SuperfluousProcs = Procs -- CorrectProcs, 1853 io:format("Superfluous processes: ~p", 1854 [lists:map(fun (Pid) -> 1855 [{pid, Pid} 1856 | case process_info(Pid, ProcInfo) of 1857 undefined -> []; 1858 Res -> Res 1859 end] 1860 end, 1861 SuperfluousProcs)]), 1862 ct:fail(unexpected_result). 1863 1864hangaround(Cleaner, Type) -> 1865 %% Type is only used to distinguish different processes from 1866 %% when doing process_info 1867 try link(Cleaner) catch error:Reason -> exit(Reason) end, 1868 receive after infinity -> ok end, 1869 exit(Type). 1870 1871spawn_initial_hangarounds(_Cleaner, NP, Max, Len, HAs) when NP > Max -> 1872 {Len, HAs}; 1873spawn_initial_hangarounds(Cleaner, NP, Max, Len, HAs) -> 1874 Skip = 30, 1875 wait_for_proc_slots(Skip+3), 1876 HA1 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround], 1877 [{priority, low}]), 1878 HA2 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround], 1879 [{priority, normal}]), 1880 HA3 = spawn_opt(?MODULE, hangaround, [Cleaner, initial_hangaround], 1881 [{priority, high}]), 1882 spawn_drop(Skip), 1883 spawn_initial_hangarounds(Cleaner, NP+Skip, Max, Len+3, [HA1,HA2,HA3|HAs]). 1884 1885wait_for_proc_slots(MinFreeSlots) -> 1886 case erlang:system_info(process_limit) - erlang:system_info(process_count) of 1887 FreeSlots when FreeSlots < MinFreeSlots -> 1888 receive after 10 -> ok end, 1889 wait_for_proc_slots(MinFreeSlots); 1890 _FreeSlots -> 1891 ok 1892 end. 1893 1894spawn_drop(N) when N =< 0 -> 1895 ok; 1896spawn_drop(N) -> 1897 spawn(fun () -> ok end), 1898 spawn_drop(N-1). 1899 1900do_processes(WantReds) -> 1901 erts_debug:set_internal_state(reds_left, WantReds), 1902 processes(). 1903 1904processes_bif_test() -> 1905 Tester = self(), 1906 enable_internal_state(), 1907 PBInfo = erts_debug:get_internal_state(processes_bif_info), 1908 print_processes_bif_info(PBInfo), 1909 WantReds = PBInfo#ptab_list_bif_info.min_start_reds + 10, 1910 WillTrap = case PBInfo of 1911 #ptab_list_bif_info{tab_chunks = Chunks} when Chunks < 10 -> 1912 false; %% Skip for small tables 1913 #ptab_list_bif_info{tab_chunks = Chunks, 1914 tab_chunks_size = ChunksSize, 1915 tab_indices_per_red = IndiciesPerRed 1916 } -> 1917 Chunks*ChunksSize >= IndiciesPerRed*WantReds 1918 end, 1919 Processes = fun () -> 1920 erts_debug:set_internal_state(reds_left,WantReds), 1921 processes() 1922 end, 1923 1924 ok = do_processes_bif_test(WantReds, WillTrap, Processes), 1925 1926 case WillTrap of 1927 false -> 1928 ok; 1929 true -> 1930 %% Do it again with a process suspended while 1931 %% in the processes/0 bif. 1932 erlang:system_flag(multi_scheduling, block_normal), 1933 Suspendee = spawn_link(fun () -> 1934 Tester ! {suspend_me, self()}, 1935 Tester ! {self(), 1936 done, 1937 hd(Processes())}, 1938 receive 1939 after infinity -> 1940 ok 1941 end 1942 end), 1943 receive {suspend_me, Suspendee} -> ok end, 1944 erlang:suspend_process(Suspendee), 1945 erlang:system_flag(multi_scheduling, unblock_normal), 1946 1947 [{status,suspended},{current_function,{erlang,ptab_list_continue,2}}] = 1948 process_info(Suspendee, [status, current_function]), 1949 1950 ok = do_processes_bif_test(WantReds, WillTrap, Processes), 1951 1952 erlang:resume_process(Suspendee), 1953 receive {Suspendee, done, _} -> ok end, 1954 unlink(Suspendee), 1955 exit(Suspendee, bang) 1956 end, 1957 case get(processes_bif_testcase_comment) of 1958 undefined -> ok; 1959 Comment -> {comment, Comment} 1960 end. 1961 1962do_processes_bif_test(WantReds, DieTest, Processes) -> 1963 Tester = self(), 1964 SpawnProcesses = fun (Prio) -> 1965 spawn_opt(?MODULE, do_processes, [WantReds], [link, {priority, Prio}]) 1966 end, 1967 Cleaner = spawn_link(fun () -> 1968 process_flag(trap_exit, true), 1969 Tester ! {cleaner_alive, self()}, 1970 processes_bif_cleaner() 1971 end), 1972 receive {cleaner_alive, Cleaner} -> ok end, 1973 try 1974 DoIt = make_ref(), 1975 GetGoing = make_ref(), 1976 {NoTestProcs, TestProcs} = spawn_initial_hangarounds(Cleaner), 1977 io:format("Testing with ~p processes~n", [NoTestProcs]), 1978 SpawnHangAround = fun () -> 1979 spawn(?MODULE, hangaround, [Cleaner, new_hangaround]) 1980 end, 1981 Killer = spawn_opt(fun () -> 1982 Splt = NoTestProcs div 10, 1983 {TP1, TP23} = lists:split(Splt, TestProcs), 1984 {TP2, TP3} = lists:split(Splt, TP23), 1985 erlang:system_flag(multi_scheduling, block_normal), 1986 Tester ! DoIt, 1987 receive GetGoing -> ok end, 1988 erlang:system_flag(multi_scheduling, unblock_normal), 1989 SpawnProcesses(high), 1990 lists:foreach( fun (P) -> 1991 SpawnHangAround(), 1992 exit(P, bang) 1993 end, TP1), 1994 SpawnProcesses(high), 1995 erlang:yield(), 1996 lists:foreach( fun (P) -> 1997 SpawnHangAround(), 1998 exit(P, bang) 1999 end, TP2), 2000 SpawnProcesses(high), 2001 lists:foreach( 2002 fun (P) -> 2003 SpawnHangAround(), 2004 exit(P, bang) 2005 end, TP3) 2006 end, [{priority, high}, link]), 2007 receive DoIt -> ok end, 2008 process_flag(priority, low), 2009 SpawnProcesses(low), 2010 erlang:yield(), 2011 process_flag(priority, normal), 2012 CorrectProcs0 = erts_debug:get_internal_state(processes), 2013 Killer ! GetGoing, 2014 erts_debug:set_internal_state(reds_left, WantReds), 2015 Procs0 = processes(), 2016 Procs = lists:sort(Procs0), 2017 CorrectProcs = lists:sort(CorrectProcs0), 2018 LengthCorrectProcs = length(CorrectProcs), 2019 io:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]), 2020 true = LengthCorrectProcs > NoTestProcs, 2021 case CorrectProcs =:= Procs of 2022 true -> 2023 ok; 2024 false -> 2025 processes_unexpected_result(CorrectProcs, Procs) 2026 end, 2027 unlink(Killer), 2028 exit(Killer, bang) 2029 after 2030 unlink(Cleaner), 2031 exit(Cleaner, kill), 2032 %% Wait for the system to recover to a normal state... 2033 wait_until_system_recover() 2034 end, 2035 do_processes_bif_die_test(DieTest, Processes), 2036 ok. 2037 2038 2039do_processes_bif_die_test(false, _Processes) -> 2040 io:format("Skipping test killing process executing processes/0~n",[]), 2041 ok; 2042do_processes_bif_die_test(true, Processes) -> 2043 do_processes_bif_die_test(5, Processes); 2044do_processes_bif_die_test(N, Processes) -> 2045 io:format("Doing test killing process executing processes/0~n",[]), 2046 try 2047 Tester = self(), 2048 Oooh_Nooooooo = make_ref(), 2049 {_, DieWhileDoingMon} = erlang:spawn_monitor( fun () -> 2050 Victim = self(), 2051 spawn_opt( 2052 fun () -> 2053 exit(Victim, got_him) 2054 end, 2055 [link, {priority, max}]), 2056 Tester ! {Oooh_Nooooooo, 2057 hd(Processes())}, 2058 exit(ohhhh_nooooo) 2059 end), 2060 receive 2061 {'DOWN', DieWhileDoingMon, _, _, Reason} -> 2062 case Reason of 2063 got_him -> ok; 2064 _ -> throw({kill_in_trap, Reason}) 2065 end 2066 end, 2067 receive 2068 {Oooh_Nooooooo, _} -> 2069 throw({kill_in_trap, 'Oooh_Nooooooo'}) 2070 after 0 -> 2071 ok 2072 end, 2073 PrcsCllrsSeqLen = 2*erlang:system_info(schedulers_online), 2074 PrcsCllrsSeq = lists:seq(1, PrcsCllrsSeqLen), 2075 ProcsCallers = lists:map( fun (_) -> 2076 spawn_link( 2077 fun () -> 2078 Tester ! hd(Processes()) 2079 end) 2080 end, PrcsCllrsSeq), 2081 erlang:yield(), 2082 {ProcsCallers1, ProcsCallers2} = lists:split(PrcsCllrsSeqLen div 2, 2083 ProcsCallers), 2084 process_flag(priority, high), 2085 lists:foreach( 2086 fun (P) -> 2087 unlink(P), 2088 exit(P, bang) 2089 end, 2090 lists:reverse(ProcsCallers2) ++ ProcsCallers1), 2091 process_flag(priority, normal), 2092 ok 2093 catch 2094 throw:{kill_in_trap, R} when N > 0 -> 2095 io:format("Failed to kill in trap: ~p~n", [R]), 2096 io:format("Trying again~n", []), 2097 do_processes_bif_die_test(N-1, Processes) 2098 end. 2099 2100 2101wait_until_system_recover() -> 2102 %% If system hasn't recovered after 10 seconds we give up 2103 Tmr = erlang:start_timer(10000, self(), no_more_wait), 2104 wait_until_system_recover(Tmr). 2105 2106wait_until_system_recover(Tmr) -> 2107 try 2108 lists:foreach(fun (P) when P == self() -> 2109 ok; 2110 (P) -> 2111 case process_info(P, initial_call) of 2112 {initial_call,{?MODULE, _, _}} -> 2113 throw(wait); 2114 {initial_call,{_, _, _}} -> 2115 ok; 2116 undefined -> 2117 ok 2118 end 2119 end, 2120 processes()) 2121 catch 2122 throw:wait -> 2123 receive 2124 {timeout, Tmr, _} -> 2125 Comment = "WARNING: Test processes still hanging around!", 2126 io:format("~s~n", [Comment]), 2127 put(processes_bif_testcase_comment, Comment), 2128 lists:foreach( 2129 fun (P) when P == self() -> 2130 ok; 2131 (P) -> 2132 case process_info(P, initial_call) of 2133 {initial_call,{?MODULE, _, _} = MFA} -> 2134 io:format("~p ~p~n", [P, MFA]); 2135 {initial_call,{_, _, _}} -> 2136 ok; 2137 undefined -> 2138 ok 2139 end 2140 end, 2141 processes()) 2142 after 100 -> 2143 wait_until_system_recover(Tmr) 2144 end 2145 end, 2146 erlang:cancel_timer(Tmr), 2147 receive {timeout, Tmr, _} -> ok after 0 -> ok end, 2148 ok. 2149 2150processes_last_call_trap(Config) when is_list(Config) -> 2151 enable_internal_state(), 2152 Processes = fun () -> processes() end, 2153 PBInfo = erts_debug:get_internal_state(processes_bif_info), 2154 print_processes_bif_info(PBInfo), 2155 WantReds = case PBInfo#ptab_list_bif_info.min_start_reds of 2156 R when R > 10 -> R - 1; 2157 _R -> 9 2158 end, 2159 lists:foreach(fun (_) -> 2160 erts_debug:set_internal_state(reds_left, 2161 WantReds), 2162 Processes(), 2163 erts_debug:set_internal_state(reds_left, 2164 WantReds), 2165 my_processes() 2166 end, 2167 lists:seq(1,100)). 2168 2169my_processes() -> 2170 processes(). 2171 2172processes_apply_trap(Config) when is_list(Config) -> 2173 enable_internal_state(), 2174 PBInfo = erts_debug:get_internal_state(processes_bif_info), 2175 print_processes_bif_info(PBInfo), 2176 WantReds = case PBInfo#ptab_list_bif_info.min_start_reds of 2177 R when R > 10 -> R - 1; 2178 _R -> 9 2179 end, 2180 lists:foreach(fun (_) -> 2181 erts_debug:set_internal_state(reds_left, 2182 WantReds), 2183 apply(erlang, processes, []) 2184 end, lists:seq(1,100)). 2185 2186processes_gc_trap(Config) when is_list(Config) -> 2187 Tester = self(), 2188 enable_internal_state(), 2189 PBInfo = erts_debug:get_internal_state(processes_bif_info), 2190 print_processes_bif_info(PBInfo), 2191 WantReds = PBInfo#ptab_list_bif_info.min_start_reds + 10, 2192 Processes = fun () -> 2193 erts_debug:set_internal_state(reds_left,WantReds), 2194 processes() 2195 end, 2196 2197 erlang:system_flag(multi_scheduling, block_normal), 2198 Suspendee = spawn_link(fun () -> 2199 Tester ! {suspend_me, self()}, 2200 Tester ! {self(), 2201 done, 2202 hd(Processes())}, 2203 receive after infinity -> ok end 2204 end), 2205 receive {suspend_me, Suspendee} -> ok end, 2206 erlang:suspend_process(Suspendee), 2207 erlang:system_flag(multi_scheduling, unblock_normal), 2208 2209 [{status,suspended}, {current_function,{erlang,ptab_list_continue,2}}] 2210 = process_info(Suspendee, [status, current_function]), 2211 2212 erlang:garbage_collect(Suspendee), 2213 erlang:garbage_collect(Suspendee), 2214 2215 erlang:resume_process(Suspendee), 2216 receive {Suspendee, done, _} -> ok end, 2217 erlang:garbage_collect(Suspendee), 2218 erlang:garbage_collect(Suspendee), 2219 2220 unlink(Suspendee), 2221 exit(Suspendee, bang), 2222 ok. 2223 2224process_flag_heap_size(Config) when is_list(Config) -> 2225 HSize = 2586, % must be gc fib+ number 2226 VHSize = 318187, % must be gc fib+ number 2227 OldHmin = erlang:process_flag(min_heap_size, HSize), 2228 {min_heap_size, HSize} = erlang:process_info(self(), min_heap_size), 2229 OldVHmin = erlang:process_flag(min_bin_vheap_size, VHSize), 2230 {min_bin_vheap_size, VHSize} = erlang:process_info(self(), min_bin_vheap_size), 2231 HSize = erlang:process_flag(min_heap_size, OldHmin), 2232 VHSize = erlang:process_flag(min_bin_vheap_size, OldVHmin), 2233 ok. 2234 2235spawn_opt_heap_size(Config) when is_list(Config) -> 2236 HSize = 987, % must be gc fib+ number 2237 VHSize = 46422, % must be gc fib+ number 2238 Pid = spawn_opt(fun () -> receive stop -> ok end end, 2239 [{min_heap_size, HSize},{ min_bin_vheap_size, VHSize}]), 2240 {min_heap_size, HSize} = process_info(Pid, min_heap_size), 2241 {min_bin_vheap_size, VHSize} = process_info(Pid, min_bin_vheap_size), 2242 Pid ! stop, 2243 ok. 2244 2245spawn_opt_max_heap_size(_Config) -> 2246 2247 error_logger:add_report_handler(?MODULE, self()), 2248 2249 %% flush any prior messages in error_logger 2250 Pid = spawn(fun() -> ok = nok end), 2251 receive 2252 {error, _, {emulator, _, [Pid|_]}} -> 2253 flush() 2254 end, 2255 2256 %% Test that numerical limit works 2257 max_heap_size_test(1024, 1024, true, true), 2258 2259 %% Test that map limit works 2260 max_heap_size_test(#{ size => 1024 }, 1024, true, true), 2261 2262 %% Test that no kill is sent 2263 max_heap_size_test(#{ size => 1024, kill => false }, 1024, false, true), 2264 2265 %% Test that no error_logger report is sent 2266 max_heap_size_test(#{ size => 1024, error_logger => false }, 1024, true, false), 2267 2268 %% Test that system_flag works 2269 erlang:system_flag(max_heap_size, #{ size => 0, kill => false, 2270 error_logger => true}), 2271 max_heap_size_test(#{ size => 1024 }, 1024, false, true), 2272 max_heap_size_test(#{ size => 1024, kill => true }, 1024, true, true), 2273 2274 erlang:system_flag(max_heap_size, #{ size => 0, kill => true, 2275 error_logger => false}), 2276 max_heap_size_test(#{ size => 1024 }, 1024, true, false), 2277 max_heap_size_test(#{ size => 1024, error_logger => true }, 1024, true, true), 2278 2279 erlang:system_flag(max_heap_size, #{ size => 1 bsl 20, kill => true, 2280 error_logger => true}), 2281 max_heap_size_test(#{ }, 1 bsl 20, true, true), 2282 2283 erlang:system_flag(max_heap_size, #{ size => 0, kill => true, 2284 error_logger => true}), 2285 2286 %% Test that ordinary case works as expected again 2287 max_heap_size_test(1024, 1024, true, true), 2288 2289 ok. 2290 2291max_heap_size_test(Option, Size, Kill, ErrorLogger) 2292 when map_size(Option) == 0 -> 2293 max_heap_size_test([], Size, Kill, ErrorLogger); 2294max_heap_size_test(Option, Size, Kill, ErrorLogger) 2295 when is_map(Option); is_integer(Option) -> 2296 max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger); 2297max_heap_size_test(Option, Size, Kill, ErrorLogger) -> 2298 OomFun = fun () -> oom_fun([]) end, 2299 Pid = spawn_opt(OomFun, Option), 2300 {max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size), 2301 ct:log("Default: ~p~nOption: ~p~nProc: ~p~n", 2302 [erlang:system_info(max_heap_size), Option, MHSz]), 2303 2304 #{ size := Size} = MHSz, 2305 2306 Ref = erlang:monitor(process, Pid), 2307 if Kill -> 2308 receive 2309 {'DOWN', Ref, process, Pid, killed} -> 2310 ok 2311 end; 2312 true -> 2313 ok 2314 end, 2315 if ErrorLogger -> 2316 receive 2317 %% There must be at least one error message. 2318 {error, _, {emulator, _, [Pid|_]}} -> 2319 ok 2320 end; 2321 true -> 2322 ok 2323 end, 2324 if not Kill -> 2325 exit(Pid, die), 2326 receive 2327 {'DOWN', Ref, process, Pid, die} -> 2328 ok 2329 end, 2330 %% If the process was not killed, the limit may have 2331 %% been reached more than once and there may be 2332 %% more {error, ...} messages left. 2333 receive_error_messages(Pid); 2334 true -> 2335 ok 2336 end, 2337 2338 %% Make sure that there are no unexpected messages. 2339 receive_unexpected(). 2340 2341oom_fun(Acc0) -> 2342 %% This is tail-recursive since the compiler is smart enough to figure 2343 %% out that a body-recursive variant never returns, and loops forever 2344 %% without keeping the list alive. 2345 timer:sleep(5), 2346 oom_fun([lists:seq(1, 1000) | Acc0]). 2347 2348receive_error_messages(Pid) -> 2349 receive 2350 {error, _, {emulator, _, [Pid|_]}} -> 2351 receive_error_messages(Pid) 2352 after 1000 -> 2353 ok 2354 end. 2355 2356receive_unexpected() -> 2357 receive 2358 {info_report, _, _} -> 2359 %% May be an alarm message from os_mon. Ignore. 2360 receive_unexpected(); 2361 M -> 2362 ct:fail({unexpected_message, M}) 2363 after 10 -> 2364 ok 2365 end. 2366 2367flush() -> 2368 receive 2369 _M -> flush() 2370 after 0 -> 2371 ok 2372 end. 2373 2374%% error_logger report handler proxy 2375init(Pid) -> 2376 {ok, Pid}. 2377 2378handle_event(Event, Pid) -> 2379 Pid ! Event, 2380 {ok, Pid}. 2381 2382huge_arglist_child(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, 2383 A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, 2384 A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, 2385 A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, 2386 A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, 2387 A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, 2388 A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, 2389 A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, 2390 A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, 2391 A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, 2392 A100, A101, A102, A103, A104, A105, A106, A107, A108, A109, 2393 A110, A111, A112, A113, A114, A115, A116, A117, A118, A119, 2394 A120, A121, A122, A123, A124, A125, A126, A127, A128, A129, 2395 A130, A131, A132, A133, A134, A135, A136, A137, A138, A139, 2396 A140, A141, A142, A143, A144, A145, A146, A147, A148, A149, 2397 A150, A151, A152, A153, A154, A155, A156, A157, A158, A159, 2398 A160, A161, A162, A163, A164, A165, A166, A167, A168, A169, 2399 A170, A171, A172, A173, A174, A175, A176, A177, A178, A179, 2400 A180, A181, A182, A183, A184, A185, A186, A187, A188, A189, 2401 A190, A191, A192, A193, A194, A195, A196, A197, A198, A199, 2402 A200, A201, A202, A203, A204, A205, A206, A207, A208, A209, 2403 A210, A211, A212, A213, A214, A215, A216, A217, A218, A219, 2404 A220, A221, A222, A223, A224, A225, A226, A227, A228, A229, 2405 A230, A231, A232, A233, A234, A235, A236, A237, A238, A239, 2406 A240, A241, A242, A243, A244, A245, A246, A247, A248, A249, 2407 A250, A251, A252, A253, A254) -> 2408 receive go -> ok end, 2409 exit([A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, 2410 A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, 2411 A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, 2412 A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, 2413 A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, 2414 A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, 2415 A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, 2416 A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, 2417 A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, 2418 A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, 2419 A100, A101, A102, A103, A104, A105, A106, A107, A108, A109, 2420 A110, A111, A112, A113, A114, A115, A116, A117, A118, A119, 2421 A120, A121, A122, A123, A124, A125, A126, A127, A128, A129, 2422 A130, A131, A132, A133, A134, A135, A136, A137, A138, A139, 2423 A140, A141, A142, A143, A144, A145, A146, A147, A148, A149, 2424 A150, A151, A152, A153, A154, A155, A156, A157, A158, A159, 2425 A160, A161, A162, A163, A164, A165, A166, A167, A168, A169, 2426 A170, A171, A172, A173, A174, A175, A176, A177, A178, A179, 2427 A180, A181, A182, A183, A184, A185, A186, A187, A188, A189, 2428 A190, A191, A192, A193, A194, A195, A196, A197, A198, A199, 2429 A200, A201, A202, A203, A204, A205, A206, A207, A208, A209, 2430 A210, A211, A212, A213, A214, A215, A216, A217, A218, A219, 2431 A220, A221, A222, A223, A224, A225, A226, A227, A228, A229, 2432 A230, A231, A232, A233, A234, A235, A236, A237, A238, A239, 2433 A240, A241, A242, A243, A244, A245, A246, A247, A248, A249, 2434 A250, A251, A252, A253, A254]). 2435 2436spawn_huge_arglist(Config) when is_list(Config) -> 2437 %% Huge in two different ways; encoded size and 2438 %% length... 2439 ArgListHead = [make_ref(), 2440 lists:duplicate(1000000, $a), 2441 <<1:8388608>>, 2442 processes(), 2443 erlang:ports(), 2444 {hej, hopp}, 2445 <<17:8388608>>, 2446 lists:duplicate(3000000, $x), 2447 #{ a => 1, b => 2, c => 3, d => 4, e => 5}], 2448 ArgList = ArgListHead ++ lists:seq(1, 255 - length(ArgListHead)), 2449 2450 io:format("size(term_to_binary(ArgList)) = ~p~n", 2451 [size(term_to_binary(ArgList))]), 2452 2453 io:format("Testing spawn with huge argument list on local node...~n", []), 2454 spawn_huge_arglist_test(true, node(), ArgList), 2455 io:format("Testing spawn with huge argument list on local node with Node...~n", []), 2456 spawn_huge_arglist_test(false, node(), ArgList), 2457 {ok, Node} = start_node(Config), 2458 _ = rpc:call(Node, ?MODULE, module_info, []), 2459 io:format("Testing spawn with huge argument list on remote node ~p...~n", [Node]), 2460 spawn_huge_arglist_test(false, Node, ArgList), 2461 stop_node(Node), 2462 ok. 2463 2464spawn_huge_arglist_test(Local, Node, ArgList) -> 2465 2466 R1 = case Local of 2467 true -> 2468 spawn_request(?MODULE, huge_arglist_child, ArgList, [monitor]); 2469 false -> 2470 spawn_request(Node, ?MODULE, huge_arglist_child, ArgList, [monitor]) 2471 end, 2472 receive 2473 {spawn_reply, R1, ok, Pid1} -> 2474 Pid1 ! go, 2475 receive 2476 {'DOWN', R1, process, Pid1, Reason1} -> 2477 ArgList = Reason1 2478 end 2479 end, 2480 2481 {Pid2, R2} = case Local of 2482 true -> 2483 spawn_monitor(?MODULE, huge_arglist_child, ArgList); 2484 false -> 2485 spawn_monitor(Node, ?MODULE, huge_arglist_child, ArgList) 2486 end, 2487 Node = node(Pid2), 2488 Pid2 ! go, 2489 receive 2490 {'DOWN', R2, process, Pid2, Reason2} -> 2491 ArgList = Reason2 2492 end, 2493 2494 {Pid3, R3} = case Local of 2495 true -> 2496 spawn_opt(?MODULE, huge_arglist_child, ArgList, [monitor]); 2497 false -> 2498 spawn_opt(Node, ?MODULE, huge_arglist_child, ArgList, [monitor]) 2499 end, 2500 Node = node(Pid3), 2501 Pid3 ! go, 2502 receive 2503 {'DOWN', R3, process, Pid3, Reason3} -> 2504 ArgList = Reason3 2505 end, 2506 2507 OldTA = process_flag(trap_exit, true), 2508 Pid4 = case Local of 2509 true -> 2510 spawn_link(?MODULE, huge_arglist_child, ArgList); 2511 false -> 2512 spawn_link(Node, ?MODULE, huge_arglist_child, ArgList) 2513 end, 2514 Node = node(Pid4), 2515 Pid4 ! go, 2516 receive 2517 {'EXIT', Pid4, Reason4} -> 2518 ArgList = Reason4 2519 end, 2520 2521 true = process_flag(trap_exit, OldTA), 2522 2523 Pid5 = case Local of 2524 true -> 2525 spawn(?MODULE, huge_arglist_child, ArgList); 2526 false -> 2527 spawn(Node, ?MODULE, huge_arglist_child, ArgList) 2528 end, 2529 Node = node(Pid5), 2530 R5 = erlang:monitor(process, Pid5), 2531 Pid5 ! go, 2532 receive 2533 {'DOWN', R5, process, Pid5, Reason5} -> 2534 ArgList = Reason5 2535 end, 2536 ok. 2537 2538spawn_request_bif(Config) when is_list(Config) -> 2539 io:format("Testing spawn_request() on local node...~n", []), 2540 spawn_request_bif_test(true, node()), 2541 io:format("Testing spawn_request() on local node with Node...~n", []), 2542 spawn_request_bif_test(false, node()), 2543 {ok, Node} = start_node(Config), 2544 io:format("Testing spawn_request() on remote node ~p...~n", [Node]), 2545 spawn_request_bif_test(false, Node), 2546 stop_node(Node), 2547 ok. 2548 2549spawn_request_bif_test(Local, Node) -> 2550 2551 Me = self(), 2552 2553 process_flag(trap_exit, true), 2554 2555 T1 = {test, 1}, 2556 F1 = fun () -> exit({exit, T1}) end, 2557 R1 = if Local -> 2558 spawn_request(F1, [{reply_tag, T1}, monitor, link]); 2559 true -> 2560 spawn_request(Node, F1, [{reply_tag, T1}, monitor, link]) 2561 end, 2562 receive 2563 {T1, R1, ok, P1} -> 2564 receive 2565 {'DOWN', R1, process, P1, {exit, T1}} -> 2566 ok 2567 end, 2568 receive 2569 {'EXIT', P1, {exit, T1}} -> 2570 ok 2571 end 2572 end, 2573 2574 R1b = if Local -> 2575 spawn_request(F1, [monitor, link]); 2576 true -> 2577 spawn_request(Node, F1, [monitor, link]) 2578 end, 2579 receive 2580 {spawn_reply, R1b, ok, P1b} -> 2581 receive 2582 {'DOWN', R1b, process, P1b, {exit, T1}} -> 2583 ok 2584 end, 2585 receive 2586 {'EXIT', P1b, {exit, T1}} -> 2587 ok 2588 end 2589 end, 2590 2591 Ref1c = make_ref(), 2592 F1c = fun () -> Me ! Ref1c end, 2593 R1c = if Local -> 2594 spawn_request(F1c); 2595 true -> 2596 spawn_request(Node, F1c) 2597 end, 2598 receive 2599 {spawn_reply, R1c, ok, _P1c} -> 2600 receive Ref1c -> ok end 2601 end, 2602 2603 R1e = if Local -> 2604 spawn_request(F1, [monitors, links, {reply_tag, T1}]); 2605 true -> 2606 spawn_request(Node, F1, [monitors, links, {reply_tag, T1}]) 2607 end, 2608 receive 2609 {T1, R1e, error, BadOpt1} -> 2610 badopt = BadOpt1, 2611 ok 2612 end, 2613 ok = try 2614 BadF = fun (X) -> exit({X,T1}) end, 2615 if Local -> 2616 spawn_request(BadF, [monitor, {reply_tag, T1}, link]); 2617 true -> 2618 spawn_request(Node, BadF, [monitor, {reply_tag, T1}, link]) 2619 end, 2620 nok 2621 catch 2622 error:badarg -> ok 2623 end, 2624 ok = try 2625 spawn_request(<<"node">>, F1, [monitor, link], T1), 2626 nok 2627 catch 2628 error:badarg -> ok 2629 end, 2630 2631 T2 = {test, 2}, 2632 M2 = erlang, 2633 F2 = exit, 2634 Reason2 = {exit, T2}, 2635 Args2 = [Reason2], 2636 R2 = if Local -> 2637 spawn_request(M2, F2, Args2, [monitor, link, {reply_tag, T2}]); 2638 true -> 2639 spawn_request(Node, M2, F2, Args2, [monitor, link, {reply_tag, T2}]) 2640 end, 2641 receive 2642 {T2, R2, ok, P2} -> 2643 receive 2644 {'DOWN', R2, process, P2, Reason2} -> 2645 ok 2646 end, 2647 receive 2648 {'EXIT', P2, Reason2} -> 2649 ok 2650 end 2651 end, 2652 2653 R2b = if Local -> 2654 spawn_request(M2, F2, Args2, [monitor, link]); 2655 true -> 2656 spawn_request(Node, M2, F2, Args2, [monitor, link]) 2657 end, 2658 receive 2659 {spawn_reply, R2b, ok, P2b} -> 2660 receive 2661 {'DOWN', R2b, process, P2b, Reason2} -> 2662 ok 2663 end, 2664 receive 2665 {'EXIT', P2b, Reason2} -> 2666 ok 2667 end 2668 end, 2669 2670 Ref2c = make_ref(), 2671 R2c = if Local -> 2672 spawn_request(erlang, send, [Me, Ref2c]); 2673 true -> 2674 spawn_request(Node, erlang, send, [Me, Ref2c]) 2675 end, 2676 receive 2677 {spawn_reply, R2c, ok, _P2c} -> 2678 receive Ref2c -> ok end 2679 end, 2680 2681 R2e = if Local -> 2682 spawn_request(M2, F2, Args2, [monitors, {reply_tag, T2}, links]); 2683 true -> 2684 spawn_request(Node, M2, F2, Args2, [monitors, {reply_tag, T2}, links]) 2685 end, 2686 receive 2687 {T2, R2e, error, BadOpt2} -> 2688 badopt = BadOpt2, 2689 ok 2690 end, 2691 2692 R2eb = if Local -> 2693 spawn_request(M2, F2, Args2, [monitors, links]); 2694 true -> 2695 spawn_request(Node, M2, F2, Args2, [monitors, links]) 2696 end, 2697 receive 2698 {spawn_reply, R2eb, error, BadOpt2b} -> 2699 badopt = BadOpt2b, 2700 ok 2701 end, 2702 2703 ok = try 2704 if Local -> 2705 spawn_request(M2, F2, [Args2|oops], [monitor, link, {reply_tag, T2}]); 2706 true -> 2707 spawn_request(Node, M2, F2, [Args2|oops], [monitor, link, {reply_tag, T2}]) 2708 end, 2709 nok 2710 catch 2711 error:badarg -> ok 2712 end, 2713 ok = try 2714 if Local -> 2715 spawn_request(M2, F2, [Args2|oops], [monitor, {reply_tag, blupp}, link]); 2716 true -> 2717 spawn_request(Node, M2, F2, [Args2|oops], [monitor, {reply_tag, blupp}, link]) 2718 end, 2719 nok 2720 catch 2721 error:badarg -> ok 2722 end, 2723 ok = try 2724 if Local -> 2725 spawn_request(M2, F2, [Args2|oops]); 2726 true -> 2727 spawn_request(Node, M2, F2, [Args2|oops]) 2728 end, 2729 nok 2730 catch 2731 error:badarg -> ok 2732 end, 2733 ok = try 2734 if Local -> 2735 spawn_request(M2, <<"exit">>, Args2, [monitor, {reply_tag, T2}, link]); 2736 true -> 2737 spawn_request(Node, M2, <<"exit">>, Args2, [monitor, {reply_tag, T2}, link]) 2738 end, 2739 nok 2740 catch 2741 error:badarg -> ok 2742 end, 2743 ok = try 2744 if Local -> 2745 spawn_request(M2, <<"exit">>, Args2, [monitor, link]); 2746 true -> 2747 spawn_request(Node, M2, <<"exit">>, Args2, [monitor, link]) 2748 end, 2749 nok 2750 catch 2751 error:badarg -> ok 2752 end, 2753 ok = try 2754 if Local -> 2755 spawn_request(M2, <<"exit">>, Args2); 2756 true -> 2757 spawn_request(Node, M2, <<"exit">>, Args2) 2758 end, 2759 nok 2760 catch 2761 error:badarg -> ok 2762 end, 2763 ok = try 2764 if Local -> 2765 spawn_request(<<"erlang">>, F2, Args2, [{reply_tag, T2}, monitor, link]); 2766 true -> 2767 spawn_request(Node, <<"erlang">>, F2, Args2, [{reply_tag, T2}, monitor, link]) 2768 end, 2769 nok 2770 catch 2771 error:badarg -> ok 2772 end, 2773 ok = try 2774 if Local -> 2775 spawn_request(<<"erlang">>, F2, Args2, [monitor, link]); 2776 true -> 2777 spawn_request(Node, <<"erlang">>, F2, Args2, [monitor, link]) 2778 end, 2779 nok 2780 catch 2781 error:badarg -> ok 2782 end, 2783 ok = try 2784 if Local -> 2785 spawn_request(<<"erlang">>, F2, Args2); 2786 true -> 2787 spawn_request(Node, <<"erlang">>, F2, Args2) 2788 end, 2789 nok 2790 catch 2791 error:badarg -> ok 2792 end, 2793 ok = try 2794 spawn_request(<<"node">>, M2, F2, Args2, [{reply_tag, T2}, monitor, link]), 2795 nok 2796 catch 2797 error:badarg -> ok 2798 end, 2799 ok = try 2800 spawn_request(<<"node">>, M2, F2, Args2, [monitor, link]), 2801 nok 2802 catch 2803 error:badarg -> ok 2804 end, 2805 ok = try 2806 spawn_request(<<"node">>, M2, F2, Args2), 2807 nok 2808 catch 2809 error:badarg -> ok 2810 end, 2811 ok. 2812 2813 2814spawn_request_monitor_demonitor(Config) when is_list(Config) -> 2815 {ok, Node} = start_node(Config), 2816 BlockFun = fun () -> 2817 erts_debug:set_internal_state(available_internal_state, true), 2818 erts_debug:set_internal_state(block, 1000), 2819 ok 2820 end, 2821 2822 %% Block receiver node... 2823 spawn_request(Node, BlockFun, [{priority,max}, link]), 2824 receive after 100 -> ok end, 2825 2826 erlang:display(spawning), 2827 erlang:yield(), 2828 R = spawn_request(Node, timer, sleep, [10000], [monitor]), 2829 %% Should not be possible to demonitor 2830 %% before operation has succeeded... 2831 erlang:display(premature_demonitor), 2832 {monitors, []} = process_info(self(), monitors), 2833 false = erlang:demonitor(R, [info]), %% Should be ignored by VM... 2834 erlang:display(wait_success), 2835 receive 2836 {spawn_reply, R, ok, P} -> 2837 erlang:display(demonitor), 2838 {monitors, [{process,P}]} = process_info(self(), monitors), 2839 true = erlang:demonitor(R, [info]), 2840 {monitors, []} = process_info(self(), monitors), 2841 exit(P, kill) 2842 end, 2843 erlang:display(done), 2844 stop_node(Node), 2845 ok. 2846 2847spawn_request_monitor_child_exit(Config) when is_list(Config) -> 2848 %% Early child exit... 2849 Tag = {a, tag}, 2850 R1 = spawn_request(nonexisting_module, nonexisting_function, [], [monitor, {reply_tag, Tag}]), 2851 receive 2852 {Tag, R1, ok, P1} -> 2853 receive 2854 {'DOWN', R1, process, P1, Reason1} -> 2855 {undef, _} = Reason1 2856 end 2857 end, 2858 {ok, Node} = start_node(Config), 2859 R2 = spawn_request(Node, nonexisting_module, nonexisting_function, [], [{reply_tag, Tag}, monitor]), 2860 receive 2861 {Tag, R2, ok, P2} -> 2862 receive 2863 {'DOWN', R2, process, P2, Reason2} -> 2864 {undef, _} = Reason2 2865 end 2866 end, 2867 stop_node(Node), 2868 ok. 2869 2870spawn_request_link_child_exit(Config) when is_list(Config) -> 2871 %% Early child exit... 2872 process_flag(trap_exit, true), 2873 Tag = {a, tag}, 2874 R1 = spawn_request(nonexisting_module, nonexisting_function, [], [{reply_tag, Tag}, link]), 2875 receive 2876 {Tag, R1, ok, P1} -> 2877 receive 2878 {'EXIT', P1, Reason1} -> 2879 {undef, _} = Reason1 2880 end 2881 end, 2882 {ok, Node} = start_node(Config), 2883 R2 = spawn_request(Node, nonexisting_module, nonexisting_function, [], [link, {reply_tag, Tag}]), 2884 receive 2885 {Tag, R2, ok, P2} -> 2886 receive 2887 {'EXIT', P2, Reason2} -> 2888 {undef, _} = Reason2 2889 end 2890 end, 2891 stop_node(Node), 2892 ok. 2893 2894spawn_request_link_parent_exit(Config) when is_list(Config) -> 2895 C1 = spawn_request_link_parent_exit_test(node()), 2896 {ok, Node} = start_node(Config), 2897 C2 = spawn_request_link_parent_exit_test(Node), 2898 stop_node(Node), 2899 {comment, C1 ++ " " ++ C2}. 2900 2901spawn_request_link_parent_exit_test(Node) -> 2902 %% Early parent exit... 2903 Tester = self(), 2904 2905 verify_nc(node()), 2906 2907 %% Ensure code loaded on other node... 2908 _ = rpc:call(Node, ?MODULE, module_info, []), 2909 2910 ChildFun = fun () -> 2911 Child = self(), 2912 spawn_opt(fun () -> 2913 process_flag(trap_exit, true), 2914 receive 2915 {'EXIT', Child, Reason} -> 2916 Tester ! {parent_exit, Reason} 2917 end 2918 end, [link,{priority,max}]), 2919 receive after infinity -> ok end 2920 end, 2921 ParentFun = case node() == Node of 2922 true -> 2923 fun (Wait) -> 2924 spawn_request(ChildFun, [link,{priority,max}]), 2925 receive after Wait -> ok end, 2926 exit(kaboom) 2927 end; 2928 false -> 2929 fun (Wait) -> 2930 spawn_request(Node, ChildFun, [link,{priority,max}]), 2931 receive after Wait -> ok end, 2932 exit(kaboom) 2933 end 2934 end, 2935 lists:foreach(fun (N) -> 2936 spawn(fun () -> ParentFun(N rem 10) end) 2937 end, 2938 lists:seq(1, 1000)), 2939 N = gather_parent_exits(kaboom, false), 2940 Comment = case node() == Node of 2941 true -> 2942 C = "Got " ++ integer_to_list(N) ++ " node local kabooms!", 2943 erlang:display(C), 2944 C; 2945 false -> 2946 C = "Got " ++ integer_to_list(N) ++ " node remote kabooms!", 2947 erlang:display(C), 2948 true = N /= 0, 2949 C 2950 end, 2951 Comment. 2952 2953spawn_request_abandon_bif(Config) when is_list(Config) -> 2954 {ok, Node} = start_node(Config), 2955 false = spawn_request_abandon(make_ref()), 2956 false = spawn_request_abandon(spawn_request(fun () -> ok end)), 2957 false = spawn_request_abandon(rpc:call(Node, erlang, make_ref, [])), 2958 try 2959 noreturn = spawn_request_abandon(self()) 2960 catch 2961 error:badarg -> 2962 ok 2963 end, 2964 try 2965 noreturn = spawn_request_abandon(4711) 2966 catch 2967 error:badarg -> 2968 ok 2969 end, 2970 2971 verify_nc(node()), 2972 2973 %% Ensure code loaded on other node... 2974 _ = rpc:call(Node, ?MODULE, module_info, []), 2975 2976 2977 TotOps = 1000, 2978 Tester = self(), 2979 2980 ChildFun = fun () -> 2981 Child = self(), 2982 spawn_opt(fun () -> 2983 process_flag(trap_exit, true), 2984 receive 2985 {'EXIT', Child, Reason} -> 2986 Tester ! {parent_exit, Reason} 2987 end 2988 end, [link,{priority,max}]), 2989 receive after infinity -> ok end 2990 end, 2991 ParentFun = fun (Wait, Opts) -> 2992 ReqId = spawn_request(Node, ChildFun, Opts), 2993 receive after Wait -> ok end, 2994 case spawn_request_abandon(ReqId) of 2995 true -> 2996 ok; 2997 false -> 2998 receive 2999 {spawn_reply, ReqId, error, _} -> 3000 exit(spawn_failed); 3001 {spawn_reply, ReqId, ok, Pid} -> 3002 unlink(Pid), 3003 exit(Pid, bye) 3004 after 3005 0 -> 3006 exit(missing_spawn_reply) 3007 end 3008 end 3009 end, 3010 %% Parent exit early... 3011 lists:foreach(fun (N) -> 3012 spawn_opt(fun () -> 3013 ParentFun(N rem 50, [link]) 3014 end, [link,{priority,max}]) 3015 end, 3016 lists:seq(1, TotOps)), 3017 NoA1 = gather_parent_exits(abandoned, true), 3018 %% Parent exit late... 3019 lists:foreach(fun (N) -> 3020 spawn_opt(fun () -> 3021 ParentFun(N rem 50, [link]), 3022 receive 3023 {spawn_reply, _, _, _} -> 3024 exit(unexpected_spawn_reply) 3025 after 3026 1000 -> ok 3027 end 3028 end, [link,{priority,max}]) 3029 end, 3030 lists:seq(1, TotOps)), 3031 NoA2 = gather_parent_exits(abandoned, true), 3032 %% Parent exit early... 3033 lists:foreach(fun (N) -> 3034 spawn_opt(fun () -> 3035 ParentFun(N rem 50, []) 3036 end, [link,{priority,max}]) 3037 end, 3038 lists:seq(1, TotOps)), 3039 0 = gather_parent_exits(abandoned, true), 3040 %% Parent exit late... 3041 lists:foreach(fun (N) -> 3042 spawn_opt(fun () -> 3043 ParentFun(N rem 50, []), 3044 receive 3045 {spawn_reply, _, _, _} -> 3046 exit(unexpected_spawn_reply) 3047 after 3048 1000 -> ok 3049 end 3050 end, [link,{priority,max}]) 3051 end, 3052 lists:seq(1, TotOps)), 3053 0 = gather_parent_exits(abandoned, true), 3054 stop_node(Node), 3055 C = "Got " ++ integer_to_list(NoA1) ++ " and " 3056 ++ integer_to_list(NoA2) ++ " abandoneds of 2*" 3057 ++ integer_to_list(TotOps) ++ " ops!", 3058 erlang:display(C), 3059 true = NoA1 /= 0, 3060 true = NoA1 /= TotOps, 3061 true = NoA2 /= 0, 3062 true = NoA2 /= TotOps, 3063 {comment, C}. 3064 3065gather_parent_exits(Reason, AllowOther) -> 3066 receive after 2000 -> ok end, 3067 gather_parent_exits(Reason, AllowOther, 0). 3068 3069gather_parent_exits(Reason, AllowOther, N) -> 3070 receive 3071 {parent_exit, Reason} -> 3072 gather_parent_exits(Reason, AllowOther, N+1); 3073 {parent_exit, _} = ParentExit -> 3074 case AllowOther of 3075 false -> 3076 ct:fail(ParentExit); 3077 true -> 3078 gather_parent_exits(Reason, AllowOther, N) 3079 end 3080 after 0 -> 3081 N 3082 end. 3083dist_spawn_monitor(Config) when is_list(Config) -> 3084 {ok, Node} = start_node(Config), 3085 R1 = spawn_request(Node, erlang, exit, [hej], [monitor]), 3086 receive 3087 {spawn_reply, R1, ok, P1} -> 3088 receive 3089 {'DOWN', R1, process, P1, Reason1} -> 3090 hej = Reason1 3091 end 3092 end, 3093 {P2, Mon2} = spawn_monitor(Node, erlang, exit, [hej]), 3094 receive 3095 {'DOWN', Mon2, process, P2, Reason2} -> 3096 hej = Reason2 3097 end, 3098 {P3, Mon3} = spawn_opt(Node, erlang, exit, [hej], [monitor]), 3099 receive 3100 {'DOWN', Mon3, process, P3, Reason3} -> 3101 hej = Reason3 3102 end, 3103 stop_node(Node), 3104 ok. 3105 3106spawn_old_node(Config) when is_list(Config) -> 3107 Cookie = atom_to_list(erlang:get_cookie()), 3108 Rel = "22_latest", 3109 case test_server:is_release_available(Rel) of 3110 false -> 3111 {skipped, "No OTP 22 available"}; 3112 true -> 3113 {ok, OldNode} = test_server:start_node(make_nodename(Config), 3114 peer, 3115 [{args, " -setcookie "++Cookie}, 3116 {erl, [{release, Rel}]}]), 3117 try 3118 %% Spawns triggering a new connection; which 3119 %% will trigger hopeful data transcoding 3120 %% of spawn requests... 3121 io:format("~n~nDoing initial connect tests...~n", []), 3122 spawn_old_node_test(OldNode, true), 3123 %% Spawns on an already existing connection... 3124 io:format("~n~nDoing already connected tests...~n", []), 3125 spawn_old_node_test(OldNode, false) 3126 after 3127 test_server:stop_node(OldNode) 3128 end, 3129 ok 3130 end. 3131 3132spawn_new_node(Config) when is_list(Config) -> 3133 Cookie = atom_to_list(erlang:get_cookie()), 3134 %% Test that the same operations as in spawn_old_node test 3135 %% works as expected on current OTP... 3136 {ok, CurrNode} = test_server:start_node(make_nodename(Config), 3137 peer, 3138 [{args, " -setcookie "++Cookie}]), 3139 try 3140 %% Spawns triggering a new connection; which 3141 %% will trigger hopeful data transcoding 3142 %% of spawn requests... 3143 io:format("~n~nDoing initial connect tests...~n", []), 3144 spawn_current_node_test(CurrNode, true), 3145 io:format("~n~nDoing already connected tests...~n", []), 3146 %% Spawns on an already existing connection... 3147 spawn_current_node_test(CurrNode, false) 3148 after 3149 test_server:stop_node(CurrNode) 3150 end. 3151 3152disconnect_node(Node, Disconnect) -> 3153 case Disconnect of 3154 false -> 3155 ok; 3156 true -> 3157 monitor_node(Node, true), 3158 erlang:disconnect_node(Node), 3159 receive {nodedown, Node} -> ok end 3160 end. 3161 3162spawn_old_node_test(Node, Disconnect) -> 3163 io:format("Testing spawn_request() on old node...", []), 3164 disconnect_node(Node, Disconnect), 3165 R1 = spawn_request(Node, erlang, exit, [hej], [monitor, {reply_tag, a_tag}]), 3166 receive 3167 {a_tag, R1, Err, Notsup} -> 3168 error = Err, 3169 notsup = Notsup, 3170 ok 3171 end, 3172 io:format("Testing spawn_monitor() on old node...", []), 3173 disconnect_node(Node, Disconnect), 3174 try 3175 spawn_monitor(Node, erlang, exit, [hej]) 3176 catch 3177 error:notsup -> 3178 ok 3179 end, 3180 io:format("Testing spawn_opt() with monitor on old node...", []), 3181 disconnect_node(Node, Disconnect), 3182 try 3183 spawn_opt(Node, erlang, exit, [hej], [monitor]) 3184 catch 3185 error:badarg -> 3186 ok 3187 end, 3188 io:format("Testing spawn_opt() with link on old node...", []), 3189 disconnect_node(Node, Disconnect), 3190 process_flag(trap_exit, true), 3191 P1 = spawn_opt(Node, erlang, exit, [hej], [link]), 3192 Node = node(P1), 3193 receive 3194 {'EXIT', P1, hej} -> 3195 ok 3196 end, 3197 io:format("Testing spawn_link() on old node...", []), 3198 disconnect_node(Node, Disconnect), 3199 P2 = spawn_link(Node, erlang, exit, [hej]), 3200 Node = node(P2), 3201 receive 3202 {'EXIT', P2, hej} -> 3203 ok 3204 end. 3205 3206spawn_current_node_test(Node, Disconnect) -> 3207 io:format("Testing spawn_request() on new node...", []), 3208 disconnect_node(Node, Disconnect), 3209 R1 = spawn_request(Node, erlang, exit, [hej], [monitor, {reply_tag, a_tag}]), 3210 receive 3211 {a_tag, R1, ok, P1} -> 3212 Node = node(P1), 3213 receive 3214 {'DOWN', R1, process, P1, hej} -> ok 3215 end 3216 end, 3217 io:format("Testing spawn_monitor() on new node...", []), 3218 disconnect_node(Node, Disconnect), 3219 {P2, M2} = spawn_monitor(Node, erlang, exit, [hej]), 3220 receive 3221 {'DOWN', M2, process, P2, hej} -> ok 3222 end, 3223 Node = node(P2), 3224 io:format("Testing spawn_opt() with monitor on new node...", []), 3225 disconnect_node(Node, Disconnect), 3226 {P3, M3} = spawn_opt(Node, erlang, exit, [hej], [monitor]), 3227 receive 3228 {'DOWN', M3, process, P3, hej} -> ok 3229 end, 3230 Node = node(P3), 3231 io:format("Testing spawn_opt() with link on new node...", []), 3232 disconnect_node(Node, Disconnect), 3233 process_flag(trap_exit, true), 3234 P4 = spawn_opt(Node, erlang, exit, [hej], [link]), 3235 Node = node(P4), 3236 receive 3237 {'EXIT', P4, hej} -> 3238 ok 3239 end, 3240 io:format("Testing spawn_link() on new node...", []), 3241 disconnect_node(Node, Disconnect), 3242 P5 = spawn_link(Node, erlang, exit, [hej]), 3243 Node = node(P5), 3244 receive 3245 {'EXIT', P5, hej} -> 3246 ok 3247 end. 3248 3249spawn_request_reply_option(Config) when is_list(Config) -> 3250 spawn_request_reply_option_test(node()), 3251 {ok, Node} = start_node(Config), 3252 spawn_request_reply_option_test(Node). 3253 3254spawn_request_reply_option_test(Node) -> 3255 io:format("Testing on node: ~p~n", [Node]), 3256 Parent = self(), 3257 Done1 = make_ref(), 3258 RID1 = spawn_request(Node, fun () -> Parent ! Done1 end, [{reply, yes}]), 3259 receive Done1 -> ok end, 3260 receive 3261 {spawn_reply, RID1, ok, _} -> ok 3262 after 0 -> 3263 ct:fail(missing_spawn_reply) 3264 end, 3265 Done2 = make_ref(), 3266 RID2 = spawn_request(Node, fun () -> Parent ! Done2 end, [{reply, success_only}]), 3267 receive Done2 -> ok end, 3268 receive 3269 {spawn_reply, RID2, ok, _} -> ok 3270 after 0 -> 3271 ct:fail(missing_spawn_reply) 3272 end, 3273 Done3 = make_ref(), 3274 RID3 = spawn_request(Node, fun () -> Parent ! Done3 end, [{reply, error_only}]), 3275 receive Done3 -> ok end, 3276 receive 3277 {spawn_reply, RID3, _, _} -> 3278 ct:fail(unexpected_spawn_reply) 3279 after 0 -> 3280 ok 3281 end, 3282 Done4 = make_ref(), 3283 RID4 = spawn_request(Node, fun () -> Parent ! Done4 end, [{reply, no}]), 3284 receive Done4 -> ok end, 3285 receive 3286 {spawn_reply, RID4, _, _} -> 3287 ct:fail(unexpected_spawn_reply) 3288 after 0 -> 3289 ok 3290 end, 3291 RID5 = spawn_request(Node, fun () -> ok end, [{reply, yes}, bad_option]), 3292 receive 3293 {spawn_reply, RID5, error, badopt} -> ok 3294 end, 3295 RID6 = spawn_request(Node, fun () -> ok end, [{reply, success_only}, bad_option]), 3296 receive 3297 {spawn_reply, RID6, error, badopt} -> ct:fail(unexpected_spawn_reply) 3298 after 1000 -> ok 3299 end, 3300 RID7 = spawn_request(Node, fun () -> ok end, [{reply, error_only}, bad_option]), 3301 receive 3302 {spawn_reply, RID7, error, badopt} -> ok 3303 end, 3304 RID8 = spawn_request(Node, fun () -> ok end, [{reply, no}, bad_option]), 3305 receive 3306 {spawn_reply, RID8, error, badopt} -> ct:fail(unexpected_spawn_reply) 3307 after 1000 -> ok 3308 end, 3309 RID8_1 = spawn_request(Node, fun () -> ok end, [{reply, nahh}]), 3310 receive 3311 {spawn_reply, RID8_1, error, badopt} -> ok 3312 end, 3313 case Node == node() of 3314 true -> 3315 ok; 3316 false -> 3317 stop_node(Node), 3318 RID9 = spawn_request(Node, fun () -> ok end, [{reply, yes}]), 3319 receive 3320 {spawn_reply, RID9, error, noconnection} -> ok 3321 end, 3322 RID10 = spawn_request(Node, fun () -> ok end, [{reply, success_only}]), 3323 receive 3324 {spawn_reply, RID10, error, noconnection} -> ct:fail(unexpected_spawn_reply) 3325 after 1000 -> ok 3326 end, 3327 RID11 = spawn_request(Node, fun () -> ok end, [{reply, error_only}]), 3328 receive 3329 {spawn_reply, RID11, error, noconnection} -> ok 3330 end, 3331 RID12 = spawn_request(Node, fun () -> ok end, [{reply, no}]), 3332 receive 3333 {spawn_reply, RID12, error, noconnection} -> ct:fail(unexpected_spawn_reply) 3334 after 1000 -> ok 3335 end, 3336 ok 3337 end. 3338 3339processes_term_proc_list(Config) when is_list(Config) -> 3340 Tester = self(), 3341 3342 Run = fun(Args) -> 3343 {ok, Node} = start_node(Config, Args), 3344 RT = spawn_link(Node, fun () -> 3345 receive after 1000 -> ok end, 3346 as_expected = processes_term_proc_list_test(false), 3347 Tester ! {it_worked, self()} 3348 end), 3349 receive {it_worked, RT} -> ok end, 3350 stop_node(Node) 3351 end, 3352 3353 %% We have to run this test case with +S1 since instrument:allocations() 3354 %% will report a free()'d block as present until it's actually deallocated 3355 %% by its employer. 3356 Run("+MSe true +Muatags false +S1"), 3357 Run("+MSe true +Muatags true +S1"), 3358 3359 ok. 3360 3361-define(CHK_TERM_PROC_LIST(MC, XB), 3362 chk_term_proc_list(?LINE, MC, XB)). 3363 3364chk_term_proc_list(Line, MustChk, ExpectBlks) -> 3365 Allocs = instrument:allocations(), 3366 case {MustChk, Allocs} of 3367 {false, {error, not_enabled}} -> 3368 not_enabled; 3369 {false, {ok, {_Shift, _Unscanned, ByOrigin}}} when ByOrigin =:= #{} -> 3370 not_enabled; 3371 {_, {ok, {_Shift, _Unscanned, ByOrigin}}} -> 3372 ByType = maps:get(system, ByOrigin, #{}), 3373 Hist = maps:get(ptab_list_deleted_el, ByType, {}), 3374 case lists:sum(tuple_to_list(Hist)) of 3375 ExpectBlks -> 3376 ok; 3377 Blks -> 3378 exit({line, Line, mismatch, 3379 expected, ExpectBlks, 3380 actual, Blks}) 3381 end 3382 end, 3383 ok. 3384 3385processes_term_proc_list_test(MustChk) -> 3386 Tester = self(), 3387 enable_internal_state(), 3388 PBInfo = erts_debug:get_internal_state(processes_bif_info), 3389 print_processes_bif_info(PBInfo), 3390 WantReds = PBInfo#ptab_list_bif_info.min_start_reds + 10, 3391 #ptab_list_bif_info{tab_chunks = Chunks, 3392 tab_chunks_size = ChunksSize, 3393 tab_indices_per_red = IndiciesPerRed 3394 } = PBInfo, 3395 true = Chunks > 1, 3396 true = Chunks*ChunksSize >= IndiciesPerRed*WantReds, 3397 Processes = fun () -> 3398 erts_debug:set_internal_state(reds_left, 3399 WantReds), 3400 processes() 3401 end, 3402 Exit = fun (P) -> 3403 unlink(P), 3404 exit(P, bang), 3405 wait_until( 3406 fun () -> 3407 not lists:member( 3408 P, 3409 erts_debug:get_internal_state( 3410 processes)) 3411 end) 3412 end, 3413 SpawnSuspendProcessesProc = fun () -> 3414 erlang:system_flag(multi_scheduling, block_normal), 3415 P = spawn_link(fun () -> 3416 Tester ! {suspend_me, self()}, 3417 Tester ! {self(), 3418 done, 3419 hd(Processes())}, 3420 receive after infinity -> ok end 3421 end), 3422 receive {suspend_me, P} -> ok end, 3423 erlang:suspend_process(P), 3424 erlang:system_flag(multi_scheduling, unblock_normal), 3425 [{status,suspended}, 3426 {current_function,{erlang,ptab_list_continue,2}}] 3427 = process_info(P, [status, current_function]), 3428 P 3429 end, 3430 ResumeProcessesProc = fun (P) -> 3431 erlang:resume_process(P), 3432 receive {P, done, _} -> ok end 3433 end, 3434 ?CHK_TERM_PROC_LIST(MustChk, 0), 3435 HangAround = fun () -> receive after infinity -> ok end end, 3436 HA1 = spawn_link(HangAround), 3437 HA2 = spawn_link(HangAround), 3438 HA3 = spawn_link(HangAround), 3439 S1 = SpawnSuspendProcessesProc(), 3440 ?CHK_TERM_PROC_LIST(MustChk, 1), 3441 Exit(HA1), 3442 ?CHK_TERM_PROC_LIST(MustChk, 2), 3443 S2 = SpawnSuspendProcessesProc(), 3444 ?CHK_TERM_PROC_LIST(MustChk, 3), 3445 S3 = SpawnSuspendProcessesProc(), 3446 ?CHK_TERM_PROC_LIST(MustChk, 4), 3447 Exit(HA2), 3448 ?CHK_TERM_PROC_LIST(MustChk, 5), 3449 S4 = SpawnSuspendProcessesProc(), 3450 ?CHK_TERM_PROC_LIST(MustChk, 6), 3451 Exit(HA3), 3452 ?CHK_TERM_PROC_LIST(MustChk, 7), 3453 ResumeProcessesProc(S1), 3454 ?CHK_TERM_PROC_LIST(MustChk, 5), 3455 ResumeProcessesProc(S3), 3456 ?CHK_TERM_PROC_LIST(MustChk, 4), 3457 ResumeProcessesProc(S4), 3458 ?CHK_TERM_PROC_LIST(MustChk, 3), 3459 ResumeProcessesProc(S2), 3460 ?CHK_TERM_PROC_LIST(MustChk, 0), 3461 Exit(S1), 3462 Exit(S2), 3463 Exit(S3), 3464 Exit(S4), 3465 3466 3467 HA4 = spawn_link(HangAround), 3468 HA5 = spawn_link(HangAround), 3469 HA6 = spawn_link(HangAround), 3470 S5 = SpawnSuspendProcessesProc(), 3471 ?CHK_TERM_PROC_LIST(MustChk, 1), 3472 Exit(HA4), 3473 ?CHK_TERM_PROC_LIST(MustChk, 2), 3474 S6 = SpawnSuspendProcessesProc(), 3475 ?CHK_TERM_PROC_LIST(MustChk, 3), 3476 Exit(HA5), 3477 ?CHK_TERM_PROC_LIST(MustChk, 4), 3478 S7 = SpawnSuspendProcessesProc(), 3479 ?CHK_TERM_PROC_LIST(MustChk, 5), 3480 Exit(HA6), 3481 ?CHK_TERM_PROC_LIST(MustChk, 6), 3482 S8 = SpawnSuspendProcessesProc(), 3483 ?CHK_TERM_PROC_LIST(MustChk, 7), 3484 3485 erlang:system_flag(multi_scheduling, block_normal), 3486 Exit(S8), 3487 ?CHK_TERM_PROC_LIST(MustChk, 7), 3488 Exit(S5), 3489 ?CHK_TERM_PROC_LIST(MustChk, 6), 3490 Exit(S7), 3491 ?CHK_TERM_PROC_LIST(MustChk, 6), 3492 Exit(S6), 3493 ?CHK_TERM_PROC_LIST(MustChk, 0), 3494 erlang:system_flag(multi_scheduling, unblock_normal), 3495 as_expected. 3496 3497 3498otp_7738_waiting(Config) when is_list(Config) -> 3499 otp_7738_test(waiting). 3500 3501otp_7738_suspended(Config) when is_list(Config) -> 3502 otp_7738_test(suspended). 3503 3504otp_7738_resume(Config) when is_list(Config) -> 3505 otp_7738_test(resume). 3506 3507otp_7738_test(Type) -> 3508 sys_mem_cond_run(3072, fun () -> do_otp_7738_test(Type) end). 3509 3510do_otp_7738_test(Type) -> 3511 T = self(), 3512 S = spawn_link(fun () -> 3513 receive 3514 {suspend, Suspendee} -> 3515 erlang:suspend_process(Suspendee), 3516 T ! {suspended, Suspendee}, 3517 receive 3518 after 10 -> 3519 erlang:resume_process(Suspendee), 3520 Suspendee ! wake_up 3521 end; 3522 {send, To, Msg} -> 3523 receive after 10 -> ok end, 3524 To ! Msg 3525 end 3526 end), 3527 R = spawn_link(fun () -> 3528 X = lists:seq(1, 20000000), 3529 T ! {initialized, self()}, 3530 case Type of 3531 _ when Type == suspended; 3532 Type == waiting -> 3533 receive _ -> ok end; 3534 _ when Type == resume -> 3535 Receive = fun (F) -> 3536 receive 3537 _ -> 3538 ok 3539 after 0 -> 3540 F(F) 3541 end 3542 end, 3543 Receive(Receive) 3544 end, 3545 T ! {woke_up, self()}, 3546 id(X) 3547 end), 3548 receive {initialized, R} -> ok end, 3549 receive after 10 -> ok end, 3550 case Type of 3551 suspended -> 3552 erlang:suspend_process(R), 3553 S ! {send, R, wake_up}; 3554 waiting -> 3555 S ! {send, R, wake_up}; 3556 resume -> 3557 S ! {suspend, R}, 3558 receive {suspended, R} -> ok end 3559 end, 3560 erlang:garbage_collect(R), 3561 case Type of 3562 suspended -> 3563 erlang:resume_process(R); 3564 _ -> 3565 ok 3566 end, 3567 receive 3568 {woke_up, R} -> 3569 ok 3570 after 2000 -> 3571 I = process_info(R, [status, message_queue_len]), 3572 io:format("~p~n", [I]), 3573 ct:fail(no_progress) 3574 end, 3575 ok. 3576 3577gor(Reds, Stop) -> 3578 receive 3579 drop_me -> 3580 gor(Reds+1, Stop); 3581 {From, reds} -> 3582 From ! {reds, Reds, self()}, 3583 gor(Reds+1, Stop); 3584 {From, Stop} -> 3585 From ! {stopped, Stop, Reds, self()} 3586 after 0 -> 3587 gor(Reds+1, Stop) 3588 end. 3589 3590garb_other_running(Config) when is_list(Config) -> 3591 Stop = make_ref(), 3592 {Pid, Mon} = spawn_monitor(fun () -> gor(0, Stop) end), 3593 Reds = lists:foldl(fun (N, OldReds) -> 3594 case N rem 2 of 3595 0 -> Pid ! drop_me; 3596 _ -> ok 3597 end, 3598 erlang:garbage_collect(Pid), 3599 receive after 1 -> ok end, 3600 Pid ! {self(), reds}, 3601 receive 3602 {reds, NewReds, Pid} -> 3603 true = (NewReds > OldReds), 3604 NewReds 3605 end 3606 end, 3607 0, 3608 lists:seq(1, 10000)), 3609 receive after 1 -> ok end, 3610 Pid ! {self(), Stop}, 3611 receive 3612 {stopped, Stop, StopReds, Pid} -> 3613 true = (StopReds > Reds) 3614 end, 3615 receive {'DOWN', Mon, process, Pid, normal} -> ok end, 3616 ok. 3617 3618no_priority_inversion(Config) when is_list(Config) -> 3619 Prio = process_flag(priority, max), 3620 Master = self(), 3621 Executing = make_ref(), 3622 HTLs = lists:map(fun (Sched) -> 3623 spawn_opt(fun () -> 3624 Master ! {self(), Executing}, 3625 tok_loop() 3626 end, 3627 [{priority, high}, 3628 {scheduler, Sched}, 3629 monitor, 3630 link]) 3631 end, 3632 lists:seq(1, erlang:system_info(schedulers_online))), 3633 lists:foreach(fun ({P, _}) -> receive {P,Executing} -> ok end end, HTLs), 3634 LTL = spawn_opt(fun () -> 3635 tok_loop() 3636 end, 3637 [{priority, low}, monitor, link]), 3638 false = erlang:check_process_code(element(1, LTL), nonexisting_module), 3639 true = erlang:garbage_collect(element(1, LTL)), 3640 lists:foreach(fun ({P, _}) -> 3641 unlink(P), 3642 exit(P, kill) 3643 end, [LTL | HTLs]), 3644 lists:foreach(fun ({P, M}) -> 3645 receive 3646 {'DOWN', M, process, P, killed} -> 3647 ok 3648 end 3649 end, [LTL | HTLs]), 3650 process_flag(priority, Prio), 3651 ok. 3652 3653no_priority_inversion2(Config) when is_list(Config) -> 3654 Prio = process_flag(priority, max), 3655 Master = self(), 3656 Executing = make_ref(), 3657 MTLs = lists:map(fun (Sched) -> 3658 spawn_opt(fun () -> 3659 Master ! {self(), Executing}, 3660 tok_loop() 3661 end, 3662 [{priority, max}, 3663 {scheduler, Sched}, 3664 monitor, link]) 3665 end, 3666 lists:seq(1, erlang:system_info(schedulers_online))), 3667 lists:foreach(fun ({P, _}) -> receive {P,Executing} -> ok end end, MTLs), 3668 {PL, ML} = spawn_opt(fun () -> 3669 tok_loop() 3670 end, 3671 [{priority, low}, monitor, link]), 3672 RL = request_test(PL, low), 3673 RN = request_test(PL, normal), 3674 RH = request_test(PL, high), 3675 receive 3676 {system_task_test, _, _} -> 3677 ct:fail(unexpected_system_task_completed) 3678 after 1000 -> 3679 ok 3680 end, 3681 RM = request_test(PL, max), 3682 receive 3683 {system_task_test, RM, true} -> 3684 ok 3685 end, 3686 lists:foreach(fun ({P, _}) -> 3687 unlink(P), 3688 exit(P, kill) 3689 end, MTLs), 3690 lists:foreach(fun ({P, M}) -> 3691 receive 3692 {'DOWN', M, process, P, killed} -> 3693 ok 3694 end 3695 end, MTLs), 3696 receive 3697 {system_task_test, RH, true} -> 3698 ok 3699 end, 3700 receive 3701 {system_task_test, RN, true} -> 3702 ok 3703 end, 3704 receive 3705 {system_task_test, RL, true} -> 3706 ok 3707 end, 3708 unlink(PL), 3709 exit(PL, kill), 3710 receive 3711 {'DOWN', ML, process, PL, killed} -> 3712 ok 3713 end, 3714 process_flag(priority, Prio), 3715 ok. 3716 3717request_test(Pid, Prio) -> 3718 Ref = make_ref(), 3719 erts_internal:request_system_task(Pid, Prio, {system_task_test, Ref}), 3720 Ref. 3721 3722system_task_blast(Config) when is_list(Config) -> 3723 Me = self(), 3724 GCReq = fun () -> 3725 RL = test_req(Me, 100), 3726 lists:foreach(fun (R) -> 3727 receive 3728 {system_task_test, R, true} -> 3729 ok 3730 end 3731 end, RL), 3732 exit(it_worked) 3733 end, 3734 HTLs = lists:map(fun (_) -> spawn_monitor(GCReq) end, lists:seq(1, 1000)), 3735 lists:foreach(fun ({P, M}) -> 3736 receive 3737 {'DOWN', M, process, P, it_worked} -> 3738 ok 3739 end 3740 end, HTLs), 3741 ok. 3742 3743test_req(_Pid, 0) -> 3744 []; 3745test_req(Pid, N) -> 3746 R0 = request_test(Pid, low), 3747 R1 = request_test(Pid, normal), 3748 R2 = request_test(Pid, high), 3749 R3 = request_test(Pid, max), 3750 [R0, R1, R2, R3 | test_req(Pid, N-1)]. 3751 3752system_task_on_suspended(Config) when is_list(Config) -> 3753 {P, M} = spawn_monitor(fun () -> 3754 tok_loop() 3755 end), 3756 true = erlang:suspend_process(P), 3757 {status, suspended} = process_info(P, status), 3758 true = erlang:garbage_collect(P), 3759 {status, suspended} = process_info(P, status), 3760 true = erlang:resume_process(P), 3761 false = ({status, suspended} == process_info(P, status)), 3762 exit(P, kill), 3763 receive 3764 {'DOWN', M, process, P, killed} -> 3765 ok 3766 end. 3767 3768%% When a system task couldn't be enqueued due to the process being in an 3769%% incompatible state, it would linger in the system task list and get executed 3770%% anyway the next time the process was scheduled. This would result in a 3771%% double-free at best. 3772%% 3773%% This test continuously purges modules while other processes run dirty code, 3774%% which will provoke this error as ERTS_PSTT_CPC can't be enqueued while a 3775%% process is running dirty code. 3776system_task_failed_enqueue(Config) when is_list(Config) -> 3777 case erlang:system_info(dirty_cpu_schedulers) of 3778 N when N > 0 -> 3779 system_task_failed_enqueue_1(Config); 3780 _ -> 3781 {skipped, "No dirty scheduler support"} 3782 end. 3783 3784system_task_failed_enqueue_1(Config) -> 3785 Priv = proplists:get_value(priv_dir, Config), 3786 3787 Purgers = [spawn_link(fun() -> purge_loop(Priv, Id) end) 3788 || Id <- lists:seq(1, erlang:system_info(schedulers))], 3789 Hogs = [spawn_link(fun() -> dirty_loop() end) 3790 || _ <- lists:seq(1, erlang:system_info(dirty_cpu_schedulers))], 3791 3792 ct:sleep(5000), 3793 3794 [begin 3795 unlink(Pid), 3796 exit(Pid, kill) 3797 end || Pid <- (Purgers ++ Hogs)], 3798 3799 ok. 3800 3801purge_loop(PrivDir, Id) -> 3802 Mod = "failed_enq_" ++ integer_to_list(Id), 3803 Path = PrivDir ++ "/" ++ Mod, 3804 file:write_file(Path ++ ".erl", 3805 "-module('" ++ Mod ++ "').\n" ++ 3806 "-export([t/0]).\n" ++ 3807 "t() -> ok."), 3808 purge_loop_1(Path). 3809purge_loop_1(Path) -> 3810 {ok, Mod} = compile:file(Path, []), 3811 erlang:delete_module(Mod), 3812 erts_code_purger:purge(Mod), 3813 purge_loop_1(Path). 3814 3815dirty_loop() -> 3816 ok = erts_debug:dirty_cpu(reschedule, 10000), 3817 dirty_loop(). 3818 3819gc_request_when_gc_disabled(Config) when is_list(Config) -> 3820 AIS = erts_debug:set_internal_state(available_internal_state, true), 3821 gc_request_when_gc_disabled_do(ref), 3822 gc_request_when_gc_disabled_do(immed), 3823 erts_debug:set_internal_state(available_internal_state, AIS). 3824 3825gc_request_when_gc_disabled_do(ReqIdType) -> 3826 Master = self(), 3827 {P, M} = spawn_opt(fun () -> 3828 true = erts_debug:set_internal_state(gc_state, 3829 false), 3830 Master ! {self(), gc_state, false}, 3831 receive after 1000 -> ok end, 3832 Master ! {self(), gc_state, true}, 3833 false = erts_debug:set_internal_state(gc_state, 3834 true), 3835 receive after 100 -> ok end 3836 end, [monitor, link]), 3837 receive {P, gc_state, false} -> ok end, 3838 ReqId = case ReqIdType of 3839 ref -> make_ref(); 3840 immed -> immed 3841 end, 3842 async = garbage_collect(P, [{async, ReqId}]), 3843 receive 3844 {garbage_collect, ReqId, Result} -> 3845 ct:fail({unexpected_gc, Result}); 3846 {P, gc_state, true} -> 3847 ok 3848 end, 3849 receive {garbage_collect, ReqId, true} -> ok end, 3850 receive {'DOWN', M, process, P, _Reason} -> ok end, 3851 ok. 3852 3853gc_request_blast_when_gc_disabled(Config) when is_list(Config) -> 3854 Master = self(), 3855 AIS = erts_debug:set_internal_state(available_internal_state, true), 3856 {P, M} = spawn_opt(fun () -> 3857 true = erts_debug:set_internal_state(gc_state, 3858 false), 3859 Master ! {self(), gc_state, false}, 3860 receive after 1000 -> ok end, 3861 false = erts_debug:set_internal_state(gc_state, 3862 true), 3863 receive after 100 -> ok end 3864 end, [monitor, link]), 3865 receive {P, gc_state, false} -> ok end, 3866 PMs = lists:map(fun (N) -> 3867 Prio = case N rem 4 of 3868 0 -> max; 3869 1 -> high; 3870 2 -> normal; 3871 3 -> low 3872 end, 3873 spawn_opt(fun () -> 3874 erlang:garbage_collect(P) 3875 end, [monitor, link, {priority, Prio}]) 3876 end, lists:seq(1, 10000)), 3877 lists:foreach(fun ({Proc, Mon}) -> 3878 receive 3879 {'DOWN', Mon, process, Proc, normal} -> 3880 ok 3881 end 3882 end, 3883 PMs), 3884 erts_debug:set_internal_state(available_internal_state, AIS), 3885 receive {'DOWN', M, process, P, _Reason} -> ok end, 3886 ok. 3887 3888otp_16436(Config) when is_list(Config) -> 3889 P = spawn_opt(fun () -> 3890 erts_debug:dirty_io(wait, 1000) 3891 end, 3892 [{priority,high},link]), 3893 erlang:check_process_code(P, non_existing), 3894 unlink(P), 3895 exit(P, kill), 3896 ok. 3897 3898otp_16642(Config) when is_list(Config) -> 3899 %% 3900 %% Whitebox testing... 3901 %% 3902 %% Ensure that low prio system tasks are interleaved with 3903 %% normal prio system tasks as they should. 3904 %% 3905 process_flag(priority, high), 3906 process_flag(scheduler, 1), 3907 Pid = spawn_opt(fun () -> receive after infinity -> ok end end, 3908 [link, {scheduler, 1}]), 3909 ReqSTasks = fun (Prio, Start, Stop) -> 3910 lists:foreach( 3911 fun (N) -> 3912 erts_internal:request_system_task( 3913 Pid, 3914 Prio, 3915 {system_task_test, 3916 {Prio, N}}) 3917 end, 3918 lists:seq(Start, Stop)) 3919 end, 3920 MkResList = fun (Prio, Start, Stop) -> 3921 lists:map(fun (N) -> 3922 {system_task_test, 3923 {Prio, N}, 3924 true} 3925 end, 3926 lists:seq(Start, Stop)) 3927 end, 3928 3929 %%% Test when normal queue clears first... 3930 3931 ReqSTasks(low, 0, 1), 3932 ReqSTasks(normal, 0, 10), 3933 ReqSTasks(low, 2, 4), 3934 ReqSTasks(normal, 11, 26), 3935 3936 Msgs1 = recv_msgs(32), 3937 io:format("Got test 1 messages: ~p~n", [Msgs1]), 3938 3939 ExpMsgs1 = 3940 MkResList(normal, 0, 7) 3941 ++ MkResList(low, 0, 0) 3942 ++ MkResList(normal, 8, 15) 3943 ++ MkResList(low, 1, 1) 3944 ++ MkResList(normal, 16, 23) 3945 ++ MkResList(low, 2, 2) 3946 ++ MkResList(normal, 24, 26) 3947 ++ MkResList(low, 3, 4), 3948 3949 case Msgs1 =:= ExpMsgs1 of 3950 true -> 3951 ok; 3952 false -> 3953 io:format("Expected test 1 messages ~p~n", 3954 [ExpMsgs1]), 3955 ct:fail(unexpected_messages) 3956 end, 3957 3958 receive Unexp1 -> ct:fail({unexpected_message, Unexp1}) 3959 after 500 -> ok 3960 end, 3961 3962 io:format("Test 1 as expected~n", []), 3963 3964 %%% Test when low queue clears first... 3965 3966 ReqSTasks(low, 0, 1), 3967 ReqSTasks(normal, 0, 20), 3968 3969 Msgs2 = recv_msgs(23), 3970 io:format("Got test 2 messages: ~p~n", [Msgs2]), 3971 3972 ExpMsgs2 = 3973 MkResList(normal, 0, 7) 3974 ++ MkResList(low, 0, 0) 3975 ++ MkResList(normal, 8, 15) 3976 ++ MkResList(low, 1, 1) 3977 ++ MkResList(normal, 16, 20), 3978 3979 case Msgs2 =:= ExpMsgs2 of 3980 true -> 3981 ok; 3982 false -> 3983 io:format("Expected test 2 messages ~p~n", 3984 [ExpMsgs2]), 3985 ct:fail(unexpected_messages) 3986 end, 3987 3988 receive Unexp2 -> ct:fail({unexpected_message, Unexp2}) 3989 after 500 -> ok 3990 end, 3991 3992 io:format("Test 2 as expected~n", []), 3993 3994 unlink(Pid), 3995 exit(Pid, kill), 3996 false = is_process_alive(Pid), 3997 ok. 3998 3999%% Internal functions 4000 4001recv_msgs(N) -> 4002 recv_msgs(N, []). 4003 4004recv_msgs(0, Msgs) -> 4005 lists:reverse(Msgs); 4006recv_msgs(N, Msgs) -> 4007 receive 4008 Msg -> 4009 recv_msgs(N-1, [Msg|Msgs]) 4010 end. 4011 4012wait_until(Fun) -> 4013 case Fun() of 4014 true -> true; 4015 false -> receive after 10 -> wait_until(Fun) end 4016 end. 4017 4018tok_loop() -> 4019 tok_loop(hej). 4020 4021tok_loop(hej) -> 4022 tok_loop(hopp); 4023tok_loop(hopp) -> 4024 tok_loop(hej). 4025 4026id(I) -> I. 4027 4028make_nodename(Config) when is_list(Config) -> 4029 list_to_atom(atom_to_list(?MODULE) 4030 ++ "-" 4031 ++ atom_to_list(proplists:get_value(testcase, Config)) 4032 ++ "-" 4033 ++ integer_to_list(erlang:system_time(second)) 4034 ++ "-" 4035 ++ integer_to_list(erlang:unique_integer([positive]))). 4036 4037start_node(Config) -> 4038 start_node(Config, ""). 4039 4040start_node(Config, Args) when is_list(Config) -> 4041 Pa = filename:dirname(code:which(?MODULE)), 4042 Name = make_nodename(Config), 4043 test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). 4044 4045stop_node(Node) -> 4046 verify_nc(node()), 4047 verify_nc(Node), 4048 test_server:stop_node(Node). 4049 4050verify_nc(Node) -> 4051 P = self(), 4052 Ref = make_ref(), 4053 Pid = spawn(Node, 4054 fun() -> 4055 R = erts_test_utils:check_node_dist(fun(E) -> E end), 4056 P ! {Ref, R} 4057 end), 4058 MonRef = monitor(process, Pid), 4059 receive 4060 {Ref, ok} -> 4061 demonitor(MonRef,[flush]), 4062 ok; 4063 {Ref, Error} -> 4064 ct:log("~s",[Error]), 4065 ct:fail(failed_nc_refc_check); 4066 {'DOWN', MonRef, _, _, _} = Down -> 4067 ct:log("~p",[Down]), 4068 ct:fail(crashed_nc_refc_check) 4069 end. 4070 4071enable_internal_state() -> 4072 case catch erts_debug:get_internal_state(available_internal_state) of 4073 true -> true; 4074 _ -> erts_debug:set_internal_state(available_internal_state, true) 4075 end. 4076 4077sys_mem_cond_run(OrigReqSizeMB, TestFun) when is_integer(OrigReqSizeMB) -> 4078 %% Debug normally needs more memory, so double the requirement 4079 Debug = erlang:system_info(debug_compiled), 4080 ReqSizeMB = if Debug -> OrigReqSizeMB * 2; true -> OrigReqSizeMB end, 4081 case total_memory() of 4082 TotMem when is_integer(TotMem), TotMem >= ReqSizeMB -> 4083 TestFun(); 4084 TotMem when is_integer(TotMem) -> 4085 {skipped, "Not enough memory ("++integer_to_list(TotMem)++" MB)"}; 4086 undefined -> 4087 {skipped, "Could not retrieve memory information"} 4088 end. 4089 4090 4091total_memory() -> 4092 %% Totat memory in MB. 4093 try 4094 MemoryData = memsup:get_system_memory_data(), 4095 case lists:keysearch(total_memory, 1, MemoryData) of 4096 {value, {total_memory, TM}} -> 4097 TM div (1024*1024); 4098 false -> 4099 {value, {system_total_memory, STM}} = 4100 lists:keysearch(system_total_memory, 1, MemoryData), 4101 STM div (1024*1024) 4102 end 4103 catch 4104 _ : _ -> 4105 undefined 4106 end. 4107