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