1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2021. 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(dirty_nif_SUITE). 22 23%%-define(line_trace,true). 24-define(CHECK(Exp,Got), check(Exp,Got,?LINE)). 25%%-define(CHECK(Exp,Got), Exp = Got). 26 27-include_lib("common_test/include/ct.hrl"). 28 29-export([all/0, suite/0, 30 init_per_suite/1, end_per_suite/1, 31 init_per_testcase/2, end_per_testcase/2, 32 dirty_nif/1, dirty_nif_send/1, 33 dirty_nif_exception/1, call_dirty_nif_exception/1, 34 dirty_scheduler_exit/1, dirty_call_while_terminated/1, 35 dirty_heap_access/1, dirty_process_info/1, 36 dirty_process_register/1, dirty_process_trace/1, 37 code_purge/1, literal_area/1, dirty_nif_send_traced/1, 38 nif_whereis/1, nif_whereis_parallel/1, nif_whereis_proxy/1]). 39 40-define(nif_stub,nif_stub_error(?LINE)). 41 42suite() -> [{ct_hooks,[ts_install_cth]}]. 43 44all() -> 45 [dirty_nif, 46 dirty_nif_send, 47 dirty_nif_exception, 48 dirty_scheduler_exit, 49 dirty_call_while_terminated, 50 dirty_heap_access, 51 dirty_process_info, 52 dirty_process_register, 53 dirty_process_trace, 54 code_purge, 55 literal_area, 56 dirty_nif_send_traced, 57 nif_whereis, 58 nif_whereis_parallel]. 59 60init_per_suite(Config) -> 61 case erlang:system_info(dirty_cpu_schedulers) of 62 N when N > 0 -> 63 case lib_loaded() of 64 false -> 65 ok = erlang:load_nif( 66 filename:join(?config(data_dir, Config), 67 "dirty_nif_SUITE"), []); 68 true -> 69 ok 70 end, 71 Config; 72 _ -> 73 {skipped, "No dirty scheduler support"} 74 end. 75 76end_per_suite(_Config) -> 77 ok. 78 79init_per_testcase(Case, Config) -> 80 [{testcase, Case} | Config]. 81 82end_per_testcase(_Case, _Config) -> 83 ok. 84 85dirty_nif(Config) when is_list(Config) -> 86 Val1 = 42, 87 Val2 = "Erlang", 88 Val3 = list_to_binary([Val2, 0]), 89 {Val1, Val2, Val3} = call_dirty_nif(Val1, Val2, Val3), 90 LargeArray = lists:duplicate(1000, ok), 91 LargeArray = call_dirty_nif_zero_args(), 92 ok. 93 94dirty_nif_send(Config) when is_list(Config) -> 95 Parent = self(), 96 Pid = spawn_link(fun() -> 97 Self = self(), 98 {ok, Self} = receive_any(), 99 Parent ! {ok, Self} 100 end), 101 {ok, Pid} = send_from_dirty_nif(Pid), 102 {ok, Pid} = receive_any(), 103 ok. 104 105dirty_nif_exception(Config) when is_list(Config) -> 106 try 107 %% this checks that the expected exception occurs when the 108 %% dirty NIF returns the result of enif_make_badarg 109 %% directly 110 call_dirty_nif_exception(1), 111 ct:fail(expected_badarg) 112 catch 113 error:badarg:Stk1 -> 114 [{?MODULE,call_dirty_nif_exception,[1],_}|_] = Stk1, 115 ok 116 end, 117 try 118 %% this checks that the expected exception occurs when the 119 %% dirty NIF calls enif_make_badarg at some point but then 120 %% returns a value that isn't an exception 121 call_dirty_nif_exception(0), 122 ct:fail(expected_badarg) 123 catch 124 error:badarg:Stk2 -> 125 [{?MODULE,call_dirty_nif_exception,[0],_}|_] = Stk2, 126 ok 127 end, 128 %% this checks that a dirty NIF can raise various terms as 129 %% exceptions 130 ok = nif_raise_exceptions(call_dirty_nif_exception). 131 132nif_raise_exceptions(NifFunc) -> 133 ExcTerms = [{error, test}, "a string", <<"a binary">>, 134 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], 135 lists:foldl(fun(Term, ok) -> 136 try 137 erlang:apply(?MODULE,NifFunc,[Term]), 138 ct:fail({expected,Term}) 139 catch 140 error:Term:Stk -> 141 [{?MODULE,NifFunc,[Term],_}|_] = Stk, 142 ok 143 end 144 end, ok, ExcTerms). 145 146dirty_scheduler_exit(Config) when is_list(Config) -> 147 {ok, Node} = start_node(Config, "+SDio 1"), 148 Path = proplists:get_value(data_dir, Config), 149 NifLib = filename:join(Path, atom_to_list(?MODULE)), 150 [ok] = mcall(Node, 151 [fun() -> 152 ok = erlang:load_nif(NifLib, []), 153 %% Perform a dry run to ensure that all required code 154 %% is loaded. Otherwise the test will fail since code 155 %% loading is done through dirty IO and it won't make 156 %% any progress during this test. 157 _DryRun = test_dirty_scheduler_exit(), 158 Start = erlang:monotonic_time(millisecond), 159 ok = test_dirty_scheduler_exit(), 160 End = erlang:monotonic_time(millisecond), 161 io:format("Time=~p ms~n", [End-Start]), 162 ok 163 end]), 164 stop_node(Node), 165 ok. 166 167test_dirty_scheduler_exit() -> 168 process_flag(trap_exit,true), 169 test_dse(10,[]). 170test_dse(0,Pids) -> 171 timer:sleep(100), 172 kill_dse(Pids,[]); 173test_dse(N,Pids) -> 174 Pid = spawn_link(fun dirty_sleeper/0), 175 test_dse(N-1,[Pid|Pids]). 176 177kill_dse([],Killed) -> 178 wait_dse(Killed, ok); 179kill_dse([Pid|Pids],AlreadyKilled) -> 180 exit(Pid,kill), 181 kill_dse(Pids,[Pid|AlreadyKilled]). 182 183wait_dse([], Result) -> 184 Result; 185wait_dse([Pid|Pids], Result) -> 186 receive 187 {'EXIT', Pid, killed} -> wait_dse(Pids, Result); 188 {'EXIT', Pid, _Other} -> wait_dse(Pids, failed) 189 end. 190 191dirty_call_while_terminated(Config) when is_list(Config) -> 192 Me = self(), 193 Bin = list_to_binary(lists:duplicate(4711, $r)), 194 {value, {BinAddr, 4711, 1}} = lists:keysearch(4711, 2, 195 element(2, 196 process_info(self(), 197 binary))), 198 {Dirty, DM} = spawn_opt(fun () -> 199 dirty_call_while_terminated_nif(Me), 200 blipp:blupp(Bin) 201 end, 202 [monitor,link]), 203 receive {dirty_alive, _Pid} -> ok end, 204 {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2, 205 element(2, 206 process_info(self(), 207 binary))), 208 Reason = die_dirty_process, 209 OT = process_flag(trap_exit, true), 210 exit(Dirty, Reason), 211 receive 212 {'DOWN', DM, process, Dirty, R0} -> 213 R0 = Reason 214 end, 215 receive 216 {'EXIT', Dirty, R1} -> 217 R1 = Reason 218 end, 219 undefined = process_info(Dirty), 220 undefined = process_info(Dirty, status), 221 false = erlang:is_process_alive(Dirty), 222 false = lists:member(Dirty, processes()), 223 %% Binary still referred by Dirty process not yet cleaned up 224 %% since the dirty nif has not yet returned... 225 {value, {BinAddr, 4711, 2}} = lists:keysearch(4711, 2, 226 element(2, 227 process_info(self(), 228 binary))), 229 receive 230 Msg -> 231 ct:fail({unexpected_message, Msg}) 232 after 233 1000 -> 234 ok 235 end, 236 ok = wait_until(fun() -> 237 {value, {BinAddr, 4711, 1}} == 238 lists:keysearch(4711, 2, 239 element(2, 240 process_info(self(), 241 binary))) 242 end, 243 10000), 244 process_flag(trap_exit, OT), 245 try 246 blipp:blupp(Bin) 247 catch 248 _ : _ -> ok 249 end. 250 251dirty_heap_access(Config) when is_list(Config) -> 252 {ok, Node} = start_node(Config), 253 Me = self(), 254 RGL = rpc:call(Node,erlang,whereis,[init]), 255 Ref = rpc:call(Node,erlang,make_ref,[]), 256 Dirty = spawn_link(fun () -> 257 Res = dirty_heap_access_nif(Ref), 258 garbage_collect(), 259 Me ! {self(), Res}, 260 receive after infinity -> ok end 261 end), 262 {N, R} = access_dirty_heap(Dirty, RGL, 0, 0), 263 receive 264 {_Pid, Res} -> 265 1000 = length(Res), 266 lists:foreach(fun (X) -> Ref = X end, Res) 267 end, 268 unlink(Dirty), 269 exit(Dirty, kill), 270 stop_node(Node), 271 {comment, integer_to_list(N) ++ " GL change loops; " 272 ++ integer_to_list(R) ++ " while running dirty"}. 273 274access_dirty_heap(Dirty, RGL, N, R) -> 275 case process_info(Dirty, status) of 276 {status, waiting} -> 277 {N, R}; 278 {status, Status} -> 279 {group_leader, GL} = process_info(Dirty, group_leader), 280 true = group_leader(RGL, Dirty), 281 {group_leader, RGL} = process_info(Dirty, group_leader), 282 true = group_leader(GL, Dirty), 283 {group_leader, GL} = process_info(Dirty, group_leader), 284 access_dirty_heap(Dirty, RGL, N+1, case Status of 285 running -> 286 R+1; 287 _ -> 288 R 289 end) 290 end. 291 292%% These tests verify that processes that access a process executing a 293%% dirty NIF where the main lock is needed for that access do not get 294%% blocked. Each test passes its pid to dirty_sleeper, which sends a 295%% 'ready' message when it's running on a dirty scheduler and just before 296%% it starts a 2 second sleep. When it receives the message, it verifies 297%% that access to the dirty process is as it expects. After the dirty 298%% process finishes its 2 second sleep but before it returns from the dirty 299%% scheduler, it sends a 'done' message. If the tester already received 300%% that message, the test fails because it means attempting to access the 301%% dirty process waited for that process to return to a regular scheduler, 302%% so verify that we haven't received that message, and also verify that 303%% the dirty process is still alive immediately after accessing it. 304dirty_process_info(Config) when is_list(Config) -> 305 access_dirty_process( 306 Config, 307 fun() -> ok end, 308 fun(NifPid) -> 309 PI = process_info(NifPid), 310 {current_function,{?MODULE,dirty_sleeper,1}} = 311 lists:keyfind(current_function, 1, PI), 312 ok 313 end, 314 fun(_) -> ok end). 315 316dirty_process_register(Config) when is_list(Config) -> 317 access_dirty_process( 318 Config, 319 fun() -> ok end, 320 fun(NifPid) -> 321 register(test_dirty_process_register, NifPid), 322 NifPid = whereis(test_dirty_process_register), 323 unregister(test_dirty_process_register), 324 false = lists:member(test_dirty_process_register, 325 registered()), 326 ok 327 end, 328 fun(_) -> ok end). 329 330dirty_process_trace(Config) when is_list(Config) -> 331 access_dirty_process( 332 Config, 333 fun() -> 334 erlang:trace_pattern({?MODULE,dirty_sleeper,1}, 335 [{'_',[],[{return_trace}]}], 336 [local,meta]), 337 ok 338 end, 339 fun(NifPid) -> 340 erlang:trace(NifPid, true, [call,timestamp]), 341 ok 342 end, 343 fun(NifPid) -> 344 receive 345 done -> 346 receive 347 {trace_ts,NifPid,call,{?MODULE,dirty_sleeper,_},_} -> 348 ok 349 after 350 0 -> 351 error(missing_trace_call_message) 352 end, 353 receive 354 {trace_ts,NifPid,return_from,{?MODULE,dirty_sleeper,1}, 355 ok,_} -> 356 ok 357 after 358 100 -> 359 error(missing_trace_return_message) 360 end 361 after 362 2500 -> 363 error(missing_done_message) 364 end, 365 ok 366 end). 367 368dirty_code_test_code() -> 369 " 370-module(dirty_code_test). 371 372-export([func/1]). 373 374func(Fun) -> 375 Fun(), 376 blipp:blapp(). 377 378". 379 380code_purge(Config) when is_list(Config) -> 381 Path = ?config(data_dir, Config), 382 File = filename:join(Path, "dirty_code_test.erl"), 383 ok = file:write_file(File, dirty_code_test_code()), 384 {ok, dirty_code_test, Bin} = compile:file(File, [binary]), 385 {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), 386 Start = erlang:monotonic_time(), 387 {Pid1, Mon1} = spawn_monitor(fun () -> 388 dirty_code_test:func(fun () -> 389 %% Sleep for 2 seconds 390 %% in dirty nif... 391 dirty_sleeper() 392 end) 393 end), 394 {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), 395 {Pid2, Mon2} = spawn_monitor(fun () -> 396 dirty_code_test:func(fun () -> 397 %% Sleep for 2 seconds 398 %% in dirty nif... 399 dirty_sleeper() 400 end) 401 end), 402 receive 403 {'DOWN', Mon1, process, Pid1, _} -> 404 ct:fail(premature_death) 405 after 100 -> 406 ok 407 end, 408 true = erlang:purge_module(dirty_code_test), 409 receive 410 {'DOWN', Mon1, process, Pid1, Reason1} -> 411 killed = Reason1 412 end, 413 receive 414 {'DOWN', Mon2, process, Pid2, _} -> 415 ct:fail(premature_death) 416 after 100 -> 417 ok 418 end, 419 true = erlang:delete_module(dirty_code_test), 420 receive 421 {'DOWN', Mon2, process, Pid2, _} -> 422 ct:fail(premature_death) 423 after 100 -> 424 ok 425 end, 426 true = erlang:purge_module(dirty_code_test), 427 receive 428 {'DOWN', Mon2, process, Pid2, Reason2} -> 429 killed = Reason2 430 end, 431 End = erlang:monotonic_time(), 432 Time = erlang:convert_time_unit(End-Start, native, milli_seconds), 433 io:format("Time=~p~n", [Time]), 434 true = Time =< 1000, 435 literal_area_collector_test:check_idle(5000), 436 ok. 437 438dirty_nif_send_traced(Config) when is_list(Config) -> 439 Parent = self(), 440 Rcvr = spawn_link(fun() -> 441 Self = self(), 442 receive {ok, Self} -> ok end, 443 Parent ! {Self, received} 444 end), 445 Sndr = spawn_link(fun () -> 446 receive {Parent, go} -> ok end, 447 {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr), 448 Parent ! {self(), sent} 449 end), 450 1 = erlang:trace(Sndr, true, [send, running, exiting]), 451 Start = erlang:monotonic_time(), 452 Sndr ! {self(), go}, 453 454 receive {Rcvr, received} -> ok end, 455 End1 = erlang:monotonic_time(), 456 Time1 = erlang:convert_time_unit(End1-Start, native, 1000), 457 io:format("Time1: ~p milliseconds~n", [Time1]), 458 true = Time1 < 500, 459 receive {Sndr, sent} -> ok end, 460 End2 = erlang:monotonic_time(), 461 Time2 = erlang:convert_time_unit(End2-Start, native, 1000), 462 io:format("Time2: ~p milliseconds~n", [Time2]), 463 true = Time2 >= 1900, 464 465 %% Make sure that the send trace is 466 %% in between an in and and out trace 467 (fun F() -> 468 %% We got an in trace, look for out or send 469 {trace,Sndr,in,_} = recv_trace_from(Sndr), 470 case recv_trace_from(Sndr) of 471 {trace,Sndr,out,_} -> 472 %% We got an out, look for another in 473 F(); 474 {trace,Sndr,send,_,_} -> 475 %% We got a send, look for another out 476 {trace,Sndr,out,_} = recv_trace_from(Sndr), 477 ok 478 end 479 end)(), 480 481 ok. 482 483recv_trace_from(Sndr) -> 484 receive 485 M when element(1, M) =:= trace; 486 element(1, M) =:= trace_ts, 487 element(2, M) =:= Sndr -> 488 M 489 end. 490 491dirty_literal_test_code() -> 492 " 493-module(dirty_literal_code_test). 494 495-export([get_literal/0]). 496 497get_literal() -> 498 {0,1,2,3,4,5,6,7,8,9}. 499 500". 501 502literal_area(Config) when is_list(Config) -> 503 NifTMO = 3000, 504 ExtraTMO = 1000, 505 TotTMO = NifTMO+ExtraTMO, 506 Path = ?config(data_dir, Config), 507 File = filename:join(Path, "dirty_literal_code_test.erl"), 508 ok = file:write_file(File, dirty_literal_test_code()), 509 {ok, dirty_literal_code_test, Bin} = compile:file(File, [binary]), 510 {module, dirty_literal_code_test} = erlang:load_module(dirty_literal_code_test, Bin), 511 Me = self(), 512 Fun = fun () -> 513 dirty_terminating_literal_access( 514 Me, 515 dirty_literal_code_test:get_literal()) 516 end, 517 {Pid, Mon} = spawn_monitor(Fun), 518 receive {dirty_alive, Pid} -> ok end, 519 exit(Pid, kill), 520 Start = erlang:monotonic_time(millisecond), 521 receive {'DOWN', Mon, process, Pid, killed} -> ok end, 522 true = erlang:delete_module(dirty_literal_code_test), 523 true = erlang:purge_module(dirty_literal_code_test), 524 End = erlang:monotonic_time(millisecond), 525 %% Wait for dirty_nif to do its access... 526 TMO = case End - Start of 527 T when T < TotTMO -> 528 TotTMO-T; 529 _ -> 530 0 531 end, 532 receive after TMO -> ok end, 533 literal_area_collector_test:check_idle(5000), 534 {comment, "Waited "++integer_to_list(TMO)++" milliseconds after purge"}. 535 536%% 537%% Internal... 538%% 539 540access_dirty_process(Config, Start, Test, Finish) -> 541 {ok, Node} = start_node(Config, ""), 542 [ok] = mcall(Node, 543 [fun() -> 544 Path = ?config(data_dir, Config), 545 Lib = atom_to_list(?MODULE), 546 ok = erlang:load_nif(filename:join(Path,Lib), []), 547 ok = test_dirty_process_access(Start, Test, Finish) 548 end]), 549 stop_node(Node), 550 ok. 551 552test_dirty_process_access(Start, Test, Finish) -> 553 ok = Start(), 554 Self = self(), 555 NifPid = spawn_link(fun() -> 556 ok = dirty_sleeper(Self) 557 end), 558 ok = receive 559 ready -> 560 ok = Test(NifPid), 561 receive 562 done -> 563 error(dirty_process_info_blocked) 564 after 565 0 -> 566 true = erlang:is_process_alive(NifPid), 567 ok 568 end 569 after 570 1000 -> 571 error(timeout) 572 end, 573 ok = Finish(NifPid). 574 575receive_any() -> 576 receive M -> M end. 577 578start_node(Config) -> 579 start_node(Config, ""). 580 581start_node(Config, Args) when is_list(Config) -> 582 Pa = filename:dirname(code:which(?MODULE)), 583 Name = list_to_atom(atom_to_list(?MODULE) 584 ++ "-" 585 ++ atom_to_list(proplists:get_value(testcase, Config)) 586 ++ "-" 587 ++ integer_to_list(erlang:system_time(second)) 588 ++ "-" 589 ++ integer_to_list(erlang:unique_integer([positive]))), 590 test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). 591 592stop_node(Node) -> 593 test_server:stop_node(Node). 594 595mcall(Node, Funs) -> 596 Parent = self(), 597 Refs = lists:map(fun (Fun) -> 598 Ref = make_ref(), 599 spawn_link(Node, 600 fun () -> 601 Res = Fun(), 602 unlink(Parent), 603 Parent ! {Ref, Res} 604 end), 605 Ref 606 end, Funs), 607 lists:map(fun (Ref) -> 608 receive 609 {Ref, Res} -> 610 Res 611 end 612 end, Refs). 613 614%% Test enif_whereis_... 615%% These tests are mostly identical to their counterparts in nif_SUITE.erl, 616%% with just name and count changes in the first few lines. 617 618nif_whereis(Config) when is_list(Config) -> 619 erl_ddll:try_load(?config(data_dir, Config), echo_drv, []), 620 621 RegName = dirty_nif_whereis_test_thing, 622 undefined = erlang:whereis(RegName), 623 false = whereis_term(pid, RegName), 624 625 Mgr = self(), 626 Ref = make_ref(), 627 ProcMsg = {Ref, ?LINE}, 628 PortMsg = ?MODULE_STRING " whereis hello\n", 629 630 {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]), 631 true = register(RegName, Pid), 632 Pid = erlang:whereis(RegName), 633 Pid = whereis_term(pid, RegName), 634 false = whereis_term(port, RegName), 635 false = whereis_term(pid, [RegName]), 636 637 ok = whereis_send(pid, RegName, {forward, Mgr, ProcMsg}), 638 ok = receive ProcMsg -> ok end, 639 640 Pid ! {Ref, quit}, 641 ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end, 642 undefined = erlang:whereis(RegName), 643 false = whereis_term(pid, RegName), 644 645 Port = open_port({spawn, echo_drv}, [eof]), 646 true = register(RegName, Port), 647 Port = erlang:whereis(RegName), 648 Port = whereis_term(port, RegName), 649 false = whereis_term(pid, RegName), 650 false = whereis_term(port, [RegName]), 651 652 ok = whereis_send(port, RegName, PortMsg), 653 ok = receive {Port, {data, PortMsg}} -> ok end, 654 655 port_close(Port), 656 undefined = erlang:whereis(RegName), 657 false = whereis_term(port, RegName), 658 ok. 659 660nif_whereis_parallel(Config) when is_list(Config) -> 661 662 %% try to be at least a little asymetric 663 NProcs = trunc(3.5 * erlang:system_info(schedulers)), 664 NSeq = lists:seq(1, NProcs), 665 Names = [list_to_atom("dirty_nif_whereis_proc_" ++ integer_to_list(N)) 666 || N <- NSeq], 667 Mgr = self(), 668 Ref = make_ref(), 669 670 NotReg = fun(Name) -> 671 erlang:whereis(Name) == undefined 672 end, 673 PidReg = fun({Name, Pid, _Mon}) -> 674 erlang:whereis(Name) == Pid andalso whereis_term(pid, Name) == Pid 675 end, 676 RecvDown = fun({_Name, Pid, Mon}) -> 677 receive {'DOWN', Mon, process, Pid, normal} -> true 678 after 1500 -> false end 679 end, 680 RecvNum = fun(N) -> 681 receive {N, Ref} -> true 682 after 1500 -> false end 683 end, 684 685 true = lists:all(NotReg, Names), 686 687 %% {Name, Pid, Mon} 688 Procs = lists:map( 689 fun(N) -> 690 Name = lists:nth(N, Names), 691 Prev = lists:nth((if N == 1 -> NProcs; true -> (N - 1) end), Names), 692 Next = lists:nth((if N == NProcs -> 1; true -> (N + 1) end), Names), 693 {Pid, Mon} = spawn_monitor( 694 ?MODULE, nif_whereis_proxy, [{N, Ref, Mgr, [Prev, Next]}]), 695 true = register(Name, Pid), 696 {Name, Pid, Mon} 697 end, NSeq), 698 699 true = lists:all(PidReg, Procs), 700 701 %% tell them all to 'fire' as fast as we can 702 [P ! {Ref, send_proc} || {_, P, _} <- Procs], 703 704 %% each gets forwarded through two processes 705 true = lists:all(RecvNum, NSeq), 706 true = lists:all(RecvNum, NSeq), 707 708 %% tell them all to 'quit' by name 709 [N ! {Ref, quit} || {N, _, _} <- Procs], 710 true = lists:all(RecvDown, Procs), 711 true = lists:all(NotReg, Names), 712 ok. 713 714%% exported to be spawned by MFA by whereis tests 715nif_whereis_proxy({N, Ref, Mgr, Targets} = Args) -> 716 receive 717 {forward, To, Data} -> 718 To ! Data, 719 nif_whereis_proxy(Args); 720 {Ref, quit} -> 721 ok; 722 {Ref, send_port} -> 723 Msg = ?MODULE_STRING " whereis " ++ integer_to_list(N) ++ "\n", 724 lists:foreach( 725 fun(T) -> 726 ok = whereis_send(port, T, Msg) 727 end, Targets), 728 nif_whereis_proxy(Args); 729 {Ref, send_proc} -> 730 lists:foreach( 731 fun(T) -> 732 ok = whereis_send(pid, T, {forward, Mgr, {N, Ref}}) 733 end, Targets), 734 nif_whereis_proxy(Args) 735 end; 736nif_whereis_proxy(Ref) -> 737 receive 738 {forward, To, Data} -> 739 To ! Data, 740 nif_whereis_proxy(Ref); 741 {Ref, quit} -> 742 ok 743 end. 744 745wait_until(Fun, infinity) -> 746 wait_until_aux(Fun, infinity); 747wait_until(Fun, MaxTime) -> 748 End = erlang:monotonic_time(millisecond) + MaxTime, 749 wait_until_aux(Fun, End). 750 751wait_until_aux(Fun, End) -> 752 case Fun() of 753 true -> 754 ok; 755 _ -> 756 if End == infinity -> 757 receive after 100 -> ok end, 758 wait_until_aux(Fun, infinity); 759 true -> 760 Now = erlang:monotonic_time(millisecond), 761 case End =< Now of 762 true -> 763 timeout; 764 _ -> 765 Wait = case End - Now of 766 Short when End - Now < 100 -> 767 Short; 768 _ -> 769 100 770 end, 771 receive after Wait -> ok end, 772 wait_until_aux(Fun, End) 773 end 774 end 775 end. 776 777 778%% The NIFs: 779lib_loaded() -> false. 780call_dirty_nif(_,_,_) -> ?nif_stub. 781send_from_dirty_nif(_) -> ?nif_stub. 782send_wait_from_dirty_nif(_) -> ?nif_stub. 783call_dirty_nif_exception(_) -> ?nif_stub. 784call_dirty_nif_zero_args() -> ?nif_stub. 785dirty_call_while_terminated_nif(_) -> ?nif_stub. 786dirty_sleeper() -> ?nif_stub. 787dirty_sleeper(_) -> ?nif_stub. 788dirty_heap_access_nif(_) -> ?nif_stub. 789whereis_term(_Type,_Name) -> ?nif_stub. 790whereis_send(_Type,_Name,_Msg) -> ?nif_stub. 791dirty_terminating_literal_access(_Me, _Literal) -> ?nif_stub. 792 793nif_stub_error(Line) -> 794 exit({nif_not_loaded,module,?MODULE,line,Line}). 795