1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-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%%% Purpose : Compiles various modules with tough code
21
22-module(receive_SUITE).
23
24-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
25	 init_per_group/2,end_per_group/2,
26	 init_per_testcase/2,end_per_testcase/2,
27	 export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1,
28	 wait/1,recv_in_try/1,double_recv/1,receive_var_zero/1,
29         match_built_terms/1,elusive_common_exit/1,
30         return_before_receive/1,trapping/1]).
31
32-include_lib("common_test/include/ct.hrl").
33
34init_per_testcase(_Case, Config) ->
35    Config.
36
37end_per_testcase(_Case, _Config) ->
38    ok.
39
40suite() ->
41    [{ct_hooks,[ts_install_cth]},
42     {timetrap,{minutes,2}}].
43
44all() ->
45    slow_group() ++ [{group,p}].
46
47groups() ->
48    [{p,test_lib:parallel(),
49      [recv,coverage,otp_7980,export,wait,
50       recv_in_try,double_recv,receive_var_zero,
51       match_built_terms,elusive_common_exit,
52       return_before_receive,trapping]},
53     {slow,[],[ref_opt]}].
54
55init_per_suite(Config) ->
56    test_lib:recompile(?MODULE),
57    Config.
58
59end_per_suite(_Config) ->
60    ok.
61
62init_per_group(_GroupName, Config) ->
63    Config.
64
65end_per_group(_GroupName, Config) ->
66    Config.
67
68slow_group() ->
69    case ?MODULE of
70	receive_SUITE ->
71            %% Canononical module name. Run slow cases.
72            [{group,slow}];
73        _ ->
74            %% Cloned module. Don't run.
75            []
76    end.
77
78-record(state, {ena = true}).
79
80recv(Config) when is_list(Config) ->
81    Pid = spawn_link(fun() -> loop(#state{}) end),
82    Self = self(),
83    Pid ! {Self,test},
84    receive
85	{ok,test} -> ok;
86	{error,Other} ->
87	    io:format("Got unpexected ~p", [Other]),
88	    ct:fail(unexpected)
89    after 10000 ->
90	    ct:fail(no_answer)
91    end,
92    receive
93	X ->
94	    io:format("Unexpected extra message: ~p", [X]),
95	    ct:fail(unexpected)
96    after 10 ->
97	    ok
98    end,
99    ok.
100
101loop(S) ->
102    receive
103	_ when S#state.ena == false ->
104	    loop(S);
105	{P,test} ->
106	    P ! {ok,test},
107	    loop(S);
108	_X ->
109	    loop(S)
110    end.
111
112coverage(Config) when is_list(Config) ->
113    do_link(self()),
114    do_unlink(self()),
115    do_monitor_node(node(), true),
116    do_monitor_node(node(), false),
117    do_group_leader(group_leader(), self()),
118    id(node(self())),
119
120    erlang:'!'(self(), {a,10}),
121    self() ! {b,20},
122    [{a,10},{b,20}] = receive_all(),
123    self() ! {c,42},
124    receive
125	{c,42} ->
126	    ok
127    after infinity ->
128	    exit(cant_happen)
129    end,
130
131    self() ! 17,
132    self() ! 19,
133    59 = tuple_to_values(infinity, x),
134    61 = tuple_to_values(999999, x),
135    0 = tuple_to_values(1, x),
136
137    {'EXIT',{{badmap,[]},_}} = (catch monitor_plus_badmap(self())),
138
139    ok.
140
141monitor_plus_badmap(Pid) ->
142    monitor(process, Pid) + []#{}.
143
144receive_all() ->
145    receive
146	Any ->
147	    [Any|receive_all()]
148    after 0 ->
149	    []
150    end.
151
152do_monitor_node(Node, Bool) ->
153    monitor_node(Node, Bool).
154
155do_link(Pid) ->
156    link(Pid).
157
158do_unlink(Pid) ->
159    unlink(Pid).
160
161do_group_leader(Leader, Pid) ->
162    group_leader(Leader, Pid).
163
164
165%% cover sys_core_fold:tuple_to_values/2
166tuple_to_values(infinity, X) ->
167    {A,B} = case X of
168		x ->
169		    receive
170			Any ->
171			    {42,Any}
172		    end
173	    end,
174    A+B;
175tuple_to_values(Timeout, X) ->
176    {A,B} = case X of
177		x ->
178		    receive
179			Any ->
180			    {42,Any}
181		    after Timeout ->
182			    {0,0}
183		    end
184	    end,
185    A+B.
186
187%% OTP-7980. Thanks to Vincent de Phily. The following code would
188%% be inccorrectly optimized by beam_jump.
189
190otp_7980(Config) when is_list(Config) ->
191    7 = otp_7980_add_clients(10),
192    ok.
193
194otp_7980_add_clients(Count) ->
195    Timeout = 42,
196    lists:foldl(fun(_, N) ->
197			case N of
198                           1 -> ok;
199			    _ -> receive after Timeout -> ok end
200			end,
201			N - 1
202		end, Count, [1,2,3]).
203
204ref_opt(Config) when is_list(Config) ->
205    DataDir = proplists:get_value(data_dir, Config),
206    PrivDir = proplists:get_value(priv_dir, Config),
207    Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])),
208    test_lib:p_run(fun(Src) ->
209			   do_ref_opt(Src, PrivDir)
210		   end, Sources),
211    cover_recv_instructions(),
212    ok.
213
214do_ref_opt(Source, PrivDir) ->
215    try
216	Ext = filename:extension(Source),
217	{ok,Mod} = compile:file(Source, [report_errors,report_warnings,
218					 {outdir,PrivDir}] ++
219					[from_asm || Ext =:= ".S" ]),
220	Base = filename:rootname(filename:basename(Source), Ext),
221	code:purge(list_to_atom(Base)),
222	BeamFile = filename:join(PrivDir, Base),
223	code:load_abs(BeamFile),
224	ok = Mod:Mod(),
225	{beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile),
226	case Base of
227	    "no_"++_ ->
228		[] = collect_recv_opt_instrs(Code);
229	    "yes_"++_ ->
230		[{recv_mark,{f,L}},{recv_set,{f,L}}] =
231		    collect_recv_opt_instrs(Code)
232	end,
233	ok
234    catch Class:Error:Stk ->
235	    io:format("~s: ~p ~p\n~p\n", [Source,Class,Error,Stk]),
236	    error
237    end.
238
239collect_recv_opt_instrs(Code) ->
240    L = [ [I || I <- Is,
241		begin
242		    case I of
243			{recv_mark,{f,_}} -> true;
244			{recv_set,{f,_}} -> true;
245			_ -> false
246		    end
247		end] || {function,_,_,_,Is} <- Code],
248    lists:append(L).
249
250cover_recv_instructions() ->
251    %% We want to cover the handling of recv_mark and recv_set in beam_utils.
252    %% Since those instructions are introduced in a late optimization pass,
253    %% beam_utils:live_opt() will not see them unless the compilation is
254    %% started from a .S file. The compile_SUITE:asm/1 test case will
255    %% compile all test suite files to .S and then run them through the
256    %% compiler again.
257    %%
258    %% Here will we will ensure that this modules contains recv_mark
259    %% and recv_set instructions.
260    Pid = spawn_link(fun() ->
261			     receive {Parent,Ref} ->
262				     Parent ! Ref
263			     end
264		     end),
265    Ref = make_ref(),
266    Pid ! {self(),Ref},
267    receive
268	Ref -> ok
269    end.
270
271export(Config) when is_list(Config) ->
272    Ref = make_ref(),
273    self() ! {result,Ref,42},
274    42 = export_1(Ref),
275    {error,timeout} = export_1(Ref),
276
277    self() ! {result,Ref},
278    {ok,Ref} = export_2(),
279
280    ok.
281
282export_1(Reference) ->
283    id(Reference),
284    receive
285	{result,Reference,Result} ->
286	    Result
287    after 1 ->
288	    Result = {error,timeout}
289    end,
290    %% Result ({x,1}) is used, but not the return value ({x,0})
291    %% of the receive. Used to be incorrectly optimized
292    %% by beam_block.
293    id({build,self()}),
294    Result.
295
296export_2() ->
297    receive {result,Result} -> ok end,
298    {ok,Result}.
299
300wait(Config) when is_list(Config) ->
301    self() ! <<42>>,
302    <<42>> = wait_1(r, 1, 2),
303    {1,2,3} = wait_1(1, 2, 3),
304    {'EXIT',{timeout_value,_}} = (catch receive after [] -> timeout end),
305    ok.
306
307wait_1(r, _, _) ->
308    receive
309	B when byte_size(B) > 0 ->
310	    B
311    end;
312%% beam_utils would wrongly assume that wait/1 could fall through
313%% to the next clause.
314wait_1(A, B, C) ->
315    {A,B,C}.
316
317recv_in_try(_Config) ->
318    self() ! {ok,fh}, {ok,fh} = recv_in_try(infinity, native),
319    self() ! {ok,ignored}, {ok,42} = recv_in_try(infinity, plain),
320    self() ! {error,ignored}, nok = recv_in_try(infinity, plain),
321    timeout = recv_in_try(1, plain),
322    ok.
323
324recv_in_try(Timeout, Format) ->
325    try
326	receive
327	    {Status,History} ->
328                %% {test,is_tuple,{f,148},[{x,0}]}.
329                %% {test,test_arity,{f,148},[{x,0},2]}.
330                %% {get_tuple_element,{x,0},0,{y,1}}.  %y1 is fragile.
331                %%
332                %% %% Here the fragility of y1 would be be progated to
333                %% %% the 'catch' below. Incorrect, since get_tuple_element
334                %% %% can't fail.
335                %% {get_tuple_element,{x,0},1,{x,2}}.
336                %%
337                %% remove_message.                     %y1 fragility cleared.
338		FH = case Format of
339			native ->
340                             id(History);
341			plain ->
342                             id(42)
343		    end,
344		case Status of
345		    ok ->
346			{ok,FH};
347		    error ->
348			nok
349		end
350	after Timeout ->
351		timeout
352	end
353    catch
354        %% The fragility of y1 incorrectly propagated to here.
355        %% beam_validator would complain.
356	throw:{error,Reason} ->
357	    {nok,Reason}
358    end.
359
360%% ERL-703. The compiler would crash because beam_utils:anno_defs/1
361%% failed to take into account that code after loop_rec_end is
362%% unreachable.
363
364double_recv(_Config) ->
365    self() ! {more,{a,term}},
366    ok = do_double_recv({more,{a,term}}, any),
367    self() ! message,
368    ok = do_double_recv(whatever, message),
369
370    error = do_double_recv({more,42}, whatever),
371    error = do_double_recv(whatever, whatever),
372    ok.
373
374do_double_recv({more, Rest}, _Msg) ->
375    receive
376        {more, Rest} ->
377            ok
378    after 0 ->
379            error
380    end;
381do_double_recv(_, Msg) ->
382    receive
383        Msg ->
384            ok
385    after 0 ->
386            error
387    end.
388
389%% Test 'after Z', when Z =:= 0 been propagated as an immediate by the type
390%% optimization pass.
391receive_var_zero(Config) when is_list(Config) ->
392    self() ! x,
393    self() ! y,
394    Z = zero(),
395    timeout = receive
396                  z -> ok
397              after Z -> timeout
398              end,
399    timeout = receive
400              after Z -> timeout
401              end,
402    self() ! w,
403    receive
404	x -> ok;
405	Other ->
406	    ct:fail({bad_message,Other})
407    end.
408
409zero() -> 0.
410
411%% ERL-862; the validator would explode when a term was constructed in a
412%% receive guard.
413
414-define(MATCH_BUILT_TERM(Ref, Expr),
415        (fun() ->
416                 Ref = make_ref(),
417                 A = id($a),
418                 B = id($b),
419                 Built = id(Expr),
420                 self() ! {Ref, A, B},
421                 receive
422                     {Ref, A, B} when Expr =:= Built ->
423                         ok
424                 after 5000 ->
425                     ct:fail("Failed to match message with term built in "
426                             "receive guard.")
427                 end
428         end)()).
429
430match_built_terms(Config) when is_list(Config) ->
431    ?MATCH_BUILT_TERM(Ref, [A, B]),
432    ?MATCH_BUILT_TERM(Ref, {A, B}),
433    ?MATCH_BUILT_TERM(Ref, <<A, B>>),
434    ?MATCH_BUILT_TERM(Ref, #{ 1 => A, 2 => B}).
435
436elusive_common_exit(_Config) ->
437    self() ! {1, a},
438    self() ! {2, b},
439    {[z], [{2,b},{1,a}]} = elusive_loop([x,y,z], 2, []),
440
441    CodeServer = whereis(code_server),
442    Self = self(),
443    Self ! {Self, abc},
444    Self ! {CodeServer, []},
445    Self ! {Self, other},
446    try elusive2([]) of
447        Unexpected ->
448            ct:fail("Expected an exception; got ~p\n", [Unexpected])
449    catch
450        throw:[other, CodeServer, Self] ->
451            ok
452    end,
453
454    ok.
455
456elusive_loop(List, 0, Results) ->
457    {List, Results};
458elusive_loop(List, ToReceive, Results) ->
459    {Result, RemList} =
460        receive
461            {_Pos, _R} = Res when List =/= [] ->
462                [_H|T] = List,
463                {Res, T};
464            {_Pos, _R} = Res when List =:= [] ->
465                {Res, []}
466        end,
467    %% beam_ssa_pre_codegen:fix_receives() would fail to find
468    %% the common exit block for this receive. That would mean
469    %% that it would not insert all necessary copy instructions.
470    elusive_loop(RemList, ToReceive-1, [Result | Results]).
471
472
473elusive2(Acc) ->
474    receive
475        {Pid, abc} ->
476            ok;
477        {Pid, []} ->
478            ok;
479        {Pid, Res} ->
480            %% beam_ssa_pre_codegen:find_loop_exit/2 attempts to find
481            %% the first block of the common code after the receive
482            %% statement. It used to only look at the two last clauses
483            %% of the receive. In this function, the last two clauses
484            %% don't have any common block, so it would be assumed
485            %% that there was no common block for any of the
486            %% clauses. That would mean that copy instructions would
487            %% not be inserted as needed.
488            throw([Res | Acc])
489    end,
490    %% Common code.
491    elusive2([Pid | Acc]).
492
493return_before_receive(_Config) ->
494    ref_received = do_return_before_receive(),
495    ok.
496
497do_return_before_receive() ->
498    Ref = make_ref(),
499    self() ! {ref,Ref},
500    maybe_receive(id(false)),
501    receive
502        {ref,Ref} ->
503            ref_received
504    after 1 ->
505            %% Can only be reached if maybe_receive/1 returned
506            %% with the receive marker set.
507            timeout
508    end.
509
510maybe_receive(Bool) ->
511    NewRef = make_ref(),
512    case Bool of
513        true ->
514            receive
515                NewRef ->
516                    ok
517            end;
518        false ->
519            %% The receive marker must not be set when
520            %% leaving this function.
521            ok
522    end.
523
524trapping(_Config) ->
525    ok = do_trapping(0),
526    ok = do_trapping(1),
527    ok.
528
529%% Simplified from emulator's binary_SUITE:trapping/1.
530do_trapping(N) ->
531    Ref = make_ref(),
532    self() ! Ref,
533    case N rem 2 of
534	0 ->
535            %% Would generate recv_set _, label _, wait_timeout _ _,
536            %% which the loader can't handle.
537            receive after 1 -> ok end;
538	1 ->
539            void
540    end,
541    receive Ref -> ok end,
542    receive after 1 -> ok end.
543
544id(I) -> I.
545