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