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