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