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