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