1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-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 21-module(nif_SUITE). 22 23%%-define(line_trace,true). 24-define(CHECK(Exp,Got), Exp = check(Exp,Got,?LINE)). 25%%-define(CHECK(Exp,Got), Exp = Got). 26 27-include_lib("common_test/include/ct.hrl"). 28-include_lib("stdlib/include/assert.hrl"). 29 30-export([all/0, suite/0, groups/0, 31 init_per_suite/1, end_per_suite/1, 32 init_per_group/2, end_per_group/2, 33 init_per_testcase/2, end_per_testcase/2, 34 basic/1, reload_error/1, upgrade/1, heap_frag/1, 35 t_on_load/1, 36 t_load_race/1, 37 t_call_nif_early/1, 38 load_traced_nif/1, 39 select/1, select_steal/1, 40 select_error/1, 41 monitor_process_a/1, 42 monitor_process_b/1, 43 monitor_process_c/1, 44 monitor_process_d/1, 45 monitor_process_purge/1, 46 demonitor_process/1, 47 monitor_frenzy/1, 48 types/1, many_args/1, binaries/1, get_string/1, get_atom/1, 49 maps/1, 50 api_macros/1, 51 from_array/1, iolist_as_binary/1, resource/1, resource_binary/1, 52 resource_takeover/1, 53 t_dynamic_resource_call/1, 54 threading/1, send/1, send2/1, send3/1, send_threaded/1, 55 send_trace/1, send_seq_trace/1, 56 neg/1, is_checks/1, 57 get_length/1, make_atom/1, make_string/1, reverse_list_test/1, 58 otp_9828/1, 59 otp_9668/1, consume_timeslice/1, nif_schedule/1, 60 nif_exception/1, call_nif_exception/1, 61 nif_nan_and_inf/1, nif_atom_too_long/1, 62 nif_monotonic_time/1, nif_time_offset/1, nif_convert_time_unit/1, 63 nif_now_time/1, nif_cpu_time/1, nif_unique_integer/1, 64 nif_is_process_alive/1, nif_is_port_alive/1, 65 nif_term_to_binary/1, nif_binary_to_term/1, 66 nif_port_command/1, 67 nif_snprintf/1, 68 nif_internal_hash/1, 69 nif_internal_hash_salted/1, 70 nif_phash2/1, 71 nif_whereis/1, nif_whereis_parallel/1, 72 nif_whereis_threaded/1, nif_whereis_proxy/1, 73 nif_ioq/1, 74 pid/1, 75 id/1, 76 nif_term_type/1 77 ]). 78 79-export([many_args_100/100]). 80 81-define(nif_stub,nif_stub_error(?LINE)). 82 83-define(is_resource, is_reference). 84 85-define(RT_CREATE,1). 86-define(RT_TAKEOVER,2). 87 88suite() -> [{ct_hooks,[ts_install_cth]}]. 89 90all() -> 91 [basic] 92 ++ 93 [{group, G} || G <- api_groups()] 94 ++ 95 [reload_error, heap_frag, types, many_args, 96 {group, select}, 97 {group, monitor}, 98 monitor_frenzy, 99 t_dynamic_resource_call, 100 t_load_race, 101 t_call_nif_early, 102 load_traced_nif, 103 binaries, get_string, get_atom, maps, api_macros, from_array, 104 iolist_as_binary, resource, resource_binary, 105 threading, send, send2, send3, 106 send_threaded, neg, is_checks, get_length, make_atom, 107 make_string,reverse_list_test, 108 otp_9828, 109 otp_9668, consume_timeslice, 110 nif_schedule, nif_exception, nif_nan_and_inf, nif_atom_too_long, 111 nif_monotonic_time, nif_time_offset, nif_convert_time_unit, 112 nif_now_time, nif_cpu_time, nif_unique_integer, 113 nif_is_process_alive, nif_is_port_alive, 114 nif_term_to_binary, nif_binary_to_term, 115 nif_port_command, 116 nif_snprintf, 117 nif_internal_hash, 118 nif_internal_hash_salted, 119 nif_phash2, 120 nif_whereis, nif_whereis_parallel, nif_whereis_threaded, 121 nif_ioq, 122 pid, 123 nif_term_type]. 124 125init_per_suite(Config) -> 126 erts_debug:set_internal_state(available_internal_state, true), 127 Config. 128 129end_per_suite(_Config) -> 130 catch erts_debug:set_internal_state(available_internal_state, false), 131 ok. 132 133groups() -> 134 [{G, [], api_repeaters()} || G <- api_groups()] 135 ++ 136 [{monitor, [], [monitor_process_a, 137 monitor_process_b, 138 monitor_process_c, 139 monitor_process_d, 140 monitor_process_purge, 141 demonitor_process]}, 142 {select, [], [select, 143 select_error, 144 select_steal]}]. 145 146api_groups() -> [api_latest, api_2_4, api_2_0]. 147 148api_repeaters() -> [upgrade, resource_takeover, t_on_load]. 149 150init_per_group(api_2_4, Config) -> 151 [{nif_api_version, ".2_4"} | Config]; 152init_per_group(api_2_0, Config) -> 153 case {os:type(),erlang:system_info({wordsize, internal})} of 154 {{win32,_}, 8} -> 155 %% ERL_NIF_TERM was declared as 32-bit 'long' until 2.3 156 {skip, "API 2.0 buggy on Windows 64-bit"}; 157 _ -> 158 [{nif_api_version, ".2_0"} | Config] 159 end; 160init_per_group(_, Config) -> Config. 161 162end_per_group(_,_) -> ok. 163 164init_per_testcase(t_on_load, Config) -> 165 ets:new(nif_SUITE, [named_table]), 166 Config; 167init_per_testcase(nif_whereis_threaded, Config) -> 168 case erlang:system_info(threads) of 169 true -> Config; 170 false -> {skip, "No thread support"} 171 end; 172init_per_testcase(Select, Config) when Select =:= select; 173 Select =:= select_steal -> 174 case os:type() of 175 {win32,_} -> 176 {skip, "Test not yet implemented for windows"}; 177 _ -> 178 Config 179 end; 180init_per_testcase(_Case, Config) -> 181 %% Clear any resource dtor data before test starts in case another tc 182 %% left it in a bad state 183 catch last_resource_dtor_call(), 184 Config. 185 186end_per_testcase(t_on_load, _Config) -> 187 ets:delete(nif_SUITE), 188 testcase_cleanup(); 189end_per_testcase(_Func, _Config) -> 190 testcase_cleanup(). 191 192testcase_cleanup() -> 193 P1 = code:purge(nif_mod), 194 Del = code:delete(nif_mod), 195 P2 = code:purge(nif_mod), 196 io:format("fin purged=~p, deleted=~p and then purged=~p\n",[P1,Del,P2]). 197 198%% Basic smoke test of load_nif and a simple NIF call 199basic(Config) when is_list(Config) -> 200 ensure_lib_loaded(Config), 201 true = (lib_version() =/= undefined), 202 [{load,1,1,101},{lib_version,1,2,102}] = call_history(), 203 [] = call_history(), 204 true = lists:member(?MODULE, erlang:system_info(taints)), 205 ok. 206 207%% Test old reload feature now always fails 208reload_error(Config) when is_list(Config) -> 209 TmpMem = tmpmem(), 210 ensure_lib_loaded(Config), 211 212 Data = proplists:get_value(data_dir, Config), 213 File = filename:join(Data, "nif_mod"), 214 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), 215 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 216 217 ok = nif_mod:load_nif_lib(Config, 1), 218 219 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 220 [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 221 222 {error, {reload, _}} = nif_mod:load_nif_lib(Config, 2), 223 1 = nif_mod:lib_version(), 224 [{lib_version,1,3,103}] = nif_mod_call_history(), 225 226 {error, {reload, _}} = nif_mod:load_nif_lib(Config, 1), 227 1 = nif_mod:lib_version(), 228 [{lib_version,1,4,104}] = nif_mod_call_history(), 229 230 true = erlang:delete_module(nif_mod), 231 [] = nif_mod_call_history(), 232 233 %%false= check_process_code(Pid, nif_mod), 234 true = erlang:purge_module(nif_mod), 235 [{unload,1,5,105}] = nif_mod_call_history(), 236 237 true = lists:member(?MODULE, erlang:system_info(taints)), 238 true = lists:member(nif_mod, erlang:system_info(taints)), 239 verify_tmpmem(TmpMem), 240 ok. 241 242%% Test upgrade callback in nif lib 243upgrade(Config) when is_list(Config) -> 244 TmpMem = tmpmem(), 245 ensure_lib_loaded(Config), 246 247 Data = proplists:get_value(data_dir, Config), 248 File = filename:join(Data, "nif_mod"), 249 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), 250 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 251 252 ok = nif_mod:load_nif_lib(Config, 1), 253 {Pid,MRef} = nif_mod:start(), 254 1 = call(Pid,lib_version), 255 256 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 257 [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(), 258 259 %% Module upgrade with same lib-version 260 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 261 undefined = nif_mod:lib_version(), 262 1 = call(Pid,lib_version), 263 [{lib_version,1,4,104}] = nif_mod_call_history(), 264 265 ok = nif_mod:load_nif_lib(Config, 1), 266 1 = nif_mod:lib_version(), 267 [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(), 268 269 upgraded = call(Pid,upgrade), 270 false = check_process_code(Pid, nif_mod), 271 true = erlang:purge_module(nif_mod), 272 [{unload,1,7,107}] = nif_mod_call_history(), 273 274 1 = nif_mod:lib_version(), 275 [{lib_version,1,8,108}] = nif_mod_call_history(), 276 277 true = erlang:delete_module(nif_mod), 278 [] = nif_mod_call_history(), 279 280 %% Repeat upgrade again but from old (deleted) instance 281 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 282 undefined = nif_mod:lib_version(), 283 1 = call(Pid,lib_version), 284 [{lib_version,1,9,109}] = nif_mod_call_history(), 285 286 ok = nif_mod:load_nif_lib(Config, 1), 287 1 = nif_mod:lib_version(), 288 [{upgrade,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(), 289 290 upgraded = call(Pid,upgrade), 291 false = check_process_code(Pid, nif_mod), 292 true = erlang:purge_module(nif_mod), 293 [{unload,1,12,112}] = nif_mod_call_history(), 294 295 1 = nif_mod:lib_version(), 296 [{lib_version,1,13,113}] = nif_mod_call_history(), 297 298 true = erlang:delete_module(nif_mod), 299 [] = nif_mod_call_history(), 300 301 302 Pid ! die, 303 {'DOWN', MRef, process, Pid, normal} = receive_any(), 304 false = check_process_code(Pid, nif_mod), 305 true = erlang:purge_module(nif_mod), 306 [{unload,1,14,114}] = nif_mod_call_history(), 307 308 %% Module upgrade with different lib version 309 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 310 undefined = nif_mod:lib_version(), 311 {Pid2,MRef2} = nif_mod:start(), 312 undefined = call(Pid2,lib_version), 313 314 ok = nif_mod:load_nif_lib(Config, 1), 315 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 316 1 = call(Pid2,lib_version), 317 [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(), 318 319 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 320 undefined = nif_mod:lib_version(), 321 [] = nif_mod_call_history(), 322 1 = call(Pid2,lib_version), 323 [{lib_version,1,4,104}] = nif_mod_call_history(), 324 325 ok = nif_mod:load_nif_lib(Config, 2), 326 2 = nif_mod:lib_version(), 327 [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(), 328 329 1 = call(Pid2,lib_version), 330 [{lib_version,1,5,105}] = nif_mod_call_history(), 331 332 upgraded = call(Pid2,upgrade), 333 false = check_process_code(Pid2, nif_mod), 334 true = erlang:purge_module(nif_mod), 335 [{unload,1,6,106}] = nif_mod_call_history(), 336 337 2 = nif_mod:lib_version(), 338 [{lib_version,2,3,203}] = nif_mod_call_history(), 339 340 true = erlang:delete_module(nif_mod), 341 [] = nif_mod_call_history(), 342 343 344 %% Reverse upgrade but from old (deleted) instance 345 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 346 undefined = nif_mod:lib_version(), 347 [] = nif_mod_call_history(), 348 2 = call(Pid2,lib_version), 349 [{lib_version,2,4,204}] = nif_mod_call_history(), 350 351 ok = nif_mod:load_nif_lib(Config, 1), 352 1 = nif_mod:lib_version(), 353 [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), 354 355 2 = call(Pid2,lib_version), 356 [{lib_version,2,5,205}] = nif_mod_call_history(), 357 358 upgraded = call(Pid2,upgrade), 359 false = check_process_code(Pid2, nif_mod), 360 true = erlang:purge_module(nif_mod), 361 [{unload,2,6,206}] = nif_mod_call_history(), 362 363 1 = nif_mod:lib_version(), 364 [{lib_version,1,3,103}] = nif_mod_call_history(), 365 366 true = erlang:delete_module(nif_mod), 367 [] = nif_mod_call_history(), 368 369 370 Pid2 ! die, 371 {'DOWN', MRef2, process, Pid2, normal} = receive_any(), 372 false= check_process_code(Pid2, nif_mod), 373 true = erlang:purge_module(nif_mod), 374 [{unload,1,4,104}] = nif_mod_call_history(), 375 376 true = lists:member(?MODULE, erlang:system_info(taints)), 377 true = lists:member(nif_mod, erlang:system_info(taints)), 378 verify_tmpmem(TmpMem), 379 ok. 380 381%% Test loading/upgrade in on_load 382t_on_load(Config) when is_list(Config) -> 383 TmpMem = tmpmem(), 384 ensure_lib_loaded(Config), 385 386 Data = proplists:get_value(data_dir, Config), 387 File = filename:join(Data, "nif_mod"), 388 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors, 389 {d,'USE_ON_LOAD'}]), 390 391 %% Use ETS to tell nif_mod:on_load what to do 392 ets:insert(nif_SUITE, {data_dir, Data}), 393 ets:insert(nif_SUITE, {lib_version, 1}), 394 API = proplists:get_value(nif_api_version, Config, ""), 395 ets:insert(nif_SUITE, {nif_api_version, API}), 396 {module,nif_mod} = code:load_binary(nif_mod,File,Bin), 397 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 398 [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 399 400 {Pid,MRef} = nif_mod:start(), 401 1 = call(Pid,lib_version), 402 [{lib_version,1,3,103}] = nif_mod_call_history(), 403 404 %% Module upgrade with same lib-version 405 {module,nif_mod} = code:load_binary(nif_mod,File,Bin), 406 1 = nif_mod:lib_version(), 407 1 = call(Pid,lib_version), 408 [{upgrade,1,4,104},{lib_version,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(), 409 410 upgraded = call(Pid,upgrade), 411 false = check_process_code(Pid, nif_mod), 412 true = code:soft_purge(nif_mod), 413 [{unload,1,7,107}] = nif_mod_call_history(), 414 415 1 = nif_mod:lib_version(), 416 [{lib_version,1,8,108}] = nif_mod_call_history(), 417 418 true = code:delete(nif_mod), 419 [] = nif_mod_call_history(), 420 421 %% Repeat upgrade again but from old (deleted) instance 422 {module,nif_mod} = code:load_binary(nif_mod,File,Bin), 423 [{upgrade,1,9,109}] = nif_mod_call_history(), 424 1 = nif_mod:lib_version(), 425 1 = call(Pid,lib_version), 426 [{lib_version,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(), 427 428 upgraded = call(Pid,upgrade), 429 false = check_process_code(Pid, nif_mod), 430 true = code:soft_purge(nif_mod), 431 [{unload,1,12,112}] = nif_mod_call_history(), 432 433 1 = nif_mod:lib_version(), 434 [{lib_version,1,13,113}] = nif_mod_call_history(), 435 436 true = code:delete(nif_mod), 437 [] = nif_mod_call_history(), 438 439 440 Pid ! die, 441 {'DOWN', MRef, process, Pid, normal} = receive_any(), 442 false = check_process_code(Pid, nif_mod), 443 true = code:soft_purge(nif_mod), 444 [{unload,1,14,114}] = nif_mod_call_history(), 445 446 %% Module upgrade with different lib version 447 {module,nif_mod} = code:load_binary(nif_mod,File,Bin), 448 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 449 [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 450 451 1 = nif_mod:lib_version(), 452 {Pid2,MRef2} = nif_mod:start(), 453 1 = call(Pid2,lib_version), 454 [{lib_version,1,3,103},{lib_version,1,4,104}] = nif_mod_call_history(), 455 456 true = ets:insert(nif_SUITE,{lib_version,2}), 457 {module,nif_mod} = code:load_binary(nif_mod,File,Bin), 458 [{upgrade,2,1,201}] = nif_mod_call_history(), 459 460 2 = nif_mod:lib_version(), 461 1 = call(Pid2,lib_version), 462 [{lib_version,2,2,202},{lib_version,1,5,105}] = nif_mod_call_history(), 463 464 upgraded = call(Pid2,upgrade), 465 false = check_process_code(Pid2, nif_mod), 466 true = code:soft_purge(nif_mod), 467 [{unload,1,6,106}] = nif_mod_call_history(), 468 469 2 = nif_mod:lib_version(), 470 2 = call(Pid2,lib_version), 471 [{lib_version,2,3,203},{lib_version,2,4,204}] = nif_mod_call_history(), 472 473 true = code:delete(nif_mod), 474 [] = nif_mod_call_history(), 475 476 %% Reverse upgrade but from old (deleted) instance 477 ets:insert(nif_SUITE,{lib_version,1}), 478 {module,nif_mod} = code:load_binary(nif_mod,File,Bin), 479 [{upgrade,1,1,101}] = nif_mod_call_history(), 480 481 1 = nif_mod:lib_version(), 482 2 = call(Pid2,lib_version), 483 [{lib_version,1,2,102},{lib_version,2,5,205}] = nif_mod_call_history(), 484 485 upgraded = call(Pid2,upgrade), 486 false = check_process_code(Pid2, nif_mod), 487 true = code:soft_purge(nif_mod), 488 [{unload,2,6,206}] = nif_mod_call_history(), 489 490 1 = nif_mod:lib_version(), 491 [{lib_version,1,3,103}] = nif_mod_call_history(), 492 493 true = code:delete(nif_mod), 494 [] = nif_mod_call_history(), 495 496 497 Pid2 ! die, 498 {'DOWN', MRef2, process, Pid2, normal} = receive_any(), 499 false= check_process_code(Pid2, nif_mod), 500 true = code:soft_purge(nif_mod), 501 [{unload,1,4,104}] = nif_mod_call_history(), 502 503 true = lists:member(?MODULE, erlang:system_info(taints)), 504 true = lists:member(nif_mod, erlang:system_info(taints)), 505 verify_tmpmem(TmpMem), 506 ok. 507 508%% Test erlang:load_nif/2 waiting for code_write_permission. 509t_load_race(Config) -> 510 Data = proplists:get_value(data_dir, Config), 511 File = filename:join(Data, "nif_mod"), 512 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), 513 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 514 try 515 erts_debug:set_internal_state(code_write_permission, true), 516 Papa = self(), 517 spawn_link(fun() -> 518 ok = nif_mod:load_nif_lib(Config, 1), 519 Papa ! "NIF loaded" 520 end), 521 timer:sleep(100), 522 timeout = receive_any(0) 523 after 524 true = erts_debug:set_internal_state(code_write_permission, false) 525 end, 526 527 "NIF loaded" = receive_any(), 528 true = erlang:delete_module(nif_mod), 529 true = erlang:purge_module(nif_mod), 530 ok. 531 532-define(NIFS_NOT_LOADED, 0). 533-define(NIFS_LOADED, 1). 534 535%% Test calling functions while loading their NIF implementation. 536%% Verify the change from beam to NIF is atomic. 537t_call_nif_early(Config) -> 538 Flag = atomics:new(1, []), 539 atomics:put(Flag, 1, ?NIFS_NOT_LOADED), 540 541 Data = proplists:get_value(data_dir, Config), 542 File = filename:join(Data, "nif_mod"), 543 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), 544 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 545 546 Papa = self(), 547 NProcs = erlang:system_info(schedulers_online), 548 Pids = [spawn_opt(fun() -> 549 undefined = nif_mod:lib_version(), 550 Papa ! ready, 551 early_caller_1(Flag, 1) 552 end, 553 [link, {scheduler,N}]) 554 || N <- lists:seq(1, NProcs)], 555 556 [begin ok = receive ready -> ok end end 557 || _ <- Pids], 558 559 ok = nif_mod:load_nif_lib(Config, 1), 560 atomics:put(Flag, 1, ?NIFS_LOADED), 561 562 %% Wait for all scheduled load finishers to complete. 563 erts_debug:set_internal_state(wait, thread_progress), 564 erts_debug:set_internal_state(wait, thread_progress), 565 erts_debug:set_internal_state(wait, thread_progress), 566 567 [P ! done || P <- Pids], 568 ok. 569 570 571early_caller_1(Flag, BeamCnt) -> 572 case atomics:get(Flag, 1) of 573 ?NIFS_NOT_LOADED -> 574 case nif_mod_lib_version(BeamCnt) of 575 undefined -> % Beam called 576 early_caller_1(Flag, BeamCnt+1); 577 1 -> % Nif called 578 atomics:put(Flag, 1, ?NIFS_LOADED), 579 early_caller_2(BeamCnt, 1) 580 end; 581 ?NIFS_LOADED -> 582 %% Someone else called a NIF 583 early_caller_2(BeamCnt, 0) 584 end. 585 586early_caller_2(BeamCnt, NifCnt) -> 587 %% From now on we only expect to call NIFs 588 1 = nif_mod_lib_version(BeamCnt+NifCnt), 589 receive done -> 590 io:format("Beam calls=~p, Nif calls=~p\n", [BeamCnt, NifCnt+1]) 591 after 0 -> 592 early_caller_2(BeamCnt, NifCnt+1) 593 end. 594 595nif_mod_lib_version(Cnt) -> 596 case Cnt rem 2 of 597 0 -> nif_mod:lib_version(); 598 1 -> nif_mod:lib_version_check() 599 end. 600 601 602%% Test load of module where a NIF stub is already traced. 603load_traced_nif(Config) when is_list(Config) -> 604 TmpMem = tmpmem(), 605 606 Data = proplists:get_value(data_dir, Config), 607 File = filename:join(Data, "nif_mod"), 608 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), 609 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 610 611 Tracee = spawn_link(fun Loop() -> receive {lib_version,ExpRet} -> 612 ExpRet = nif_mod:lib_version() 613 end, 614 Loop() 615 end), 616 1 = erlang:trace_pattern({nif_mod,lib_version,0}, true, [local]), 617 1 = erlang:trace(Tracee, true, [call]), 618 619 Tracee ! {lib_version, undefined}, 620 {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000), 621 622 ok = nif_mod:load_nif_lib(Config, 1), 623 624 Tracee ! {lib_version, 1}, 625 {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000), 626 627 %% Wait for NIF loading to finish and write final call_nif instruction 628 timer:sleep(500), 629 630 Tracee ! {lib_version, 1}, 631 {trace, Tracee, call, {nif_mod,lib_version,[]}} = receive_any(1000), 632 633 true = erlang:delete_module(nif_mod), 634 true = erlang:purge_module(nif_mod), 635 636 unlink(Tracee), 637 exit(Tracee, kill), 638 639 verify_tmpmem(TmpMem), 640 ok. 641 642 643-define(ERL_NIF_SELECT_READ, (1 bsl 0)). 644-define(ERL_NIF_SELECT_WRITE, (1 bsl 1)). 645-define(ERL_NIF_SELECT_STOP, (1 bsl 2)). 646-define(ERL_NIF_SELECT_CANCEL, (1 bsl 3)). 647-define(ERL_NIF_SELECT_CUSTOM_MSG, (1 bsl 4)). 648-define(ERL_NIF_SELECT_ERROR, (1 bsl 5)). 649 650-define(ERL_NIF_SELECT_STOP_CALLED, (1 bsl 0)). 651-define(ERL_NIF_SELECT_STOP_SCHEDULED, (1 bsl 1)). 652-define(ERL_NIF_SELECT_INVALID_EVENT, (1 bsl 2)). 653-define(ERL_NIF_SELECT_FAILED, (1 bsl 3)). 654-define(ERL_NIF_SELECT_READ_CANCELLED, (1 bsl 4)). 655-define(ERL_NIF_SELECT_WRITE_CANCELLED, (1 bsl 5)). 656-define(ERL_NIF_SELECT_ERROR_CANCELLED, (1 bsl 6)). 657-define(ERL_NIF_SELECT_NOTSUP, (1 bsl 7)). 658 659select(Config) when is_list(Config) -> 660 ensure_lib_loaded(Config), 661 select_do(0, make_ref(), make_ref(), null), 662 663 RefBin = list_to_binary(lists:duplicate(100, $x)), 664 [select_do(?ERL_NIF_SELECT_CUSTOM_MSG, 665 small, {a, tuple, with, "some", RefBin}, MSG_ENV) 666 || MSG_ENV <- [null, alloc_env]], 667 ok. 668 669select_do(Flag, Ref, Ref2, MSG_ENV) -> 670 io:format("select_do(~p, ~p, ~p)\n", [Ref, Ref2, MSG_ENV]), 671 672 {{R, R_ptr}, {W, W_ptr}} = pipe_nif(), 673 ok = write_nif(W, <<"hej">>), 674 <<"hej">> = read_nif(R, 3), 675 676 %% Wait for read 677 eagain = read_nif(R, 3), 678 0 = select_nif(R,?ERL_NIF_SELECT_READ bor Flag, R,null,Ref,MSG_ENV), 679 [] = flush(0), 680 ok = write_nif(W, <<"hej">>), 681 receive_ready(R, Ref, ready_input), 682 0 = select_nif(R,?ERL_NIF_SELECT_READ bor Flag,R,self(),Ref2,MSG_ENV), 683 receive_ready(R, Ref2, ready_input), 684 Papa = self(), 685 Pid = spawn_link(fun() -> 686 receive_ready(R, Ref, ready_input), 687 Papa ! {self(), done} 688 end), 689 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, Pid, Ref,MSG_ENV), 690 {Pid, done} = receive_any(1000), 691 692 %% Cancel read 693 0 = select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref,null), 694 <<"hej">> = read_nif(R, 3), 695 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref, MSG_ENV), 696 ?ERL_NIF_SELECT_READ_CANCELLED = 697 select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref,null), 698 ok = write_nif(W, <<"hej again">>), 699 [] = flush(0), 700 <<"hej again">> = read_nif(R, 9), 701 702 %% Wait for write 703 Written = write_full(W, $a), 704 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor Flag, W, self(), Ref, MSG_ENV), 705 [] = flush(0), 706 Written = read_nif(R,byte_size(Written)), 707 receive_ready(W, Ref, ready_output), 708 709 %% Cancel write 710 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null), 711 Written2 = write_full(W, $b), 712 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor Flag, W, null, Ref, MSG_ENV), 713 ?ERL_NIF_SELECT_WRITE_CANCELLED = 714 select_nif(W, ?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null), 715 Written2 = read_nif(R,byte_size(Written2)), 716 [] = flush(0), 717 718 %% Close write and wait for EOF 719 eagain = read_nif(R, 1), 720 check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), 721 [{fd_resource_stop, W_ptr, _}] = flush(), 722 {1, {W_ptr,_}} = last_fd_stop_call(), 723 true = is_closed_nif(W), 724 [] = flush(0), 725 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, self(), Ref, MSG_ENV), 726 receive_ready(R, Ref, ready_input), 727 eof = read_nif(R,1), 728 729 check_stop_ret(select_nif(R, ?ERL_NIF_SELECT_STOP, R, null, Ref, null)), 730 [{fd_resource_stop, R_ptr, _}] = flush(), 731 {1, {R_ptr,_}} = last_fd_stop_call(), 732 true = is_closed_nif(R), 733 734 select_2(Flag, Ref, Ref2, MSG_ENV). 735 736select_2(Flag, Ref1, Ref2, MSG_ENV) -> 737 erlang:garbage_collect(), 738 {_,_,2} = last_resource_dtor_call(), 739 740 {{R, R_ptr}, {W, W_ptr}} = pipe_nif(), 741 742 %% Change ref 743 eagain = read_nif(R, 1), 744 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV), 745 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, self(), Ref2, MSG_ENV), 746 747 [] = flush(0), 748 ok = write_nif(W, <<"hej">>), 749 receive_ready(R, Ref2, ready_input), 750 <<"hej">> = read_nif(R, 3), 751 752 %% Change pid 753 eagain = read_nif(R, 1), 754 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV), 755 Papa = self(), 756 spawn_link(fun() -> 757 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV), 758 [] = flush(0), 759 Papa ! sync, 760 receive_ready(R, Ref1, ready_input), 761 <<"hej">> = read_nif(R, 3), 762 Papa ! done 763 end), 764 sync = receive_any(), 765 ok = write_nif(W, <<"hej">>), 766 done = receive_any(), 767 [] = flush(0), 768 769 check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref1, null)), 770 [{fd_resource_stop, R_ptr, _}] = flush(), 771 {1, {R_ptr,_}} = last_fd_stop_call(), 772 true = is_closed_nif(R), 773 774 %% Stop without previous read/write select 775 ?ERL_NIF_SELECT_STOP_CALLED = select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref1,null), 776 [{fd_resource_stop, W_ptr, 1}] = flush(), 777 {1, {W_ptr,1}} = last_fd_stop_call(), 778 true = is_closed_nif(W), 779 780 select_3(). 781 782select_3() -> 783 erlang:garbage_collect(), 784 {_,_,2} = last_resource_dtor_call(), 785 ok. 786 787receive_ready(R, Ref, IOatom) when is_reference(Ref) -> 788 [{select, R, Ref, IOatom}] = flush(); 789receive_ready(_, Msg, _) -> 790 [Got] = flush(), 791 {true,_,_} = {Got=:=Msg, Got, Msg}. 792 793 794select_error(Config) when is_list(Config) -> 795 ensure_lib_loaded(Config), 796 797 case os:type() of 798 {unix,linux} -> 799 select_error_do(); 800 _ -> 801 {skipped, "not Linux"} 802 end. 803 804select_error_do() -> 805 RefBin = list_to_binary(lists:duplicate(100, $x)), 806 807 select_error_do1(0, make_ref(), null), 808 select_error_do1(?ERL_NIF_SELECT_CUSTOM_MSG, [a, "list", RefBin], null), 809 select_error_do1(?ERL_NIF_SELECT_CUSTOM_MSG, [a, "list", RefBin], alloc_env), 810 ok. 811 812select_error_do1(Flag, Ref, MsgEnv) -> 813 {{R, _R_ptr}, {W, W_ptr}} = pipe_nif(), 814 ok = write_nif(W, <<"hej">>), 815 <<"hej">> = read_nif(R, 3), 816 817 %% Wait for error on write end when read end is closed 818 0 = select_nif(W, ?ERL_NIF_SELECT_ERROR bor Flag, W, null, Ref, MsgEnv), 819 [] = flush(0), 820 0 = close_nif(R), 821 receive_ready(W, Ref, ready_error), 822 823 check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), 824 [{fd_resource_stop, W_ptr, _}] = flush(), 825 {1, {W_ptr,_}} = last_fd_stop_call(), 826 true = is_closed_nif(W), 827 select_error_do2(Flag, Ref, MsgEnv). 828 829select_error_do2(Flag, Ref, MsgEnv) -> 830 {{R, _R_ptr}, {W, W_ptr}} = pipe_nif(), 831 ok = write_nif(W, <<"hej">>), 832 <<"hej">> = read_nif(R, 3), 833 834 %% Same again but test cancel of error works 835 0 = select_nif(W, ?ERL_NIF_SELECT_ERROR bor Flag, W, null, Ref, MsgEnv), 836 ?ERL_NIF_SELECT_ERROR_CANCELLED = 837 select_nif(W, ?ERL_NIF_SELECT_ERROR bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null), 838 0 = close_nif(R), 839 [] = flush(0), 840 0 = select_nif(W, ?ERL_NIF_SELECT_ERROR bor Flag, W, null, Ref, MsgEnv), 841 receive_ready(W, Ref, ready_error), 842 843 check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), 844 [{fd_resource_stop, W_ptr, _}] = flush(), 845 {1, {W_ptr,_}} = last_fd_stop_call(), 846 true = is_closed_nif(W), 847 ok. 848 849-ifdef(NOT_DEFINED). 850check_select_error_supported() -> 851 {{_R, _R_ptr}, {W, W_ptr}} = pipe_nif(), 852 Ref = make_ref(), 853 case select_nif(W, ?ERL_NIF_SELECT_ERROR, W, null, Ref, null) of 854 0 -> 855 check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), 856 [{fd_resource_stop, W_ptr, _}] = flush(), 857 {1, {W_ptr,_}} = last_fd_stop_call(), 858 true = is_closed_nif(W), 859 true; 860 861 Err when Err < 0, (Err band ?ERL_NIF_SELECT_NOTSUP) =/= 0 -> 862 false 863 end. 864-endif. 865 866%% @doc The stealing child process for the select_steal test. Duplicates given 867%% W/RFds and runs select on them to steal 868select_steal_child_process(Parent, RFd) -> 869 %% Duplicate the resource with the same FD 870 {R2Fd, _R2Ptr} = dupe_resource_nif(RFd), 871 Ref2 = make_ref(), 872 873 %% Try to select from the child pid (steal from parent) 874 ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2, null)), 875 ?assertEqual([], flush(0)), 876 ?assertEqual(eagain, read_nif(R2Fd, 1)), 877 878 %% Check that now events arrive to this temporary process 879 Parent ! {self(), stage1}, % signal parent to send the <<"stolen1">> 880 881 %% Receive <<"stolen1">> via enif_select 882 ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2, null)), 883 ?assertMatch([{select, R2Fd, Ref2, ready_input}], flush()), 884 ?assertEqual(<<"stolen1">>, read_nif(R2Fd, 7)), 885 886 clear_select_nif(R2Fd), 887 888 % do not do this here - stop_selecting(R2Fd, R2Rsrc, Ref2), 889 Parent ! {self(), done}. 890 891%% @doc Similar to select/1 test, make a double ended pipe. Then try to steal 892%% the socket, see what happens. 893select_steal(Config) when is_list(Config) -> 894 ensure_lib_loaded(Config), 895 896 Ref = make_ref(), 897 {{RFd, RPtr}, {WFd, WPtr}} = pipe_nif(), 898 899 %% Bind the socket to current pid in enif_select 900 ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, null, Ref, null)), 901 ?assertEqual([], flush(0)), 902 903 %% Spawn a process and do some stealing 904 Parent = self(), 905 Pid = spawn_link(fun() -> select_steal_child_process(Parent, RFd) end), 906 907 %% Signal from the child to send the first message 908 {Pid, stage1} = receive_any(), 909 ?assertEqual(ok, write_nif(WFd, <<"stolen1">>)), 910 911 ?assertMatch([{Pid, done}], flush(1)), % synchronize with the child 912 913 %% Try to select from the parent pid (steal back) 914 ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, Pid, Ref, null)), 915 916 %% Ensure that no data is hanging and close. 917 %% Rfd is stolen at this point. 918 check_stop_ret(select_nif(WFd, ?ERL_NIF_SELECT_STOP, WFd, null, Ref, null)), 919 ?assertMatch([{fd_resource_stop, WPtr, _}], flush()), 920 {1, {WPtr, 1}} = last_fd_stop_call(), 921 922 check_stop_ret(select_nif(RFd, ?ERL_NIF_SELECT_STOP, RFd, null, Ref, null)), 923 ?assertMatch([{fd_resource_stop, RPtr, _}], flush()), 924 {1, {RPtr, _DirectCall}} = last_fd_stop_call(), 925 926 ?assert(is_closed_nif(WFd)), 927 928 ok. 929 930check_stop_ret(?ERL_NIF_SELECT_STOP_CALLED) -> ok; 931check_stop_ret(?ERL_NIF_SELECT_STOP_SCHEDULED) -> ok. 932 933write_full(W, C) -> 934 write_full(W, C, <<>>). 935write_full(W, C, Acc) -> 936 case write_nif(W, <<C>>) of 937 ok -> 938 write_full(W, (C+1) band 255, <<Acc/binary, C>>); 939 {eagain,0} -> 940 Acc 941 end. 942 943%% Basic monitoring of one process that terminates 944monitor_process_a(Config) -> 945 ensure_lib_loaded(Config), 946 947 F = fun(Terminator, UseMsgEnv) -> 948 Pid = spawn(fun() -> 949 receive 950 {exit, Arg} -> exit(Arg); 951 return -> ok; 952 BadMatch -> goodmatch = BadMatch 953 end 954 end), 955 R_ptr = alloc_monitor_resource_nif(), 956 {0, Mon1} = monitor_process_nif(R_ptr, Pid, UseMsgEnv, self()), 957 [R_ptr] = monitored_by(Pid), 958 Terminator(Pid), 959 [{monitor_resource_down, R_ptr, Pid, Mon2}] = flush(), 960 0 = compare_monitors_nif(Mon1, Mon2), 961 [] = last_resource_dtor_call(), 962 ok = release_resource(R_ptr), 963 {R_ptr, _, 1} = last_resource_dtor_call() 964 end, 965 966 T1 = fun(Pid) -> Pid ! {exit, 17} end, 967 T2 = fun(Pid) -> Pid ! return end, 968 T3 = fun(Pid) -> Pid ! badmatch end, 969 T4 = fun(Pid) -> exit(Pid, 18) end, 970 971 [F(T, UME) || T <- [T1,T2,T3,T4], UME <- [true, false]], 972 973 ok. 974 975%% Test auto-demonitoring at resource destruction 976monitor_process_b(Config) -> 977 ensure_lib_loaded(Config), 978 979 monitor_process_b_do(false), 980 case erlang:system_info(threads) of 981 true -> monitor_process_b_do(true); 982 false -> ok 983 end, 984 ok. 985 986 987monitor_process_b_do(FromThread) -> 988 Pid = spawn_link(fun() -> 989 receive 990 return -> ok 991 end 992 end), 993 R_ptr = alloc_monitor_resource_nif(), 994 {0,_} = monitor_process_nif(R_ptr, Pid, true, self()), 995 [R_ptr] = monitored_by(Pid), 996 case FromThread of 997 false -> ok = release_resource(R_ptr); 998 true -> ok = release_resource_from_thread(R_ptr) 999 end, 1000 [] = flush(0), 1001 {R_ptr, _, 1} = last_resource_dtor_call(), 1002 [] = monitored_by(Pid), 1003 Pid ! return, 1004 ok. 1005 1006%% Test termination of monitored process holding last resource ref 1007monitor_process_c(Config) -> 1008 ensure_lib_loaded(Config), 1009 1010 Papa = self(), 1011 Pid = spawn_link(fun() -> 1012 R_ptr = alloc_monitor_resource_nif(), 1013 {0,Mon} = monitor_process_nif(R_ptr, self(), true, Papa), 1014 [R_ptr] = monitored_by(self()), 1015 put(store, make_resource(R_ptr)), 1016 ok = release_resource(R_ptr), 1017 [] = last_resource_dtor_call(), 1018 Papa ! {self(), done, R_ptr, Mon}, 1019 exit 1020 end), 1021 [{Pid, done, R_ptr, Mon1}, 1022 {monitor_resource_down, R_ptr, Pid, Mon2}] = flush(2), 1023 compare_monitors_nif(Mon1, Mon2), 1024 {R_ptr, _, 1} = last_resource_dtor_call(), 1025 ok. 1026 1027%% Test race of resource dtor called when monitored process is exiting 1028monitor_process_d(Config) -> 1029 ensure_lib_loaded(Config), 1030 1031 Papa = self(), 1032 {Target,TRef} = spawn_monitor(fun() -> 1033 nothing = receive_any() 1034 end), 1035 1036 R_ptr = alloc_monitor_resource_nif(), 1037 {0,_} = monitor_process_nif(R_ptr, Target, true, self()), 1038 [Papa, R_ptr] = monitored_by(Target), 1039 1040 exit(Target, die), 1041 ok = release_resource(R_ptr), 1042 1043 [{'DOWN', TRef, process, Target, die}] = flush(), %% no monitor_resource_down 1044 {R_ptr, _, 1} = last_resource_dtor_call(), 1045 1046 ok. 1047 1048%% OTP-16399: Test fire resource monitor after the NIF module been purged. 1049monitor_process_purge(Config) -> 1050 Data = proplists:get_value(data_dir, Config), 1051 File = filename:join(Data, "nif_mod"), 1052 {ok,nif_mod,NifModBin} = compile:file(File, [binary,return_errors]), 1053 1054 monitor_process_purge_do(Config, NifModBin, resource_dtor_A), 1055 erlang:garbage_collect(), 1056 receive after 10 -> ok end, 1057 [{{resource_dtor_A_v1,_},1,4,104}, 1058 {unload,1,5,105}] = nif_mod_call_history(), 1059 1060 %% This used to crash VM as only resources with destructor 1061 %% prevented NIF lib from being unloaded. 1062 monitor_process_purge_do(Config, NifModBin, null), 1063 erlang:garbage_collect(), 1064 receive after 10 -> ok end, 1065 [{unload,1,4,104}] = nif_mod_call_history(), 1066 ok. 1067 1068monitor_process_purge_do(Config, NifModBin, Dtor) -> 1069 io:format("Test with destructor = ~p\n", [Dtor]), 1070 1071 {module,nif_mod} = erlang:load_module(nif_mod,NifModBin), 1072 1073 ok = nif_mod:load_nif_lib(Config, 1, [{resource_type, 0, ?RT_CREATE, 1074 "monitor_process_purge", Dtor, 1075 ?RT_CREATE, resource_down_D} 1076 ]), 1077 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 1078 [{load,1,1,101}, 1079 {get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 1080 1081 {Pid,MRef} = spawn_opt(fun() -> 1082 receive 1083 return -> ok 1084 end 1085 end, 1086 [link, monitor]), 1087 RBin = <<"blahblah">>, 1088 R = nif_mod:make_new_resource(0, RBin), 1089 0 = nif_mod:monitor_process(0, R, Pid), 1090 true = erlang:delete_module(nif_mod), 1091 true = erlang:purge_module(nif_mod), 1092 Pid ! return, 1093 [{'DOWN', MRef, process, Pid, normal}] = flush(), 1094 [{{resource_down_D_v1,RBin},1,3,103}] = nif_mod_call_history(), 1095 keep_alive(R), 1096 ok. 1097 1098 1099%% Test basic demonitoring 1100demonitor_process(Config) -> 1101 ensure_lib_loaded(Config), 1102 1103 Pid = spawn_link(fun() -> 1104 receive 1105 return -> ok 1106 end 1107 end), 1108 R_ptr = alloc_monitor_resource_nif(), 1109 {0,MonBin1} = monitor_process_nif(R_ptr, Pid, true, self()), 1110 MonTerm1 = make_monitor_term_nif(MonBin1), 1111 [R_ptr] = monitored_by(Pid), 1112 {0,MonBin2} = monitor_process_nif(R_ptr, Pid, true, self()), 1113 MonTerm2 = make_monitor_term_nif(MonBin2), 1114 true = (MonTerm1 =/= MonTerm2), 1115 [R_ptr, R_ptr] = monitored_by(Pid), 1116 0 = demonitor_process_nif(R_ptr, MonBin1), 1117 [R_ptr] = monitored_by(Pid), 1118 1 = demonitor_process_nif(R_ptr, MonBin1), 1119 0 = demonitor_process_nif(R_ptr, MonBin2), 1120 [] = monitored_by(Pid), 1121 1 = demonitor_process_nif(R_ptr, MonBin2), 1122 1123 ok = release_resource(R_ptr), 1124 [] = flush(0), 1125 {R_ptr, _, 1} = last_resource_dtor_call(), 1126 [] = monitored_by(Pid), 1127 Pid ! return, 1128 1129 erlang:garbage_collect(), 1130 true = (MonTerm1 =/= MonTerm2), 1131 io:format("MonTerm1 = ~p\nMonTerm2 = ~p\n", [MonTerm1, MonTerm2]), 1132 ok. 1133 1134 1135monitored_by(Pid) -> 1136 {monitored_by, List0} = process_info(Pid, monitored_by), 1137 List1 = lists:map(fun(E) when ?is_resource(E) -> 1138 {Ptr, _} = get_resource(monitor_resource_type, E), 1139 Ptr; 1140 (E) -> E 1141 end, 1142 List0), 1143 erlang:garbage_collect(), 1144 lists:sort(List1). 1145 1146-define(FRENZY_RAND_BITS, 25). 1147 1148%% Exercise monitoring from NIF resources by randomly 1149%% create/destruct processes, resources and monitors. 1150monitor_frenzy(Config) -> 1151 ensure_lib_loaded(Config), 1152 1153 Procs1 = processes(), 1154 io:format("~p processes before: ~p\n", [length(Procs1), Procs1]), 1155 1156 %% Spawn first worker process 1157 Master = self(), 1158 spawn_link(fun() -> 1159 SelfPix = monitor_frenzy_nif(init, ?FRENZY_RAND_BITS, 0, 0), 1160 unlink(Master), 1161 frenzy(SelfPix, {undefined, []}) 1162 end), 1163 receive after 5*1000 -> ok end, 1164 1165 io:format("stats = ~p\n", [monitor_frenzy_nif(stats, 0, 0, 0)]), 1166 1167 Pids = monitor_frenzy_nif(stop, 0, 0, 0), 1168 io:format("stats = ~p\n", [monitor_frenzy_nif(stats, 0, 0, 0)]), 1169 1170 lists:foreach(fun(P) -> 1171 MRef = monitor(process, P), 1172 exit(P, stop), 1173 {'DOWN', MRef, process, P, _} = receive_any() 1174 end, 1175 Pids), 1176 1177 io:format("stats = ~p\n", [monitor_frenzy_nif(stats, 0, 0, 0)]), 1178 1179 Procs2 = processes(), 1180 io:format("~p processes after: ~p\n", [length(Procs2), Procs2]), 1181 ok. 1182 1183 1184frenzy(_SelfPix, done) -> 1185 ok; 1186frenzy(SelfPix, State0) -> 1187 Rnd = rand:uniform(1 bsl (?FRENZY_RAND_BITS+2)) - 1, 1188 Op = Rnd band 3, 1189 State1 = frenzy_do_op(SelfPix, Op, (Rnd bsr 2), State0), 1190 frenzy(SelfPix, State1). 1191 1192frenzy_do_op(SelfPix, Op, Rnd, {Pid0,RBins}=State0) -> 1193 case Op of 1194 0 -> % add/remove process 1195 Papa = self(), 1196 NewPid = case Pid0 of 1197 undefined -> % Prepare new process to be added 1198 spawn(fun() -> 1199 MRef = monitor(process, Papa), 1200 case receive_any() of 1201 {go, MyPix, MyState} -> 1202 demonitor(MRef, [flush]), 1203 frenzy(MyPix, MyState); 1204 {'DOWN', MRef, process, Papa, _} -> 1205 ok 1206 end 1207 end); 1208 _ -> 1209 Pid0 1210 end, 1211 case monitor_frenzy_nif(Op, Rnd, SelfPix, NewPid) of 1212 NewPix when is_integer(NewPix) -> 1213 NewPid ! {go, NewPix, {undefined, []}}, 1214 {undefined, RBins}; 1215 ExitPid when is_pid(ExitPid) -> 1216 false = (ExitPid =:= self()), 1217 exit(ExitPid,die), 1218 {NewPid, RBins}; 1219 done -> 1220 done 1221 end; 1222 1223 3 -> 1224 %% Try provoke revival-race of resource from magic ref external format 1225 _ = [binary_to_term(B) || B <- RBins], 1226 {Pid0, []}; 1227 _ -> 1228 case monitor_frenzy_nif(Op, Rnd, SelfPix, undefined) of 1229 Rsrc when ?is_resource(Rsrc) -> 1230 %% Store resource in ext format only, for later revival 1231 State1 = {Pid0, [term_to_binary(Rsrc) | RBins]}, 1232 gc_and_return(State1); 1233 ok -> State0; 1234 0 -> State0; 1235 1 -> State0; 1236 done -> done 1237 end 1238 end. 1239 1240gc_and_return(RetVal) -> 1241 erlang:garbage_collect(), 1242 RetVal. 1243 1244t_dynamic_resource_call(Config) -> 1245 ensure_lib_loaded(Config), 1246 Data = proplists:get_value(data_dir, Config), 1247 File = filename:join(Data, "nif_mod"), 1248 {ok,nif_mod,NifModBin} = compile:file(File, [binary,return_errors]), 1249 1250 dynamic_resource_call_do(Config, NifModBin), 1251 erlang:garbage_collect(), 1252 1253 true = erlang:delete_module(nif_mod), 1254 true = erlang:purge_module(nif_mod), 1255 1256 receive after 10 -> ok end, 1257 [{{resource_dtor_A_v1,_},1,2,102}, 1258 {unload,1,3,103}] = nif_mod_call_history(), 1259 1260 ok. 1261 1262 1263dynamic_resource_call_do(Config, NifModBin) -> 1264 {module,nif_mod} = erlang:load_module(nif_mod,NifModBin), 1265 1266 ok = nif_mod:load_nif_lib(Config, 1, 1267 [{resource_type, 0, ?RT_CREATE, "with_dyncall", 1268 resource_dtor_A, ?RT_CREATE, resource_dyncall}]), 1269 1270 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 1271 [{load,1,1,101}, 1272 {get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 1273 1274 R = nif_mod:make_new_resource(0, <<>>), 1275 1276 {0, 1001} = dynamic_resource_call(nif_mod, with_dyncall, R, 1000), 1277 {1, 1000} = dynamic_resource_call(wrong, with_dyncall, R, 1000), 1278 {1, 1000} = dynamic_resource_call(nif_mod, wrong, R, 1000), 1279 1280 %% Upgrade resource type with new dyncall implementation. 1281 {module,nif_mod} = erlang:load_module(nif_mod,NifModBin), 1282 ok = nif_mod:load_nif_lib(Config, 2, 1283 [{resource_type, 0, ?RT_TAKEOVER, "with_dyncall", 1284 resource_dtor_A, ?RT_TAKEOVER, resource_dyncall}]), 1285 [{upgrade,2,1,201}] = nif_mod_call_history(), 1286 1287 {0, 1002} = dynamic_resource_call(nif_mod, with_dyncall, R, 1000), 1288 true = erlang:purge_module(nif_mod), 1289 [{unload,1,3,103}] = nif_mod_call_history(), 1290 1291 %% Upgrade resource type with missing dyncall implementation. 1292 {module,nif_mod} = erlang:load_module(nif_mod,NifModBin), 1293 ok = nif_mod:load_nif_lib(Config, 1, 1294 [{resource_type, 0, ?RT_TAKEOVER, "with_dyncall", 1295 resource_dtor_A, ?RT_TAKEOVER, null}]), 1296 [{upgrade,1,1,101}] = nif_mod_call_history(), 1297 1298 {1, 1000} = dynamic_resource_call(nif_mod, with_dyncall, R, 1000), 1299 true = erlang:purge_module(nif_mod), 1300 [{unload,2,2,202}] = nif_mod_call_history(), 1301 ok. 1302 1303 1304 1305%% Test NIF building heap fragments 1306heap_frag(Config) when is_list(Config) -> 1307 TmpMem = tmpmem(), 1308 ensure_lib_loaded(Config), 1309 1310 heap_frag_do(1,1000000), 1311 verify_tmpmem(TmpMem), 1312 ok. 1313 1314heap_frag_do(N, Max) when N > Max -> 1315 ok; 1316heap_frag_do(N, Max) -> 1317 io:format("Create list of length ~p\n",[N]), 1318 L = lists:seq(1,N), 1319 L = list_seq(N), 1320 heap_frag_do(((N*5) div 4) + 1, Max). 1321 1322%% Type tests 1323types(Config) when is_list(Config) -> 1324 TmpMem = tmpmem(), 1325 ensure_lib_loaded(Config), 1326 ok = type_test(), 1327 lists:foreach(fun(Tpl) -> 1328 Lst = erlang:tuple_to_list(Tpl), 1329 Lst = tuple_2_list(Tpl) 1330 end, 1331 [{},{ok},{{}},{[],{}},{1,2,3,4,5}]), 1332 Stuff = [[],{},0,0.0,(1 bsl 100),(fun()-> ok end),make_ref(),self()], 1333 [eq_cmp(A,clone(B)) || A<-Stuff, B<-Stuff], 1334 1335 {IntSz, LongSz} = type_sizes(), 1336 UintMax = (1 bsl (IntSz*8)) - 1, 1337 IntMax = UintMax bsr 1, 1338 IntMin = -(IntMax+1), 1339 UlongMax = (1 bsl (LongSz*8)) - 1, 1340 LongMax = UlongMax bsr 1, 1341 LongMin = -(LongMax+1), 1342 Uint64Max = (1 bsl 64) - 1, 1343 Int64Max = Uint64Max bsr 1, 1344 Int64Min = -(Int64Max+1), 1345 Limits = [{IntMin,IntMax},{0,UintMax},{LongMin,LongMax},{0,UlongMax},{Int64Min,Int64Max},{0,Uint64Max}], 1346 io:format("Limits = ~p\n", [Limits]), 1347 lists:foreach(fun(I) -> 1348 R1 = echo_int(I), 1349 %%io:format("echo_int(~p) -> ~p\n", [I, R1]), 1350 R2 = my_echo_int(I, Limits), 1351 R1 = R2, 1352 true = (R1 =:= R2), 1353 true = (R1 == R2) 1354 end, int_list()), 1355 1356 verify_tmpmem(TmpMem), 1357 true = (compare(-1294536544000, -1178704800000) < 0), 1358 true = (compare(-1178704800000, -1294536544000) > 0), 1359 true = (compare(-295147905179352825856, -36893488147419103232) < 0), 1360 true = (compare(-36893488147419103232, -295147905179352825856) > 0), 1361 true = (compare(-29514790517935282585612345678, -36893488147419103232) < 0), 1362 true = (compare(-36893488147419103232, -29514790517935282585612345678) > 0), 1363 ok. 1364 1365int_list() -> 1366 Start = 1 bsl 200, 1367 int_list([Start], -Start). 1368int_list([N | _]=List, End) when N<End -> 1369 List; 1370int_list([N | _]=List, End) -> 1371 int_list([N - (1 + (abs(N) div 3)) | List], End). 1372 1373my_echo_int(I, Limits) -> 1374 lists:map(fun({Min,Max}) -> 1375 if I < Min -> false; 1376 I > Max -> false; 1377 true -> I 1378 end 1379 end, Limits). 1380 1381clone(X) -> 1382 binary_to_term(term_to_binary(X)). 1383 1384eq_cmp(A,B) -> 1385 eq_cmp_do(A,B), 1386 eq_cmp_do([A,B],[A,B]), 1387 eq_cmp_do({A,B},{A,B}). 1388 1389eq_cmp_do(A,B) -> 1390 %%io:format("compare ~p and ~p\n",[A,B]), 1391 Eq = (A =:= B), 1392 Eq = is_identical(A,B), 1393 Cmp = if 1394 A < B -> -1; 1395 A == B -> 0; 1396 A > B -> 1 1397 end, 1398 Cmp = case compare(A,B) of 1399 C when is_integer(C), C < 0 -> -1; 1400 0 -> 0; 1401 C when is_integer(C) -> 1 1402 end, 1403 ok. 1404 1405 1406%% Test NIF with many arguments 1407many_args(Config) when is_list(Config) -> 1408 TmpMem = tmpmem(), 1409 ensure_lib_loaded(Config ,1), 1410 ok = apply(?MODULE,many_args_100,lists:seq(1,100)), 1411 ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100), 1412 verify_tmpmem(TmpMem), 1413 ok. 1414 1415%% Test NIF binary handling. 1416binaries(Config) when is_list(Config) -> 1417 TmpMem = tmpmem(), 1418 ensure_lib_loaded(Config, 1), 1419 RefcBin = list_to_binary(lists:seq(1,255)), 1420 RefcBin = clone_bin(RefcBin), 1421 HeapBin = list_to_binary(lists:seq(1,20)), 1422 HeapBin = clone_bin(HeapBin), 1423 <<_:8,Sub1:6/binary,_/binary>> = RefcBin, 1424 <<_:8,Sub2:6/binary,_/binary>> = HeapBin, 1425 Sub1 = Sub2, 1426 Sub1 = clone_bin(Sub1), 1427 Sub2 = clone_bin(Sub2), 1428 <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin, 1429 <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin, 1430 Sub3 = Sub4, 1431 Sub3 = clone_bin(Sub3), 1432 Sub4 = clone_bin(Sub4), 1433 %% When NIFs get bitstring support 1434 %%<<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin, 1435 %%<<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin, 1436 %%Sub5 = Sub6, 1437 %%Sub5 = clone_bin(Sub5), 1438 %%Sub6 = clone_bin(Sub6), 1439 %%<<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin, 1440 %%<<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin, 1441 %%Sub7 = Sub8, 1442 %%Sub7 = clone_bin(Sub7), 1443 %%Sub8 = clone_bin(Sub8), 1444 %%<<>> = clone_bin(<<>>), 1445 1446 <<_:8,SubBinA:200/binary,_/binary>> = RefcBin, 1447 <<_:9,SubBinB:200/binary,_/bitstring>> = RefcBin, 1448 <<_:8,SubBinC:17/binary,_/binary>> = HeapBin, 1449 <<_:9,SubBinD:17/binary,_/bitstring>> = HeapBin, 1450 test_make_sub_bin(RefcBin), 1451 test_make_sub_bin(HeapBin), 1452 test_make_sub_bin(SubBinA), 1453 test_make_sub_bin(SubBinB), 1454 test_make_sub_bin(SubBinC), 1455 test_make_sub_bin(SubBinD), 1456 1457 verify_tmpmem(TmpMem), 1458 ok. 1459 1460test_make_sub_bin(Bin) -> 1461 Size = byte_size(Bin), 1462 Rest10 = Size - 10, 1463 Rest1 = Size - 1, 1464 Bin = make_sub_bin(Bin, 0, Size), 1465 <<_:10/binary,Sub0:Rest10/binary>> = Bin, 1466 Sub0 = make_sub_bin(Bin, 10, Rest10), 1467 <<Sub1:10/binary,_/binary>> = Bin, 1468 Sub1 = make_sub_bin(Bin, 0, 10), 1469 <<_:7/binary,Sub2:10/binary,_/binary>> = Bin, 1470 Sub2 = make_sub_bin(Bin, 7, 10), 1471 <<>> = make_sub_bin(Bin, 0, 0), 1472 <<>> = make_sub_bin(Bin, 10, 0), 1473 <<>> = make_sub_bin(Bin, Rest1, 0), 1474 <<>> = make_sub_bin(Bin, Size, 0), 1475 ok. 1476 1477%% Test enif_get_string 1478get_string(Config) when is_list(Config) -> 1479 ensure_lib_loaded(Config, 1), 1480 {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10), 1481 {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8), 1482 {7, <<"hejsan",0>>} = string_to_bin("hejsan",7), 1483 {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6), 1484 {-5, <<"hejs",0>>} = string_to_bin("hejsan",5), 1485 {-1, <<0>>} = string_to_bin("hejsan",1), 1486 {0, <<>>} = string_to_bin("hejsan",0), 1487 {1, <<0>>} = string_to_bin("",1), 1488 {0, <<>>} = string_to_bin("",0), 1489 ok. 1490 1491%% Test enif_get_atom 1492get_atom(Config) when is_list(Config) -> 1493 ensure_lib_loaded(Config, 1), 1494 {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10), 1495 {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8), 1496 {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7), 1497 {0, <<_:6/binary>>} = atom_to_bin(hejsan,6), 1498 {0, <<>>} = atom_to_bin(hejsan,0), 1499 {1, <<0>>} = atom_to_bin('',1), 1500 {0, <<>>} = atom_to_bin('',0), 1501 ok. 1502 1503%% Test NIF maps handling. 1504maps(Config) when is_list(Config) -> 1505 TmpMem = tmpmem(), 1506 Pairs = [{adam, "bert"}] ++ 1507 [{I,I}||I <- lists:seq(1,10)] ++ 1508 [{a,value},{"a","value"},{<<"a">>,<<"value">>}], 1509 ok = ensure_lib_loaded(Config, 1), 1510 M = maps_from_list_nif(Pairs), 1511 R = {RIs,Is} = sorted_list_from_maps_nif(M), 1512 io:format("Pairs: ~p~nMap: ~p~nReturned: ~p~n", [lists:sort(Pairs),M,R]), 1513 true = (lists:sort(Is) =:= lists:sort(Pairs)), 1514 Is = lists:reverse(RIs), 1515 1516 #{} = maps_from_list_nif([]), 1517 {[],[]} = sorted_list_from_maps_nif(#{}), 1518 1519 1 = is_map_nif(M), 1520 0 = is_map_nif("no map"), 1521 1522 Msz = map_size(M), 1523 {1,Msz} = get_map_size_nif(M), 1524 {1,0} = get_map_size_nif(#{}), 1525 {0,-123} = get_map_size_nif({#{}}), 1526 1527 #{} = M0 = make_new_map_nif(), 1528 1529 {1, #{key := value}=M1} = make_map_put_nif(M0, key, value), 1530 {1, #{key := value, "key2" := "value2"}=M2} = make_map_put_nif(M1, "key2", "value2"), 1531 {1, #{key := "value", "key2" := "value2"}=M3} = make_map_put_nif(M2, key, "value"), 1532 {0, undefined} = make_map_put_nif(666, key, value), 1533 1534 {1, "value2"} = get_map_value_nif(M3,"key2"), 1535 {0, undefined} = get_map_value_nif(M3,"key3"), 1536 {0, undefined} = get_map_value_nif(false,key), 1537 1538 {0, undefined} = make_map_update_nif(M0, key, value), 1539 {0, undefined} = make_map_update_nif(M1, "key2", "value2"), 1540 {1, #{key := "value", "key2" := "value2"}} = make_map_update_nif(M2, key, "value"), 1541 {0, undefined} = make_map_update_nif(666, key, value), 1542 1543 {1, #{}} = make_map_remove_nif(M1, key), 1544 {1, M1} = make_map_remove_nif(M2, "key2"), 1545 {1, M2} = make_map_remove_nif(M2, "key3"), 1546 {0, undefined} = make_map_remove_nif(self(), key), 1547 1548 M1 = maps_from_list_nif(maps:to_list(M1)), 1549 M2 = maps_from_list_nif(maps:to_list(M2)), 1550 M3 = maps_from_list_nif(maps:to_list(M3)), 1551 1552 %% Test different map sizes (OTP-15567) 1553 repeat_while(fun({35,_}) -> false; 1554 ({K,Map}) -> 1555 Map = maps_from_list_nif(maps:to_list(Map)), 1556 Map = maps:filter(fun(K2,V) -> V =:= K2*100 end, Map), 1557 {K+1, maps:put(K,K*100,Map)} 1558 end, 1559 {1,#{}}), 1560 1561 M5 = lists:foldl(fun(N, MapIn) -> 1562 {1, #{N := value}=MapOut} = make_map_put_nif(MapIn, N, value), 1563 MapOut 1564 end, 1565 #{}, 1566 lists:seq(1,40)), 1567 M6 = lists:foldl(fun(N, MapIn) -> 1568 {1, MapOut} = make_map_remove_nif(MapIn, N), 1569 ok = maps:get(N, MapOut, ok), 1570 MapOut 1571 end, 1572 M5, 1573 lists:seq(1,40)), 1574 true = (M6 =:= #{}), 1575 1576 has_duplicate_keys = maps_from_list_nif([{1,1},{1,1}]), 1577 1578 verify_tmpmem(TmpMem), 1579 ok. 1580 1581%% Test macros enif_make_list<N> and enif_make_tuple<N> 1582api_macros(Config) when is_list(Config) -> 1583 ensure_lib_loaded(Config, 1), 1584 Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)], 1585 [list_to_tuple(lists:seq(1,N)) || N <- lists:seq(1,9)] 1586 }, 1587 Expected = macros(list_to_tuple(lists:seq(1,9))), 1588 ok. 1589 1590%% enif_make_[tuple|list]_from_array 1591from_array(Config) when is_list(Config) -> 1592 ensure_lib_loaded(Config, 1), 1593 lists:foreach(fun(Tpl) -> 1594 Lst = tuple_to_list(Tpl), 1595 {Lst,Tpl} = tuple_2_list_and_tuple(Tpl) 1596 end, 1597 [{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]), 1598 ok. 1599 1600%% enif_inspect_iolist_as_binary 1601iolist_as_binary(Config) when is_list(Config) -> 1602 ensure_lib_loaded(Config, 1), 1603 TmpMem = tmpmem(), 1604 List = [<<"hejsan">>, <<>>, [], [17], [<<>>], 1605 [127,128,255,0], 1606 [1, 2, 3, <<"abc">>, [<<"def">>,4], 5, <<"ghi">>], 1607 [1, 2, 3, <<"abc">>, [<<"def">>,4], 5 | <<"ghi">>]], 1608 1609 lists:foreach(fun(IoL) -> 1610 B1 = erlang:iolist_to_binary(IoL), 1611 B2 = iolist_2_bin(IoL), 1612 B1 = B2 1613 end, 1614 List), 1615 verify_tmpmem(TmpMem), 1616 ok. 1617 1618%% Test memory managed objects, aka 'resources' 1619resource(Config) when is_list(Config) -> 1620 ensure_lib_loaded(Config, 1), 1621 Type = get_resource_type(0), 1622 resource_hugo(Type), 1623 resource_otto(Type), 1624 resource_new(Type), 1625 resource_neg(Type), 1626 ok. 1627 1628resource_hugo(Type) -> 1629 DtorCall = resource_hugo_do(Type), 1630 erlang:garbage_collect(), 1631 DtorCall = last_resource_dtor_call(), 1632 ok. 1633 1634resource_hugo_do(Type) -> 1635 HugoBin = <<"Hugo Hacker">>, 1636 HugoPtr = alloc_resource(Type, HugoBin), 1637 Hugo = make_resource(HugoPtr), 1638 true = is_reference(Hugo), 1639 release_resource(HugoPtr), 1640 erlang:garbage_collect(), 1641 {HugoPtr,HugoBin} = get_resource(Type,Hugo), 1642 {Pid,_} = 1643 spawn_monitor(fun() -> 1644 receive {Pid, Type, Resource, Ptr, Bin} -> 1645 Pid ! {self(), got_it}, 1646 receive {Pid, check_it} -> 1647 {Ptr,Bin} = get_resource(Type,Resource) 1648 end 1649 end, 1650 gc_and_exit(ok) 1651 end), 1652 Pid ! {self(), Type, Hugo, HugoPtr, HugoBin}, 1653 {Pid, got_it} = receive_any(), 1654 erlang:garbage_collect(), % just to make our ProcBin move in memory 1655 Pid ! {self(), check_it}, 1656 {'DOWN', _, process, Pid, ok} = receive_any(), 1657 [] = last_resource_dtor_call(), 1658 {HugoPtr,HugoBin} = get_resource(Type,Hugo), 1659 {HugoPtr, HugoBin, 1}. 1660 1661gc_and_exit(Reason) -> 1662 erlang:garbage_collect(), 1663 exit(Reason). 1664 1665resource_otto(Type) -> 1666 {OttoPtr, OttoBin} = resource_otto_do(Type), 1667 erlang:garbage_collect(), 1668 [] = last_resource_dtor_call(), 1669 release_resource(OttoPtr), 1670 {OttoPtr,OttoBin,1} = last_resource_dtor_call(), 1671 ok. 1672 1673resource_otto_do(Type) -> 1674 OttoBin = <<"Otto Ordonnans">>, 1675 OttoPtr = alloc_resource(Type, OttoBin), 1676 Otto = make_resource(OttoPtr), 1677 true = is_reference(Otto), 1678 %% forget resource term but keep referenced by NIF 1679 {OttoPtr, OttoBin}. 1680 1681resource_new(Type) -> 1682 {PtrB,BinB} = resource_new_do1(Type), 1683 erlang:garbage_collect(), 1684 {PtrB,BinB,1} = last_resource_dtor_call(), 1685 ok. 1686 1687resource_new_do1(Type) -> 1688 {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type), 1689 erlang:garbage_collect(), 1690 {PtrA,BinA,1} = last_resource_dtor_call(), 1691 {PtrB,BinB} = get_resource(Type, ResB), 1692 %% forget ResB and make it garbage 1693 {PtrB,BinB}. 1694 1695resource_new_do2(Type) -> 1696 BinA = <<"NewA">>, 1697 BinB = <<"NewB">>, 1698 ResA = make_new_resource(Type, BinA), 1699 ResB = make_new_resource(Type, BinB), 1700 true = is_reference(ResA), 1701 true = is_reference(ResB), 1702 true = (ResA /= ResB), 1703 {PtrA,BinA} = get_resource(Type, ResA), 1704 {PtrB,BinB} = get_resource(Type, ResB), 1705 true = (PtrA =/= PtrB), 1706 %% forget ResA and make it garbage 1707 {{PtrA,BinA}, {ResB,PtrB,BinB}}. 1708 1709resource_neg(TypeA) -> 1710 resource_neg_do(TypeA), 1711 1712 catch exit(42), % dummy exception to purge saved stacktraces from earlier exception 1713 erlang:garbage_collect(), 1714 {_,_,2} = last_resource_dtor_call(), 1715 ok. 1716 1717resource_neg_do(TypeA) -> 1718 TypeB = get_resource_type(1), 1719 ResA = make_new_resource(TypeA, <<"Arnold">>), 1720 ResB= make_new_resource(TypeB, <<"Bobo">>), 1721 {'EXIT',{badarg,_}} = (catch get_resource(TypeA, ResB)), 1722 {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)), 1723 ok. 1724 1725%% Test enif_make_resource_binary 1726resource_binary(Config) when is_list(Config) -> 1727 ensure_lib_loaded(Config, 1), 1728 {Ptr,Bin} = resource_binary_do(), 1729 erlang:garbage_collect(), 1730 Last = last_resource_dtor_call(), 1731 ?CHECK({Ptr,Bin,1}, Last), 1732 ok. 1733 1734resource_binary_do() -> 1735 Bin = <<"Hej Hopp i lingonskogen">>, 1736 {Ptr,ResBin1} = make_new_resource_binary(Bin), 1737 ResBin1 = Bin, 1738 ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1), 1739 1740 Papa = self(), 1741 {Forwarder,_} = spawn_monitor(fun() -> forwarder(Papa) end), 1742 io:format("sending to forwarder pid=~p\n",[Forwarder]), 1743 Forwarder ! ResBin1, 1744 ResBin2 = receive_any(), 1745 ResBin2 = ResBin1, 1746 ResInfo = get_resource(binary_resource_type,ResBin2), 1747 Forwarder ! terminate, 1748 {'DOWN', _, process, Forwarder, 1} = receive_any(), 1749 erlang:garbage_collect(), 1750 ResInfo = get_resource(binary_resource_type,ResBin1), 1751 ResInfo = get_resource(binary_resource_type,ResBin2), 1752 ResInfo. 1753 1754%% Test resource takeover by module upgrade 1755resource_takeover(Config) when is_list(Config) -> 1756 TmpMem = tmpmem(), 1757 ensure_lib_loaded(Config), 1758 1759 Data = proplists:get_value(data_dir, Config), 1760 File = filename:join(Data, "nif_mod"), 1761 {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]), 1762 {module,nif_mod} = erlang:load_module(nif_mod,ModBin), 1763 1764 ok = nif_mod:load_nif_lib(Config, 1, 1765 [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A, 1766 ?RT_CREATE}, 1767 {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null, 1768 ?RT_CREATE}, 1769 {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, 1770 ?RT_CREATE}, 1771 {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, 1772 ?RT_CREATE}, 1773 {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null, 1774 ?RT_CREATE}, 1775 {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, 1776 ?RT_TAKEOVER} 1777 ]), 1778 1779 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 1780 [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 1781 1782 {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]), 1783 1784 {A1,BinA1} = make_resource(0,Holder,"A1"), 1785 {A2,BinA2} = make_resource(0,Holder,"A2"), 1786 {A3,BinA3} = make_resource(0,Holder,"A3"), 1787 1788 {NA1,_BinNA1} = make_resource(1,Holder,"NA1"), 1789 {NA2,BinNA2} = make_resource(1,Holder,"NA2"), 1790 {NA3,_BinNA3} = make_resource(1,Holder,"NA3"), 1791 1792 {AN1,BinAN1} = make_resource(2,Holder,"AN1"), 1793 {AN2,_BinAN2} = make_resource(2,Holder,"AN2"), 1794 {AN3,BinAN3} = make_resource(2,Holder,"AN3"), 1795 1796 {BGX1,BinBGX1} = make_resource(3,Holder,"BGX1"), 1797 {BGX2,BinBGX2} = make_resource(3,Holder,"BGX2"), 1798 1799 {NGX1,_BinNGX1} = make_resource(4,Holder,"NGX1"), 1800 {NGX2,_BinNGX2} = make_resource(4,Holder,"NGX2"), 1801 1802 [] = nif_mod_call_history(), 1803 1804 ok = forget_resource(A1), 1805 [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(), 1806 1807 ok = forget_resource(NA1), 1808 [] = nif_mod_call_history(), % no dtor 1809 1810 ok = forget_resource(AN1), 1811 ?CHECK([{{resource_dtor_A_v1,BinAN1},1,4,104}] , nif_mod_call_history()), 1812 1813 ok = forget_resource(BGX1), 1814 ?CHECK([{{resource_dtor_B_v1,BinBGX1},1,5,105}], nif_mod_call_history()), 1815 1816 ok = forget_resource(NGX1), 1817 ?CHECK([], nif_mod_call_history()), % no dtor 1818 1819 {module,nif_mod} = erlang:load_module(nif_mod,ModBin), 1820 ok = nif_mod:load_nif_lib(Config, 2, 1821 [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, 1822 ?RT_TAKEOVER}, 1823 {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, 1824 ?RT_TAKEOVER}, 1825 {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null, 1826 ?RT_TAKEOVER}, 1827 {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, 1828 ?RT_TAKEOVER}, 1829 {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, 1830 ?RT_CREATE}, 1831 {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null, 1832 ?RT_CREATE}, 1833 {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B, 1834 ?RT_CREATE}, 1835 {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null, 1836 ?RT_CREATE} 1837 ]), 1838 ?CHECK([{upgrade,2,1,201}], nif_mod_call_history()), 1839 true = erlang:purge_module(nif_mod), 1840 ?CHECK([], nif_mod_call_history()), % BGX2 keeping lib loaded 1841 1842 BinA2 = read_resource(0,A2), 1843 ok = forget_resource(A2), 1844 ?CHECK([{{resource_dtor_A_v2,BinA2},2,2,202}], nif_mod_call_history()), 1845 1846 ok = forget_resource(NA2), 1847 ?CHECK([{{resource_dtor_A_v2,BinNA2},2,3,203}], nif_mod_call_history()), 1848 1849 ok = forget_resource(AN2), 1850 ?CHECK([], nif_mod_call_history()), % no dtor 1851 1852 ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded 1853 ?CHECK([{{resource_dtor_B_v1,BinBGX2},1,6,106}, {unload,1,7,107}], 1854 nif_mod_call_history()), 1855 1856 ok = forget_resource(NGX2), 1857 ?CHECK([], nif_mod_call_history()), % no dtor 1858 1859 {BGY1,BinBGY1} = make_resource(3,Holder,"BGY1"), 1860 {NGY1,_BinNGY1} = make_resource(4,Holder,"NGY1"), 1861 1862 %% Module upgrade with same lib-version 1863 {module,nif_mod} = erlang:load_module(nif_mod,ModBin), 1864 undefined = nif_mod:lib_version(), 1865 ok = nif_mod:load_nif_lib(Config, 2, 1866 [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, 1867 ?RT_TAKEOVER}, 1868 {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, 1869 ?RT_TAKEOVER}, 1870 {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, 1871 ?RT_TAKEOVER}, 1872 {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A, 1873 ?RT_TAKEOVER}, 1874 {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B, 1875 ?RT_CREATE}, 1876 {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null, 1877 ?RT_CREATE} 1878 ]), 1879 1880 2 = nif_mod:lib_version(), 1881 ?CHECK([{upgrade,2,4,204},{lib_version,2,5,205}], nif_mod_call_history()), 1882 1883 ok = forget_resource(A3), 1884 ?CHECK([{{resource_dtor_B_v2,BinA3},2,6,206}], nif_mod_call_history()), 1885 1886 ok = forget_resource(NA3), 1887 ?CHECK([], nif_mod_call_history()), 1888 1889 ok = forget_resource(AN3), 1890 ?CHECK([{{resource_dtor_A_v2,BinAN3},2,7,207}], nif_mod_call_history()), 1891 1892 {A4,BinA4} = make_resource(2,Holder, "A4"), 1893 {NA4,BinNA4} = make_resource(0,Holder, "NA4"), 1894 {AN4,_BinAN4} = make_resource(1,Holder, "AN4"), 1895 1896 {BGZ1,BinBGZ1} = make_resource(3,Holder,"BGZ1"), 1897 {NGZ1,_BinNGZ1} = make_resource(4,Holder,"NGZ1"), 1898 1899 false = code:purge(nif_mod), 1900 [] = nif_mod_call_history(), 1901 1902 ok = forget_resource(NGY1), 1903 [] = nif_mod_call_history(), 1904 1905 ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded 1906 [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(), 1907 1908 %% Module upgrade with other lib-version 1909 {module,nif_mod} = erlang:load_module(nif_mod,ModBin), 1910 undefined = nif_mod:lib_version(), 1911 ok = nif_mod:load_nif_lib(Config, 1, 1912 [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, 1913 ?RT_TAKEOVER}, 1914 {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, 1915 ?RT_TAKEOVER}, 1916 {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",null, 1917 ?RT_TAKEOVER}, 1918 {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, 1919 ?RT_TAKEOVER} 1920 ]), 1921 1922 1 = nif_mod:lib_version(), 1923 [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), 1924 1925 %%false= check_process_code(Pid, nif_mod), 1926 false = code:purge(nif_mod), 1927 %% no unload here as we still have instances with destructors 1928 [] = nif_mod_call_history(), 1929 1930 ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded 1931 [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(), 1932 1933 ok = forget_resource(NGZ1), 1934 [] = nif_mod_call_history(), 1935 1936 ok = forget_resource(A4), 1937 [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(), 1938 1939 ok = forget_resource(NA4), 1940 [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(), 1941 1942 ok = forget_resource(AN4), 1943 [] = nif_mod_call_history(), 1944 1945 1946 %% 1947 %% Test rollback after failed upgrade of same lib-version 1948 %% 1949 1950 {A5,BinA5} = make_resource(2, Holder, "A5"), 1951 {NA5,BinNA5} = make_resource(0, Holder, "NA5"), 1952 {AN5,_BinAN5} = make_resource(1, Holder, "AN5"), 1953 1954 {A6,BinA6} = make_resource(2, Holder, "A6"), 1955 {NA6,BinNA6} = make_resource(0, Holder, "NA6"), 1956 {AN6,_BinAN6} = make_resource(1, Holder, "AN6"), 1957 1958 {module,nif_mod} = erlang:load_module(nif_mod,ModBin), 1959 undefined = nif_mod:lib_version(), 1960 {error,{upgrade,_}} = 1961 nif_mod:load_nif_lib(Config, 1, 1962 [{resource_type, 4, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, 1963 ?RT_TAKEOVER}, 1964 {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, 1965 ?RT_TAKEOVER}, 1966 {resource_type, 4, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, 1967 ?RT_TAKEOVER}, 1968 {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, 1969 ?RT_CREATE}, 1970 1971 {return, 1} % FAIL 1972 ]), 1973 1974 undefined = nif_mod:lib_version(), 1975 [{upgrade,1,5,105}] = nif_mod_call_history(), 1976 1977 %% Make sure dtor was not changed (from A to B) 1978 ok = forget_resource(A5), 1979 [{{resource_dtor_A_v1,BinA5},1,6,106}] = nif_mod_call_history(), 1980 1981 %% Make sure dtor was not nullified (from A to null) 1982 ok = forget_resource(NA5), 1983 [{{resource_dtor_A_v1,BinNA5},1,7,107}] = nif_mod_call_history(), 1984 1985 %% Make sure dtor was not added (from null to A) 1986 ok = forget_resource(AN5), 1987 [] = nif_mod_call_history(), 1988 1989 %% 1990 %% Test rollback after failed upgrade of other lib-version 1991 %% 1992 1993 {error,{upgrade,_}} = 1994 nif_mod:load_nif_lib(Config, 2, 1995 [{resource_type, 4, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, 1996 ?RT_TAKEOVER}, 1997 {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, 1998 ?RT_TAKEOVER}, 1999 {resource_type, 4, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, 2000 ?RT_TAKEOVER}, 2001 {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, 2002 ?RT_TAKEOVER}, 2003 {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, 2004 ?RT_CREATE}, 2005 2006 {return, 1} % FAIL 2007 ]), 2008 2009 undefined = nif_mod:lib_version(), 2010 [{upgrade,2,_,_}] = nif_mod_call_history(), 2011 2012 %% Make sure dtor was not changed (from A to B) 2013 ok = forget_resource(A6), 2014 [{{resource_dtor_A_v1,BinA6},1,_,_}] = nif_mod_call_history(), 2015 2016 %% Make sure dtor was not nullified (from A to null) 2017 ok = forget_resource(NA6), 2018 [{{resource_dtor_A_v1,BinNA6},1,_,_}] = nif_mod_call_history(), 2019 2020 %% Make sure dtor was not added (from null to A) 2021 ok = forget_resource(AN6), 2022 [] = nif_mod_call_history(), 2023 2024 %% 2025 %% Test rolback after failed initial load 2026 %% 2027 false = code:purge(nif_mod), 2028 [{unload,1,_,_}] = nif_mod_call_history(), 2029 true = code:delete(nif_mod), 2030 false = code:purge(nif_mod), 2031 [] = nif_mod_call_history(), 2032 2033 2034 {module,nif_mod} = erlang:load_module(nif_mod,ModBin), 2035 undefined = nif_mod:lib_version(), 2036 {error,{load,_}} = 2037 nif_mod:load_nif_lib(Config, 1, 2038 [{resource_type, null, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, 2039 ?RT_TAKEOVER}, 2040 {resource_type, 4, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, 2041 ?RT_CREATE}, 2042 {resource_type, 4, ?RT_CREATE, "resource_type_A_null",resource_dtor_A, 2043 ?RT_CREATE}, 2044 {resource_type, 4, ?RT_CREATE, "Mr Pink", resource_dtor_A, 2045 ?RT_CREATE}, 2046 2047 {return, 1} % FAIL 2048 ]), 2049 2050 undefined = nif_mod:lib_version(), 2051 ok = nif_mod:load_nif_lib(Config, 1, 2052 [{resource_type, null, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, 2053 ?RT_TAKEOVER}, 2054 {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A", 2055 resource_dtor_A, ?RT_CREATE}, 2056 2057 {resource_type, 1, ?RT_CREATE, "resource_type_A_null", null, 2058 ?RT_CREATE}, 2059 {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, 2060 ?RT_TAKEOVER}, 2061 2062 {return, 0} % SUCCESS 2063 ]), 2064 2065 hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), 2066 [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), 2067 2068 {NA7,BinNA7} = make_resource(0, Holder, "NA7"), 2069 {AN7,_BinAN7} = make_resource(1, Holder, "AN7"), 2070 2071 ok = forget_resource(NA7), 2072 [{{resource_dtor_A_v1,BinNA7},1,_,_}] = nif_mod_call_history(), 2073 2074 ok = forget_resource(AN7), 2075 [] = nif_mod_call_history(), 2076 2077 true = erlang:delete_module(nif_mod), 2078 true = erlang:purge_module(nif_mod), 2079 2080 true = lists:member(?MODULE, erlang:system_info(taints)), 2081 true = lists:member(nif_mod, erlang:system_info(taints)), 2082 verify_tmpmem(TmpMem), 2083 ok. 2084 2085make_resource(Type,Holder,Str) when is_list(Str) -> 2086 Bin = list_to_binary(Str), 2087 A1 = make_resource_do(Type,Holder,Bin), 2088 Bin = read_resource(Type,A1), 2089 {A1,Bin}. 2090 2091make_resource_do(Type, Holder, Bin) -> 2092 Holder ! {self(), make, Type, Bin}, 2093 {Holder, make_ok, Id} = receive_any(), 2094 {Holder,Id}. 2095 2096read_resource(Type, {Holder,Id}) -> 2097 Holder ! {self(), get, Type, Id}, 2098 {Holder, get_ok, Bin} = receive_any(), 2099 Bin. 2100 2101forget_resource({Holder,Id}) -> 2102 Holder ! {self(), forget, Id}, 2103 {Holder, forget_ok, Id} = receive_any(), 2104 erts_debug:set_internal_state(wait, aux_work), 2105 ok. 2106 2107 2108resource_holder() -> 2109 resource_holder([]). 2110resource_holder(List) -> 2111 %%io:format("resource_holder waiting for msg\n", []), 2112 Msg = receive_any(), 2113 %%io:format("resource_holder got ~p with list = ~p\n", [Msg,List]), 2114 case Msg of 2115 {Pid, make, Type, Bin} -> 2116 Resource = nif_mod:make_new_resource(Type, Bin), 2117 Id = {make_ref(),Bin}, 2118 Pid ! {self(), make_ok, Id}, 2119 resource_holder([{Id,Resource} | List]); 2120 {Pid, get, Type, Id} -> 2121 {Id,Resource} = lists:keyfind(Id, 1, List), 2122 Pid ! {self(), get_ok, nif_mod:get_resource(Type, Resource)}, 2123 resource_holder(List); 2124 2125 {Pid, forget, Id} -> 2126 NewList = lists:keydelete(Id, 1, List), 2127 %%io:format("resource_holder forget: NewList = ~p\n", [NewList]), 2128 resource_holder(Pid, {self(),forget_ok,Id}, NewList) 2129 end. 2130 2131resource_holder(Pid,Reply,List) -> 2132 erlang:garbage_collect(), 2133 %%io:format("resource_holder GC'ed, now send ~p to ~p\n", [Reply,Pid]), 2134 Pid ! Reply, 2135 resource_holder(List). 2136 2137 2138%% Test the threading API functions (reuse tests from driver API) 2139threading(Config) when is_list(Config) -> 2140 case erlang:system_info(threads) of 2141 true -> threading_do(Config); 2142 false -> {skipped,"No thread support"} 2143 end. 2144 2145threading_do(Config) -> 2146 Data = proplists:get_value(data_dir, Config), 2147 File = filename:join(Data, "tester"), 2148 {ok,tester,ModBin} = compile:file(File, [binary,return_errors]), 2149 {module,tester} = erlang:load_module(tester,ModBin), 2150 2151 ok = tester:load_nif_lib(Config, "basic"), 2152 ok = tester:run(), 2153 2154 erlang:load_module(tester,ModBin), 2155 erlang:purge_module(tester), 2156 ok = tester:load_nif_lib(Config, "rwlock"), 2157 ok = tester:run(), 2158 2159 erlang:load_module(tester,ModBin), 2160 erlang:purge_module(tester), 2161 ok = tester:load_nif_lib(Config, "tsd"), 2162 ok = tester:run(), 2163 2164 erlang:delete_module(tester), 2165 erlang:purge_module(tester). 2166 2167 2168%% Test NIF message sending 2169send(Config) when is_list(Config) -> 2170 ensure_lib_loaded(Config), 2171 2172 N = 1500, 2173 List = lists:seq(1,N), 2174 {ok,1} = send_list_seq(N, self), 2175 {ok,1} = send_list_seq(N, self()), 2176 List = receive_any(), 2177 List = receive_any(), 2178 Papa = self(), 2179 spawn_link(fun() -> {ok,1} = send_list_seq(N, Papa) end), 2180 List = receive_any(), 2181 2182 {ok, 1, BlobS} = send_new_blob(self(), other_term()), 2183 BlobR = receive_any(), 2184 io:format("Sent ~p\nGot ~p\n", [BlobS, BlobR]), 2185 BlobR = BlobS, 2186 2187 %% send to dead pid 2188 {DeadPid, DeadMon} = spawn_monitor(fun() -> void end), 2189 {'DOWN', DeadMon, process, DeadPid, normal} = receive_any(), 2190 {ok,0} = send_list_seq(7, DeadPid), 2191 ok. 2192 2193 2194%% Test tracing of enif_send 2195send_trace(Config) when is_list(Config) -> 2196 ensure_lib_loaded(Config), 2197 2198 Papa = self(), 2199 N = 1500, 2200 List = lists:seq(1,N), 2201 2202 Tracer = spawn_link(fun F() -> receive get -> Papa ! receive_any(), F() end end), 2203 2204 erlang:trace(self(), true, [send,'receive',{tracer,Tracer}]), 2205 {ok,1} = send_list_seq(N, self()), 2206 List = receive_any(), 2207 timeout = receive_any(0), 2208 Tracer ! get, 2209 {trace,Papa,send,List,Papa} = receive_any(), 2210 Tracer ! get, 2211 {trace,Papa,'receive',List} = receive_any(). 2212 2213%% Test that seq_trace works with nif trace 2214send_seq_trace(Config) when is_list(Config) -> 2215 ensure_lib_loaded(Config), 2216 2217 Papa = self(), 2218 N = 1500, 2219 List = lists:seq(1,N), 2220 Label = make_ref(), 2221 2222 Tracer = spawn_link(fun F() -> receive get -> Papa ! receive_any(), F() end end), 2223 2224 seq_trace:set_system_tracer(Tracer), 2225 seq_trace:set_token(label,Label), 2226 seq_trace:set_token(send,true), 2227 seq_trace:set_token('receive',true), 2228 2229 {ok,1} = send_list_seq(N, self()), 2230 List = receive_any(), 2231 timeout = receive_any(0), 2232 {ok,1} = send_list_seq(N, self()), 2233 List = receive_any(), 2234 timeout = receive_any(0), 2235 2236 Tracer ! get, 2237 {seq_trace,Label,{send,{0,1},Papa,Papa,List}} = receive_any(), 2238 Tracer ! get, 2239 {seq_trace,Label,{'receive',{0,1},Papa,Papa,List}} = receive_any(), 2240 Tracer ! get, 2241 {seq_trace,Label,{send,{1,2},Papa,Papa,List}} = receive_any(), 2242 Tracer ! get, 2243 {seq_trace,Label,{'receive',{1,2},Papa,Papa,List}} = receive_any(). 2244 2245 2246%% More NIF message sending 2247send2(Config) when is_list(Config) -> 2248 ensure_lib_loaded(Config), 2249 2250 send2_do1(fun send_blob_dbg/2), 2251 ok. 2252 2253%% Send msg from user thread 2254send_threaded(Config) when is_list(Config) -> 2255 send2_do1(fun(ME,To) -> send_blob_thread_dbg(ME,To,join) end), 2256 send2_do1(fun(ME,To) -> send_blob_thread_and_join(ME,To) end), 2257 ok. 2258 2259 2260send2_do1(SendBlobF) -> 2261 io:format("sending to self=~p\n",[self()]), 2262 send2_do2(SendBlobF, self()), 2263 2264 Papa = self(), 2265 {Forwarder,_} = spawn_monitor(fun() -> forwarder(Papa) end), 2266 io:format("sending to forwarder pid=~p\n",[Forwarder]), 2267 send2_do2(SendBlobF, Forwarder), 2268 Forwarder ! terminate, 2269 {'DOWN', _, process, Forwarder, 4} = receive_any(), 2270 ok. 2271 2272send2_do2(SendBlobF, To) -> 2273 MsgEnv = alloc_msgenv(), 2274 repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), 2275 {ok,1,Blob0} = SendBlobF(MsgEnv, To), 2276 Blob1 = receive_any(), 2277 Blob1 = Blob0, 2278 2279 clear_msgenv(MsgEnv), 2280 repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), 2281 {ok,1,Blob2} = SendBlobF(MsgEnv, To), 2282 Blob3 = receive_any(), 2283 Blob3 = Blob2, 2284 2285 clear_msgenv(MsgEnv), 2286 repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), 2287 2288 clear_msgenv(MsgEnv), 2289 repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), 2290 {ok,1,Blob4} = SendBlobF(MsgEnv, To), 2291 Blob5 = receive_any(), 2292 Blob5 = Blob4, 2293 2294 clear_msgenv(MsgEnv), 2295 clear_msgenv(MsgEnv), 2296 repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []), 2297 {ok,1,Blob6} = SendBlobF(MsgEnv, To), 2298 Blob7 = receive_any(), 2299 Blob7 = Blob6, 2300 2301 ok. 2302 2303 2304send_blob_thread_and_join(MsgEnv, To) -> 2305 {ok,Blob} = send_blob_thread_dbg(MsgEnv, To, no_join), 2306 {ok,SendRes} = join_send_thread(MsgEnv), 2307 {ok,SendRes,Blob}. 2308 2309send_blob_dbg(MsgEnv, To) -> 2310 Ret = send_blob(MsgEnv, To), 2311 %%io:format("send_blob to ~p returned ~p\n",[To,Ret]), 2312 Ret. 2313 2314send_blob_thread_dbg(MsgEnv, To, Join) -> 2315 Ret = send_blob_thread(MsgEnv, To, Join), 2316 %%io:format("send_blob_thread to ~p Join=~p returned ~p\n",[To,Join,Ret]), 2317 Ret. 2318 2319 2320forwarder(To) -> 2321 forwarder(To, 0). 2322forwarder(To, N) -> 2323 case receive_any() of 2324 terminate -> 2325 gc_and_exit(N); 2326 Msg -> 2327 To ! Msg, 2328 forwarder(To, N+1) 2329 end. 2330 2331other_term() -> 2332 {fun(X,Y) -> X*Y end, make_ref()}. 2333 2334%% Message sending stress test 2335send3(Config) when is_list(Config) -> 2336 %% Let a number of processes send random message blobs between each other 2337 %% using enif_send. Kill and spawn new ones randomly to keep a ~constant 2338 %% number of workers running. 2339 rand:seed(exsplus), 2340 io:format("seed: ~p\n",[rand:export_seed()]), 2341 ets:new(nif_SUITE,[named_table,public]), 2342 true = ets:insert(nif_SUITE,{send3,0,0,0,0}), 2343 timer:send_after(10000, timeout), % Run for 10 seconds 2344 SpawnCnt = send3_controller(0, [], [], 20), 2345 [{_,Rcv,SndOk,SndFail,Balance}] = ets:lookup(nif_SUITE,send3), 2346 io:format("spawns=~p received=~p, sent=~p send-failure=~p balance=~p\n", 2347 [SpawnCnt,Rcv,SndOk,SndFail,Balance]), 2348 ets:delete(nif_SUITE). 2349 2350send3_controller(SpawnCnt, [], _, infinity) -> 2351 SpawnCnt; 2352send3_controller(SpawnCnt0, Mons0, Pids0, Tick) -> 2353 receive 2354 timeout -> 2355 io:format("Timeout. Sending 'halt' to ~p\n",[Pids0]), 2356 lists:foreach(fun(P) -> P ! {halt,self()} end, Pids0), 2357 lists:foreach(fun(P) -> receive {halted,P} -> ok end end, Pids0), 2358 QTot = lists:foldl(fun(P,QSum) -> 2359 {message_queue_len,QLen} = 2360 erlang:process_info(P,message_queue_len), 2361 QSum + QLen 2362 end, 0, Pids0), 2363 io:format("Total queue length ~p\n",[QTot]), 2364 lists:foreach(fun(P) -> P ! die end, Pids0), 2365 send3_controller(SpawnCnt0, Mons0, [], infinity); 2366 {'DOWN', MonRef, process, _Pid, _} -> 2367 Mons1 = lists:delete(MonRef, Mons0), 2368 %%io:format("Got DOWN from ~p. Monitors left: ~p\n",[Pid,Mons1]), 2369 send3_controller(SpawnCnt0, Mons1, Pids0, Tick) 2370 after Tick -> 2371 Max = 20, 2372 N = length(Pids0), 2373 PidN = rand:uniform(Max), 2374 %%io:format("N=~p PidN=~p Pids0=~p\n", [N,PidN,Pids0]), 2375 case PidN > N of 2376 true -> 2377 {NewPid,Mon} = spawn_opt(fun send3_proc/0, [link,monitor]), 2378 lists:foreach(fun(P) -> P ! {is_born,NewPid} end, Pids0), 2379 Balance = ets:lookup_element(nif_SUITE,send3,5), 2380 Inject = (Balance =< 0), 2381 case Inject of 2382 true -> ok; 2383 false -> ets:update_element(nif_SUITE,send3,{5,-1}) 2384 end, 2385 NewPid ! {pids,Pids0,Inject}, 2386 send3_controller(SpawnCnt0+1, [Mon|Mons0], [NewPid|Pids0], Tick); 2387 false -> 2388 KillPid = lists:nth(PidN,Pids0), 2389 KillPid ! die, 2390 Pids1 = lists:delete(KillPid, Pids0), 2391 lists:foreach(fun(P) -> P ! {is_dead,KillPid} end, Pids1), 2392 send3_controller(SpawnCnt0, Mons0, Pids1, Tick) 2393 end 2394 end. 2395 2396send3_proc() -> 2397 %%io:format("Process ~p spawned\n",[self()]), 2398 send3_proc([self()], {0,0,0}, {1,2,3,4,5}). 2399send3_proc(Pids0, Counters={Rcv,SndOk,SndFail}, State0) -> 2400 %%io:format("~p: Pids0=~p", [self(), Pids0]), 2401 %%timer:sleep(10), 2402 receive 2403 {pids, Pids1, Inject} -> 2404 %%io:format("~p: got ~p Inject=~p\n", [self(), Pids1, Inject]), 2405 Pids0 = [self()], 2406 Pids2 = [self() | Pids1], 2407 case Inject of 2408 true -> send3_proc_send(Pids2, Counters, State0); 2409 false -> send3_proc(Pids2, Counters, State0) 2410 end; 2411 {is_born, Pid} -> 2412 %%io:format("~p: is_born ~p, got ~p\n", [self(), Pid, Pids0]), 2413 send3_proc([Pid | Pids0], Counters, State0); 2414 {is_dead, Pid} -> 2415 Pids1 = lists:delete(Pid,Pids0), 2416 %%io:format("~p: is_dead ~p, got ~p\n", [self(), Pid, Pids1]), 2417 send3_proc(Pids1, Counters, State0); 2418 {blob, Blob0} -> 2419 %%io:format("~p: blob ~p\n", [self(), Blob0]), 2420 State1 = send3_new_state(State0, Blob0), 2421 send3_proc_send(Pids0, {Rcv+1,SndOk,SndFail}, State1); 2422 die -> 2423 %%io:format("Process ~p terminating, stats = ~p\n",[self(),Counters]), 2424 {message_queue_len,Dropped} = erlang:process_info(self(),message_queue_len), 2425 _R = ets:update_counter(nif_SUITE,send3, 2426 [{2,Rcv},{3,SndOk},{4,SndFail},{5,1-Dropped}]), 2427 %%io:format("~p: dies R=~p\n", [self(), R]), 2428 ok; 2429 {halt,Papa} -> 2430 Papa ! {halted,self()}, 2431 io:format("~p halted\n",[self()]), 2432 receive die -> ok end, 2433 io:format("~p dying\n",[self()]) 2434 end. 2435 2436send3_proc_send(Pids, {Rcv,SndOk,SndFail}, State0) -> 2437 To = lists:nth(rand:uniform(length(Pids)),Pids), 2438 Blob = send3_make_blob(), 2439 State1 = send3_new_state(State0,Blob), 2440 case send3_send(To, Blob) of 2441 true -> 2442 send3_proc(Pids, {Rcv,SndOk+1,SndFail}, State1); 2443 false -> 2444 send3_proc(Pids, {Rcv,SndOk,SndFail+1}, State1) 2445 end. 2446 2447 2448send3_make_blob() -> 2449 case rand:uniform(20)-1 of 2450 0 -> {term,[]}; 2451 N -> 2452 MsgEnv = alloc_msgenv(), 2453 repeat(N bsr 1, 2454 fun(_) -> grow_blob(MsgEnv,other_term(),rand:uniform(1 bsl 20)) 2455 end, void), 2456 case (N band 3) of 2457 0 -> {term,copy_blob(MsgEnv)}; 2458 1 -> {copy,copy_blob(MsgEnv)}; 2459 _ -> {msgenv,MsgEnv} 2460 end 2461 end. 2462 2463send3_send(Pid, Msg) -> 2464 %% 90% enif_send and 10% normal bang 2465 case rand:uniform(10) of 2466 1 -> send3_send_bang(Pid,Msg); 2467 _ -> send3_send_nif(Pid,Msg) 2468 end. 2469send3_send_nif(Pid, {term,Blob}) -> 2470 %%io:format("~p send term nif\n",[self()]), 2471 send_term(Pid, {blob, Blob}) =:= 1; 2472send3_send_nif(Pid, {copy,Blob}) -> 2473 %%io:format("~p send term nif\n",[self()]), 2474 send_copy_term(Pid, {blob, Blob}) =:= 1; 2475send3_send_nif(Pid, {msgenv,MsgEnv}) -> 2476 %%io:format("~p send blob nif\n",[self()]), 2477 send3_blob(MsgEnv, Pid, blob) =:= 1. 2478 2479send3_send_bang(Pid, {term,Blob}) -> 2480 %%io:format("~p send term bang\n",[self()]), 2481 Pid ! {blob, Blob}, 2482 true; 2483send3_send_bang(Pid, {copy,Blob}) -> 2484 %%io:format("~p send term bang\n",[self()]), 2485 Pid ! {blob, Blob}, 2486 true; 2487send3_send_bang(Pid, {msgenv,MsgEnv}) -> 2488 %%io:format("~p send blob bang\n",[self()]), 2489 Pid ! {blob, copy_blob(MsgEnv)}, 2490 true. 2491 2492send3_new_state(State, Blob) -> 2493 case rand:uniform(5+2) of 2494 N when N =< 5-> setelement(N, State, Blob); 2495 _ -> State % Don't store blob 2496 end. 2497 2498%% Negative testing of load_nif 2499neg(Config) when is_list(Config) -> 2500 TmpMem = tmpmem(), 2501 {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)), 2502 2503 Data = proplists:get_value(data_dir, Config), 2504 File = filename:join(Data, "nif_mod"), 2505 {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]), 2506 {module,nif_mod} = erlang:load_module(nif_mod,Bin), 2507 2508 {error,{load_failed,_}} = nif_mod:load_nif_lib(Config, 0), 2509 {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init), 2510 verify_tmpmem(TmpMem), 2511 ok. 2512 2513%% Test all enif_is functions 2514is_checks(Config) when is_list(Config) -> 2515 ensure_lib_loaded(Config, 1), 2516 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2517 self(), hd(erlang:ports()), [], [1,9,9,8], 2518 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 12), 2519 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2520 self(), hd(erlang:ports()), [], [1,9,9,8], 2521 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -12), 2522 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2523 self(), hd(erlang:ports()), [], [1,9,9,8], 2524 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551617), 2525 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2526 self(), hd(erlang:ports()), [], [1,9,9,8], 2527 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551617), 2528 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2529 self(), hd(erlang:ports()), [], [1,9,9,8], 2530 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 99.146), 2531 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2532 self(), hd(erlang:ports()), [], [1,9,9,8], 2533 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -99.146), 2534 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2535 self(), hd(erlang:ports()), [], [1,9,9,8], 2536 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551616.2e2), 2537 ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end, 2538 self(), hd(erlang:ports()), [], [1,9,9,8], 2539 {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2), 2540 try 2541 check_is_exception(), 2542 throw(expected_badarg) 2543 catch 2544 error:badarg -> 2545 ok 2546 end. 2547 2548%% Test all enif_get_length functions 2549get_length(Config) when is_list(Config) -> 2550 ensure_lib_loaded(Config, 1), 2551 ok = length_test(hejsan, "hejsan", [], [], not_a_list, [1,2|3]). 2552 2553ensure_lib_loaded(Config) -> 2554 ensure_lib_loaded(Config, 1). 2555ensure_lib_loaded(Config, Ver) -> 2556 Path = ?config(data_dir, Config), 2557 case lib_version() of 2558 undefined -> 2559 Lib = "nif_SUITE." ++ integer_to_list(Ver), 2560 ok = erlang:load_nif(filename:join(Path,Lib), []); 2561 Ver when is_integer(Ver) -> 2562 ok 2563 end, 2564 erl_ddll:try_load(Path, echo_drv, []), 2565 ok. 2566 2567make_atom(Config) when is_list(Config) -> 2568 ensure_lib_loaded(Config, 1), 2569 An0Atom = an0atom, 2570 An0Atom0 = 'an\000atom\000', 2571 Atoms = make_atoms(), 2572 7 = size(Atoms), 2573 Atoms = {An0Atom,An0Atom,An0Atom,An0Atom0,An0Atom,An0Atom,An0Atom0}. 2574 2575make_string(Config) when is_list(Config) -> 2576 ensure_lib_loaded(Config, 1), 2577 Strings = make_strings(), 2578 5 = size(Strings), 2579 A0String = "a0string", 2580 A0String0 = [$a,0,$s,$t,$r,$i,$n,$g,0], 2581 AStringWithAccents = [$E,$r,$l,$a,$n,$g,$ ,16#e4,$r,$ ,$e,$t,$t,$ ,$g,$e,$n,$e,$r,$e,$l,$l,$t,$ ,$p,$r,$o,$g,$r,$a,$m,$s,$p,$r,16#e5,$k], 2582 Strings = {A0String,A0String,A0String,A0String0, AStringWithAccents}. 2583 2584reverse_list_test(Config) -> 2585 ensure_lib_loaded(Config, 1), 2586 List = lists:seq(1,100), 2587 RevList = lists:reverse(List), 2588 RevList = reverse_list(List), 2589 badarg = reverse_list(foo). 2590 2591%% Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment 2592otp_9668(Config) -> 2593 ensure_lib_loaded(Config, 1), 2594 TmpMem = tmpmem(), 2595 IOList = ["This",' ',<<"is">>,' ',[<<"an iolist">>,'.']], 2596 otp_9668_nif(IOList), 2597 2598 <<_:5/bitstring,UnalignedBin:10/binary,_/bitstring>> = <<"Abuse me as unaligned">>, 2599 otp_9668_nif(UnalignedBin), 2600 2601 verify_tmpmem(TmpMem), 2602 ok. 2603 2604%% Copy of writable binary 2605otp_9828(Config) -> 2606 ensure_lib_loaded(Config, 1), 2607 otp_9828_loop(<<"I'm alive!">>, 1000). 2608 2609otp_9828_loop(_Bin, 0) -> 2610 ok; 2611otp_9828_loop(Bin, Val) -> 2612 WrtBin = <<Bin/binary, Val:32>>, 2613 ok = otp_9828_nif(WrtBin), 2614 otp_9828_loop(WrtBin, Val-1). 2615 2616 2617consume_timeslice(Config) when is_list(Config) -> 2618 case {erlang:system_info(debug_compiled), 2619 erlang:system_info(lock_checking)} of 2620 {false, false} -> 2621 consume_timeslice_test(Config); 2622 {false, true} -> 2623 {skipped, "Lock checking enabled"}; 2624 _ -> 2625 {skipped, "Debug compiled"} 2626 end. 2627 2628consume_timeslice_test(Config) when is_list(Config) -> 2629 ensure_lib_loaded(Config), 2630 CONTEXT_REDS = 4000, 2631 Me = self(), 2632 Go = make_ref(), 2633 RedDiff = make_ref(), 2634 Done = make_ref(), 2635 DummyMFA = {?MODULE,dummy_call,1}, 2636 P = spawn(fun () -> 2637 receive Go -> ok end, 2638 {reductions, R1} = process_info(self(), reductions), 2639 1 = consume_timeslice_nif(100, false), 2640 dummy_call(111), 2641 0 = consume_timeslice_nif(90, false), 2642 dummy_call(222), 2643 1 = consume_timeslice_nif(10, false), 2644 dummy_call(333), 2645 0 = consume_timeslice_nif(25, false), 2646 0 = consume_timeslice_nif(25, false), 2647 0 = consume_timeslice_nif(25, false), 2648 1 = consume_timeslice_nif(25, false), 2649 0 = consume_timeslice_nif(25, false), 2650 2651 ok = case consume_timeslice_nif(1, true) of 2652 Cnt when Cnt > 70, Cnt < 80 -> ok; 2653 Other -> Other 2654 end, 2655 dummy_call(444), 2656 2657 {reductions, R2} = process_info(self(), reductions), 2658 Me ! {RedDiff, R2 - R1}, 2659 exit(Done) 2660 end), 2661 erlang:yield(), 2662 2663 erlang:trace_pattern(DummyMFA, [{'_', [], [{return_trace}]}], [local]), 2664 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]), 2665 2666 P ! Go, 2667 2668 %% receive Go -> ok end, 2669 {trace, P, in, _} = next_tmsg(P), 2670 2671 %% consume_timeslice_nif(100), 2672 %% dummy_call(111) 2673 %% 2674 %% Note that we may be scheduled out immediately before or immediately 2675 %% "after" dummy_call(111) depending on when the emulator tests reductions. 2676 %% 2677 %% In either case, we should be rescheduled before the function returns. 2678 Dummy_111 = {?MODULE,dummy_call,[111]}, 2679 case next_tmsg(P) of 2680 {trace, P, out, _} -> 2681 %% See dummy_call(111) above 2682 {trace, P, in, _} = next_tmsg(P), 2683 {trace, P, call, Dummy_111} = next_tmsg(P); 2684 {trace, P, call, Dummy_111} -> 2685 {trace, P, out, _} = next_tmsg(P), 2686 {trace, P, in, _} = next_tmsg(P) 2687 end, 2688 {trace, P, return_from, DummyMFA, ok} = next_tmsg(P), 2689 2690 %% consume_timeslice_nif(90), 2691 %% dummy_call(222) 2692 Dummy_222 = {?MODULE,dummy_call,[222]}, 2693 {trace, P, call, Dummy_222} = next_tmsg(P), 2694 {trace, P, return_from, DummyMFA, ok} = next_tmsg(P), 2695 2696 %% consume_timeslice_nif(10), 2697 %% dummy_call(333) 2698 Dummy_333 = {?MODULE,dummy_call,[333]}, 2699 case next_tmsg(P) of 2700 {trace, P, out, _} -> 2701 %% See dummy_call(111) above 2702 {trace, P, in, _} = next_tmsg(P), 2703 {trace, P, call, Dummy_333} = next_tmsg(P); 2704 {trace, P, call, Dummy_333} -> 2705 {trace, P, out, _} = next_tmsg(P), 2706 {trace, P, in, _} = next_tmsg(P) 2707 end, 2708 {trace, P, return_from, DummyMFA, ok} = next_tmsg(P), 2709 2710 %% 25,25,25,25, 25 2711 {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), 2712 {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), 2713 2714 %% consume_timeslice(1,true) 2715 %% dummy_call(444) 2716 Dummy_444 = {?MODULE,dummy_call,[444]}, 2717 case next_tmsg(P) of 2718 {trace, P, out, DummyMFA} -> 2719 %% See dummy_call(111) above 2720 {trace, P, in, DummyMFA} = next_tmsg(P), 2721 {trace, P, call, Dummy_444} = next_tmsg(P); 2722 {trace, P, call, Dummy_444} -> 2723 {trace, P, out, DummyMFA} = next_tmsg(P), 2724 {trace, P, in, DummyMFA} = next_tmsg(P) 2725 end, 2726 {trace, P, return_from, DummyMFA, ok} = next_tmsg(P), 2727 2728 %% exit(Done) 2729 {trace, P, exit, Done} = next_tmsg(P), 2730 2731 ExpReds = (100 + 90 + 10 + 25*5 + 75) * CONTEXT_REDS div 100, 2732 receive 2733 {RedDiff, Reductions} when Reductions < (ExpReds + 10), Reductions > (ExpReds - 10) -> 2734 io:format("Reductions = ~p~n", [Reductions]), 2735 ok; 2736 {RedDiff, Reductions} -> 2737 ct:fail({unexpected_reduction_count, Reductions, ExpReds}) 2738 end, 2739 2740 none = next_msg(P), 2741 2742 ok. 2743 2744nif_schedule(Config) when is_list(Config) -> 2745 ensure_lib_loaded(Config), 2746 A = "this is a string", 2747 B = {this,is,a,tuple}, 2748 {B,A} = call_nif_schedule(A, B), 2749 ok = try call_nif_schedule(1, 2) 2750 catch 2751 error:badarg:Stk -> 2752 [{?MODULE,call_nif_schedule,[1,2],_}|_] = Stk, 2753 ok 2754 end, 2755 ok. 2756 2757nif_exception(Config) when is_list(Config) -> 2758 ensure_lib_loaded(Config), 2759 try 2760 %% this checks that the expected exception occurs when the NIF 2761 %% calls enif_make_badarg at some point but then tries to return a 2762 %% value that isn't an exception 2763 call_nif_exception(0), 2764 ct:fail(expected_badarg) 2765 catch 2766 error:badarg -> 2767 ok 2768 end, 2769 %% this checks that a NIF can raise various terms as exceptions 2770 ok = nif_raise_exceptions(call_nif_exception), 2771 ok. 2772 2773nif_nan_and_inf(Config) when is_list(Config) -> 2774 ensure_lib_loaded(Config), 2775 try 2776 call_nif_nan_or_inf(nan), 2777 ct:fail(expected_badarg) 2778 catch 2779 error:badarg -> 2780 ok 2781 end, 2782 try 2783 call_nif_nan_or_inf(inf), 2784 ct:fail(expected_badarg) 2785 catch 2786 error:badarg -> 2787 ok 2788 end, 2789 try 2790 call_nif_nan_or_inf(tuple), 2791 ct:fail(expected_badarg) 2792 catch 2793 error:badarg -> 2794 ok 2795 end. 2796 2797nif_atom_too_long(Config) when is_list(Config) -> 2798 ensure_lib_loaded(Config), 2799 try 2800 call_nif_atom_too_long(all), 2801 ct:fail(expected_badarg) 2802 catch 2803 error:badarg -> 2804 ok 2805 end, 2806 try 2807 call_nif_atom_too_long(len), 2808 ct:fail(expected_badarg) 2809 catch 2810 error:badarg -> 2811 ok 2812 end. 2813 2814next_msg(_Pid) -> 2815 receive 2816 M -> M 2817 after 100 -> 2818 none 2819 end. 2820 2821next_tmsg(Pid) -> 2822 receive TMsg when is_tuple(TMsg), 2823 element(1, TMsg) == trace, 2824 element(2, TMsg) == Pid -> 2825 TMsg 2826 after 100 -> 2827 none 2828 end. 2829 2830dummy_call(_) -> 2831 ok. 2832 2833tmpmem() -> 2834 erts_debug:alloc_blocks_size(temp_alloc). 2835 2836verify_tmpmem(MemInfo) -> 2837 %%wait_for_test_procs(), 2838 case tmpmem() of 2839 MemInfo -> 2840 io:format("Tmp mem info: ~p", [MemInfo]), 2841 case MemInfo of 2842 {notsup,undefined} -> 2843 %% Use 'erl +Mea max' to do more complete memory leak testing. 2844 {comment,"Incomplete or no mem leak testing"}; 2845 _ -> 2846 ok 2847 end; 2848 Other -> 2849 ct:fail("Expected: ~p\nActual: ~p", [MemInfo, Other]) 2850 end. 2851 2852call(Pid,Cmd) -> 2853 %%io:format("~p calling ~p with ~p\n",[self(), Pid, Cmd]), 2854 Pid ! {self(), Cmd}, 2855 receive 2856 {Pid,Reply} -> Reply 2857 end. 2858 2859receive_any() -> 2860 receive M -> M end. 2861 2862receive_any(Timeout) -> 2863 receive M -> M 2864 after Timeout -> timeout end. 2865 2866flush() -> 2867 flush(1). 2868 2869flush(0) -> 2870 flush(0, 10); % don't waste too much time waiting for nothing 2871flush(N) -> 2872 flush(N, 1000). 2873 2874flush(N, Timeout) -> 2875 receive M -> 2876 [M | flush(N-1)] 2877 after Timeout -> 2878 [] 2879 end. 2880 2881repeat(0, _, Arg) -> 2882 Arg; 2883repeat(N, Fun, Arg0) -> 2884 repeat(N-1, Fun, Fun(Arg0)). 2885 2886repeat_while(Fun, Acc0) -> 2887 case Fun(Acc0) of 2888 false -> ok; 2889 Acc1 -> 2890 repeat_while(Fun, Acc1) 2891 end. 2892 2893check(Exp,Got,Line) -> 2894 case Got of 2895 Exp -> Exp; 2896 _ -> 2897 io:format("CHECK at line ~p\nExpected: ~p\nGot : ~p\n", 2898 [Line,Exp,Got]), 2899 Got 2900 end. 2901 2902nif_raise_exceptions(NifFunc) -> 2903 ExcTerms = [{error, test}, "a string", <<"a binary">>, 2904 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], 2905 lists:foldl(fun(Term, ok) -> 2906 try 2907 erlang:apply(?MODULE,NifFunc,[Term]), 2908 ct:fail({expected,Term}) 2909 catch 2910 error:Term:Stk -> 2911 [{?MODULE,NifFunc,[Term],_}|_] = Stk, 2912 ok 2913 end 2914 end, ok, ExcTerms). 2915 2916-define(ERL_NIF_TIME_ERROR, -9223372036854775808). 2917-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]). 2918 2919nif_monotonic_time(_Config) -> 2920 ?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit), 2921 mtime_loop(1000000). 2922 2923mtime_loop(0) -> 2924 ok; 2925mtime_loop(N) -> 2926 chk_mtime(?TIME_UNITS), 2927 mtime_loop(N-1). 2928 2929chk_mtime([]) -> 2930 ok; 2931chk_mtime([TU|TUs]) -> 2932 A = erlang:monotonic_time(TU), 2933 B = monotonic_time(TU), 2934 C = erlang:monotonic_time(TU), 2935 try 2936 true = A =< B, 2937 true = B =< C 2938 catch 2939 _ : _ -> 2940 ct:fail({monotonic_time_missmatch, TU, A, B, C}) 2941 end, 2942 chk_mtime(TUs). 2943 2944nif_time_offset(_Config) -> 2945 ?ERL_NIF_TIME_ERROR = time_offset(invalid_time_unit), 2946 toffs_loop(1000000). 2947 2948toffs_loop(0) -> 2949 ok; 2950toffs_loop(N) -> 2951 chk_toffs(?TIME_UNITS), 2952 toffs_loop(N-1). 2953 2954chk_toffs([]) -> 2955 ok; 2956chk_toffs([TU|TUs]) -> 2957 TO = erlang:time_offset(TU), 2958 NifTO = time_offset(TU), 2959 case TO =:= NifTO of 2960 true -> 2961 ok; 2962 false -> 2963 case erlang:system_info(time_warp_mode) of 2964 no_time_warp -> 2965 ct:fail({time_offset_mismatch, TU, TO, NifTO}); 2966 _ -> 2967 %% Most frequent time offset change 2968 %% is currently only every 15:th 2969 %% second so this should currently 2970 %% work... 2971 NTO = erlang:time_offset(TU), 2972 case NifTO =:= NTO of 2973 true -> 2974 ok; 2975 false -> 2976 ct:fail({time_offset_mismatch, TU, TO, NifTO, NTO}) 2977 end 2978 end 2979 end, 2980 chk_toffs(TUs). 2981 2982nif_convert_time_unit(_Config) -> 2983 ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit), 2984 ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second), 2985 ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit), 2986 lists:foreach(fun (Offset) -> 2987 lists:foreach(fun (Diff) -> 2988 chk_ctu(Diff+(Offset*1000*1000*1000)) 2989 end, 2990 [999999999999, 2991 99999999999, 2992 9999999999, 2993 999999999, 2994 99999999, 2995 9999999, 2996 999999, 2997 99999, 2998 999, 2999 99, 3000 9, 3001 1, 3002 11, 3003 101, 3004 1001, 3005 10001, 3006 100001, 3007 1000001, 3008 10000001, 3009 100000001, 3010 1000000001, 3011 100000000001, 3012 1000000000001, 3013 5, 3014 50, 3015 500, 3016 5000, 3017 50000, 3018 500000, 3019 5000000, 3020 50000000, 3021 500000000, 3022 5000000000, 3023 50000000000, 3024 500000000000]) 3025 end, 3026 [-4711, -1000, -475, -5, -4, -3, -2, -1, 0, 3027 1, 2, 3, 4, 5, 475, 1000, 4711]), 3028 ctu_loop(1000000). 3029 3030ctu_loop(0) -> 3031 ok; 3032ctu_loop(N) -> 3033 chk_ctu(erlang:monotonic_time(nanosecond)), 3034 ctu_loop(N-1). 3035 3036chk_ctu(Time) -> 3037 chk_ctu(Time, ?TIME_UNITS). 3038 3039chk_ctu(_Time, []) -> 3040 ok; 3041chk_ctu(Time, [FromTU|FromTUs]) -> 3042 chk_ctu(Time, FromTU, ?TIME_UNITS), 3043 chk_ctu(Time, FromTUs). 3044 3045chk_ctu(_Time, _FromTU, []) -> 3046 ok; 3047chk_ctu(Time, FromTU, [ToTU|ToTUs]) -> 3048 T = erlang:convert_time_unit(Time, nanosecond, FromTU), 3049 TE = erlang:convert_time_unit(T, FromTU, ToTU), 3050 TN = convert_time_unit(T, FromTU, ToTU), 3051 case TE =:= TN of 3052 false -> 3053 ct:fail({conversion_mismatch, FromTU, T, ToTU, TE, TN}); 3054 true -> 3055 chk_ctu(Time, FromTU, ToTUs) 3056 end. 3057 3058nif_now_time(Config) -> 3059 ensure_lib_loaded(Config), 3060 3061 N1 = now(), 3062 NifN1 = now_time(), 3063 NifN2 = now_time(), 3064 N2 = now(), 3065 true = N1 < NifN1, 3066 true = NifN1 < NifN2, 3067 true = NifN2 < N2. 3068 3069nif_cpu_time(Config) -> 3070 ensure_lib_loaded(Config), 3071 3072 try cpu_time() of 3073 {_, _, _} -> 3074 ok 3075 catch error:badarg -> 3076 {comment, "cpu_time not supported"} 3077 end. 3078 3079nif_unique_integer(Config) -> 3080 ensure_lib_loaded(Config), 3081 3082 UM1 = erlang:unique_integer([monotonic]), 3083 UM2 = unique_integer_nif([monotonic]), 3084 UM3 = erlang:unique_integer([monotonic]), 3085 3086 true = UM1 < UM2, 3087 true = UM2 < UM3, 3088 3089 UMP1 = erlang:unique_integer([monotonic, positive]), 3090 UMP2 = unique_integer_nif([monotonic, positive]), 3091 UMP3 = erlang:unique_integer([monotonic, positive]), 3092 3093 true = 0 =< UMP1, 3094 true = UMP1 < UMP2, 3095 true = UMP2 < UMP3, 3096 3097 UP1 = erlang:unique_integer([positive]), 3098 UP2 = unique_integer_nif([positive]), 3099 UP3 = erlang:unique_integer([positive]), 3100 3101 true = 0 =< UP1, 3102 true = 0 =< UP2, 3103 true = 0 =< UP3, 3104 3105 true = is_integer(unique_integer_nif([])), 3106 true = is_integer(unique_integer_nif([])), 3107 true = is_integer(unique_integer_nif([])). 3108 3109nif_is_process_alive(Config) -> 3110 ensure_lib_loaded(Config), 3111 3112 {Pid,_} = spawn_monitor(fun() -> receive ok -> nok end end), 3113 true = is_process_alive_nif(Pid), 3114 exit(Pid, die), 3115 receive _ -> ok end, %% Clear monitor 3116 false = is_process_alive_nif(Pid). 3117 3118nif_is_port_alive(Config) -> 3119 ensure_lib_loaded(Config), 3120 3121 Port = open_port({spawn,echo_drv},[eof]), 3122 true = is_port_alive_nif(Port), 3123 port_close(Port), 3124 false = is_port_alive_nif(Port). 3125 3126nif_term_to_binary(Config) -> 3127 ensure_lib_loaded(Config), 3128 T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)}, 3129 Bin = term_to_binary(T), 3130 ct:log("~p",[Bin]), 3131 Bin = term_to_binary_nif(T, undefined), 3132 true = term_to_binary_nif(T, self()), 3133 receive Bin -> ok end. 3134 3135-define(ERL_NIF_BIN2TERM_SAFE, 16#20000000). 3136 3137nif_binary_to_term(Config) -> 3138 ensure_lib_loaded(Config), 3139 BigMap = maps:from_list([{I,-I} || I <- lists:seq(1,100)]), 3140 [nif_binary_to_term_do(T) 3141 || T <- [{#{ok => nok}, <<0:8096>>, lists:seq(1,100)}, 3142 atom, 42, self(), BigMap]], 3143 ok. 3144 3145nif_binary_to_term_do(T) -> 3146 Dummy = [true|false], 3147 Bin = term_to_binary(T), 3148 Len = byte_size(Bin), 3149 {Len,T,Dummy} = binary_to_term_nif(Bin, undefined, 0), 3150 Len = binary_to_term_nif(Bin, self(), 0), 3151 {T,Dummy} = receive M -> M after 1000 -> timeout end, 3152 3153 {Len,T,Dummy} = binary_to_term_nif(Bin, undefined, ?ERL_NIF_BIN2TERM_SAFE), 3154 false = binary_to_term_nif(<<131,100,0,14,"undefined_atom">>, 3155 undefined, ?ERL_NIF_BIN2TERM_SAFE), 3156 false = binary_to_term_nif(Bin, undefined, 1), 3157 ok. 3158 3159nif_port_command(Config) -> 3160 ensure_lib_loaded(Config), 3161 3162 Port = open_port({spawn,echo_drv},[eof]), 3163 true = port_command_nif(Port, "hello\n"), 3164 receive {Port,{data,"hello\n"}} -> ok 3165 after 1000 -> ct:fail(timeout) end, 3166 3167 RefcBin = lists:flatten([lists:duplicate(100, "hello"),"\n"]), 3168 true = port_command_nif(Port, iolist_to_binary(RefcBin)), 3169 receive {Port,{data,RefcBin}} -> ok 3170 after 1000 -> ct:fail(timeout) end, 3171 3172 %% Test that invalid arguments correctly returns 3173 %% badarg and that the port survives. 3174 {'EXIT', {badarg, _}} = (catch port_command_nif(Port, [ok])), 3175 3176 IoList = [lists:duplicate(100,<<"hello">>),"\n"], 3177 true = port_command_nif(Port, [IoList]), 3178 FlatIoList = binary_to_list(iolist_to_binary(IoList)), 3179 receive {Port,{data,FlatIoList}} -> ok 3180 after 1000 -> ct:fail(timeout) end, 3181 3182 port_close(Port), 3183 3184 {'EXIT', {badarg, _}} = (catch port_command_nif(Port, "hello\n")), 3185 ok. 3186 3187nif_snprintf(Config) -> 3188 ensure_lib_loaded(Config), 3189 <<"ok",0>> = format_term_nif(3,ok), 3190 <<"o",0>> = format_term_nif(2,ok), 3191 <<"\"hello world\"",0>> = format_term_nif(14,"hello world"), 3192 <<"{{hello,world,-33},3.14",_/binary>> = format_term_nif(50,{{hello,world, -33}, 3.14, self()}), 3193 <<"{{hello,world,-33},",0>> = format_term_nif(20,{{hello,world, -33}, 3.14, self()}), 3194 ok. 3195 3196nif_internal_hash(Config) -> 3197 ensure_lib_loaded(Config), 3198 HashValueBitSize = nif_hash_result_bitsize(internal), 3199 Terms = unique([random_term() || _ <- lists:seq(1, 500)]), 3200 HashValues = [hash_nif(internal, Term, 0) || Term <- Terms], 3201 test_bit_distribution_fitness(HashValues, HashValueBitSize). 3202 3203nif_internal_hash_salted(Config) -> 3204 ensure_lib_loaded(Config), 3205 test_salted_nif_hash(internal). 3206 3207nif_phash2(Config) -> 3208 ensure_lib_loaded(Config), 3209 HashValueBitSize = nif_hash_result_bitsize(phash2), 3210 Terms = unique([random_term() || _ <- lists:seq(1, 500)]), 3211 HashValues = 3212 lists:map( 3213 fun (Term) -> 3214 HashValue = erlang:phash2(Term), 3215 Salt = random_uint32(), % phash2 should ignore salt 3216 NifHashValue = hash_nif(phash2, Term, Salt), 3217 (HashValue =:= NifHashValue 3218 orelse ct:fail("Expected: ~p\nActual: ~p", 3219 [HashValue, NifHashValue])), 3220 HashValue 3221 end, 3222 Terms), 3223 test_bit_distribution_fitness(HashValues, HashValueBitSize). 3224 3225test_salted_nif_hash(HashType) -> 3226 HashValueBitSize = nif_hash_result_bitsize(HashType), 3227 Terms = unique([random_term() || _ <- lists:seq(1, 500)]), 3228 Salts = unique([random_uint32() || _ <- lists:seq(1, 50)]), 3229 {HashValuesPerSalt, HashValuesPerTerm} = 3230 lists:mapfoldl( 3231 fun (Salt, Acc) -> 3232 {HashValues, NewAcc} = 3233 lists:mapfoldl( 3234 fun (Term, AccB) -> 3235 HashValue = hash_nif(HashType, Term, Salt), 3236 NewAccB = dict:append(Term, HashValue, AccB), 3237 {HashValue, NewAccB} 3238 end, 3239 Acc, 3240 Terms), 3241 {{Salt, HashValues}, NewAcc} 3242 end, 3243 dict:new(), 3244 Salts), 3245 3246 % Test per-salt hash distribution of different terms 3247 lists:foreach( 3248 fun ({_Salt, HashValues}) -> 3249 test_bit_distribution_fitness(HashValues, HashValueBitSize) 3250 end, 3251 HashValuesPerSalt), 3252 3253 % Test per-term hash distribution of different salts 3254 dict:fold( 3255 fun (_Term, HashValues, Acc) -> 3256 test_bit_distribution_fitness(HashValues, HashValueBitSize), 3257 Acc 3258 end, 3259 ok, 3260 HashValuesPerTerm). 3261 3262test_bit_distribution_fitness(Integers, BitSize) -> 3263 MaxInteger = (1 bsl BitSize) - 1, 3264 OnesPerBit = 3265 lists:foldl( 3266 fun (Integer, Acc) when Integer >= 0, Integer =< MaxInteger -> 3267 lists:foldl( 3268 fun (BitIndex, AccB) -> 3269 BitValue = (Integer band (1 bsl BitIndex)) bsr BitIndex, 3270 orddict:update_counter(BitIndex, BitValue, AccB) 3271 end, 3272 Acc, 3273 lists:seq(0, BitSize - 1)) 3274 end, 3275 orddict:new(), 3276 Integers), 3277 3278 N = length(Integers), 3279 ExpectedNrOfOnes = N div 2, 3280 %% ExpectedNrOfOnes should have a binomial distribution 3281 %% with a standard deviation as: 3282 ExpectedStdDev = math:sqrt(N) / 2, 3283 %% which can be approximated as a normal distribution 3284 %% where we allow a deviation of 6 std.devs 3285 %% for a fail probability of 0.000000002: 3286 MaxStdDevs = 6, 3287 3288 FailureText = 3289 orddict:fold( 3290 fun (BitIndex, NrOfOnes, Acc) -> 3291 Deviation = abs(NrOfOnes - ExpectedNrOfOnes) / ExpectedStdDev, 3292 case Deviation >= MaxStdDevs of 3293 false -> 3294 Acc; 3295 true -> 3296 [Acc, 3297 io_lib:format( 3298 "Unreasonable deviation on number of set bits (i=~p): " 3299 "expected ~p, got ~p (# std.dev ~.3f > ~p)~n", 3300 [BitIndex, ExpectedNrOfOnes, NrOfOnes, Deviation, MaxStdDevs])] 3301 end 3302 end, 3303 [], 3304 OnesPerBit), 3305 3306 (FailureText =:= [] orelse ct:fail(FailureText)). 3307 3308nif_hash_result_bitsize(internal) -> 32; 3309nif_hash_result_bitsize(phash2) -> 27. 3310 3311unique(List) -> 3312 lists:usort(List). 3313 3314random_uint32() -> 3315 rand:uniform(1 bsl 32) - 1. 3316 3317random_term() -> 3318 case rand:uniform(6) of 3319 1 -> rand:uniform(1 bsl 27) - 1; % small 3320 2 -> (1 bsl 27) + rand:uniform(1 bsl 128); % big 3321 3 -> random_sign() * (rand:uniform() * (1 bsl 53)); % float 3322 4 -> random_binary(); 3323 5 -> random_pid(); 3324 6 -> 3325 Length = rand:uniform(10), 3326 List = [random_term() || _ <- lists:seq(1, Length)], 3327 case rand:uniform(2) of 3328 1 -> 3329 List; 3330 2 -> 3331 list_to_tuple(List) 3332 end 3333 end. 3334 3335random_sign() -> 3336 case rand:uniform(2) of 3337 1 -> -1.0; 3338 2 -> 1.0 3339 end. 3340 3341random_binary() -> 3342 list_to_binary(random_bytes(rand:uniform(32) - 1)). 3343 3344random_bytes(0) -> 3345 []; 3346random_bytes(N) when N > 0 -> 3347 [rand:uniform(256) - 1 | random_bytes(N - 1)]. 3348 3349random_pid() -> 3350 Processes = erlang:processes(), 3351 lists:nth(rand:uniform(length(Processes)), Processes). 3352 3353%% Test enif_whereis_... 3354 3355nif_whereis(Config) when is_list(Config) -> 3356 ensure_lib_loaded(Config), 3357 3358 RegName = nif_whereis_test_thing, 3359 undefined = erlang:whereis(RegName), 3360 false = whereis_term(pid, RegName), 3361 3362 Mgr = self(), 3363 Ref = make_ref(), 3364 ProcMsg = {Ref, ?LINE}, 3365 PortMsg = ?MODULE_STRING " whereis hello\n", 3366 3367 {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]), 3368 true = register(RegName, Pid), 3369 Pid = erlang:whereis(RegName), 3370 Pid = whereis_term(pid, RegName), 3371 false = whereis_term(port, RegName), 3372 false = whereis_term(pid, [RegName]), 3373 3374 ok = whereis_send(pid, RegName, {forward, Mgr, ProcMsg}), 3375 ok = receive ProcMsg -> ok end, 3376 3377 Pid ! {Ref, quit}, 3378 ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end, 3379 undefined = erlang:whereis(RegName), 3380 false = whereis_term(pid, RegName), 3381 3382 Port = open_port({spawn, echo_drv}, [eof]), 3383 true = register(RegName, Port), 3384 Port = erlang:whereis(RegName), 3385 Port = whereis_term(port, RegName), 3386 false = whereis_term(pid, RegName), 3387 false = whereis_term(port, [RegName]), 3388 3389 ok = whereis_send(port, RegName, PortMsg), 3390 ok = receive {Port, {data, PortMsg}} -> ok end, 3391 3392 port_close(Port), 3393 undefined = erlang:whereis(RegName), 3394 false = whereis_term(port, RegName), 3395 ok. 3396 3397nif_whereis_parallel(Config) when is_list(Config) -> 3398 ensure_lib_loaded(Config), 3399 3400 %% try to be at least a little asymetric 3401 NProcs = trunc(3.7 * erlang:system_info(schedulers)), 3402 NSeq = lists:seq(1, NProcs), 3403 Names = [list_to_atom("nif_whereis_proc_" ++ integer_to_list(N)) 3404 || N <- NSeq], 3405 Mgr = self(), 3406 Ref = make_ref(), 3407 3408 NotReg = fun(Name) -> 3409 erlang:whereis(Name) == undefined 3410 end, 3411 PidReg = fun({Name, Pid, _Mon}) -> 3412 erlang:whereis(Name) == Pid andalso whereis_term(pid, Name) == Pid 3413 end, 3414 RecvDown = fun({_Name, Pid, Mon}) -> 3415 receive {'DOWN', Mon, process, Pid, normal} -> true 3416 after 1500 -> false end 3417 end, 3418 RecvNum = fun(N) -> 3419 receive {N, Ref} -> true 3420 after 1500 -> false end 3421 end, 3422 3423 true = lists:all(NotReg, Names), 3424 3425 %% {Name, Pid, Mon} 3426 Procs = lists:map( 3427 fun(N) -> 3428 Name = lists:nth(N, Names), 3429 Prev = lists:nth((if N == 1 -> NProcs; true -> (N - 1) end), Names), 3430 Next = lists:nth((if N == NProcs -> 1; true -> (N + 1) end), Names), 3431 {Pid, Mon} = spawn_monitor( 3432 ?MODULE, nif_whereis_proxy, [{N, Ref, Mgr, [Prev, Next]}]), 3433 true = register(Name, Pid), 3434 {Name, Pid, Mon} 3435 end, NSeq), 3436 3437 true = lists:all(PidReg, Procs), 3438 3439 %% tell them all to 'fire' as fast as we can 3440 repeat(10, fun(_) -> 3441 [P ! {Ref, send_proc} || {_, P, _} <- Procs] 3442 end, void), 3443 3444 %% each gets forwarded through two processes 3445 repeat(10, fun(_) -> 3446 true = lists:all(RecvNum, NSeq), 3447 true = lists:all(RecvNum, NSeq) 3448 end, void), 3449 3450 %% tell them all to 'quit' by name 3451 [N ! {Ref, quit} || {N, _, _} <- Procs], 3452 true = lists:all(RecvDown, Procs), 3453 true = lists:all(NotReg, Names), 3454 ok. 3455 3456nif_whereis_threaded(Config) when is_list(Config) -> 3457 ensure_lib_loaded(Config), 3458 3459 RegName = nif_whereis_test_threaded, 3460 undefined = erlang:whereis(RegName), 3461 3462 Self = self(), 3463 true = register(RegName, Self), 3464 3465 {ok, ProcThr} = whereis_thd_lookup(pid, RegName, "dtor to proc"), 3466 {ok, Self} = whereis_thd_result(ProcThr), 3467 3468 nif_whereis_threaded_2(RegName). 3469 3470nif_whereis_threaded_2(RegName) -> 3471 erlang:garbage_collect(), 3472 "dtor to proc" = receive_any(1000), 3473 true = unregister(RegName), 3474 3475 Port = open_port({spawn, echo_drv}, [eof]), 3476 true = register(RegName, Port), 3477 3478 {ok, PortThr} = whereis_thd_lookup(port, RegName, "dtor to port"), 3479 {ok, Port} = whereis_thd_result(PortThr), 3480 3481 nif_whereis_threaded_3(Port). 3482 3483nif_whereis_threaded_3(Port) -> 3484 erlang:garbage_collect(), 3485 {Port, {data, "dtor to port"}} = receive_any(1000), 3486 3487 port_close(Port), 3488 ok. 3489 3490%% exported to be spawned by MFA by whereis tests 3491nif_whereis_proxy({N, Ref, Mgr, Targets} = Args) -> 3492 receive 3493 {forward, To, Data} -> 3494 To ! Data, 3495 nif_whereis_proxy(Args); 3496 {Ref, quit} -> 3497 ok; 3498 {Ref, send_port} -> 3499 Msg = ?MODULE_STRING " whereis " ++ integer_to_list(N) ++ "\n", 3500 lists:foreach( 3501 fun(T) -> 3502 ok = whereis_send(port, T, Msg) 3503 end, Targets), 3504 nif_whereis_proxy(Args); 3505 {Ref, send_proc} -> 3506 lists:foreach( 3507 fun(T) -> 3508 ok = whereis_send(pid, T, {forward, Mgr, {N, Ref}}) 3509 end, Targets), 3510 nif_whereis_proxy(Args) 3511 end; 3512nif_whereis_proxy(Ref) -> 3513 receive 3514 {forward, To, Data} -> 3515 To ! Data, 3516 nif_whereis_proxy(Ref); 3517 {Ref, quit} -> 3518 ok 3519 end. 3520nif_ioq(Config) -> 3521 ensure_lib_loaded(Config), 3522 3523 Script = 3524 [{create, a}, 3525 3526 %% Test enq of erlang term binary 3527 {enqb, a}, 3528 {enqb, a, 3}, 3529 3530 %% Test enq of non-erlang term binary 3531 {enqbraw,a}, 3532 {enqbraw,a, 5}, 3533 {peek, a}, 3534 {peek_head, a}, 3535 {deq, a, 42}, 3536 3537 %% Test enqv 3538 {enqv, a, 2, 100}, 3539 {peek_head, a}, 3540 {deq, a, all}, 3541 3542 %% This skips all elements but one in the iolist 3543 {enqv, a, 5, iolist_size(nif_ioq_payload(5)) - 1}, 3544 {peek_head, a}, 3545 {peek, a}, 3546 3547 %% Ensure that enqueued refc binaries are intact after a roundtrip. 3548 %% 3549 %% This test and the ones immediately following it does not go through 3550 %% erlang:iolist_to_iovec/1 3551 {enqv, a, [nif_ioq_payload(refcbin) || _ <- lists:seq(1,20)], 0}, 3552 {peek, a}, 3553 3554 %% ... heap binaries 3555 {enqv, a, [nif_ioq_payload(heapbin) || _ <- lists:seq(1,20)], 0}, 3556 {peek, a}, 3557 3558 %% ... plain sub-binaries 3559 {enqv, a, [nif_ioq_payload(subbin) || _ <- lists:seq(1,20)], 0}, 3560 {peek, a}, 3561 3562 %% ... unaligned binaries 3563 {enqv, a, [nif_ioq_payload(unaligned_bin) || _ <- lists:seq(1,20)], 0}, 3564 {peek, a}, 3565 3566 %% Enq stuff to destroy with data in queue 3567 {enqv, a, 2, 100}, 3568 {destroy,a}, 3569 3570 %% Test destroy of new queue 3571 {create, a}, 3572 {destroy,a} 3573 ], 3574 3575 nif_ioq_run(Script), 3576 3577 %% Test that only enif_inspect_as_vec works 3578 Payload = nif_ioq_payload(5), 3579 PayloadBin = iolist_to_binary(Payload), 3580 3581 [begin 3582 PayloadBin = iolist_to_binary(ioq_nif(inspect,Payload,Stack,Env)), 3583 <<>> = iolist_to_binary(ioq_nif(inspect,[],Stack,Env)) 3584 end || Stack <- [no_stack, use_stack], Env <- [use_env, no_env]], 3585 3586 %% Test error cases 3587 3588 Q = ioq_nif(create), 3589 3590 false = ioq_nif(peek_head, Q), 3591 3592 {'EXIT', {badarg, _}} = (catch ioq_nif(deq, Q, 1)), 3593 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, 1, 1234)), 3594 3595 false = ioq_nif(peek_head, Q), 3596 3597 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [atom_in_list], 0)), 3598 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [make_ref()], 0)), 3599 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [256], 0)), 3600 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [-1], 0)), 3601 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [#{}], 0)), 3602 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [1 bsl 64], 0)), 3603 {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [{tuple}], 0)), 3604 3605 false = ioq_nif(peek_head, Q), 3606 3607 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [atom_in_list], use_stack)), 3608 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [make_ref()], no_stack)), 3609 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [256], use_stack)), 3610 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [-1], no_stack)), 3611 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [#{}], use_stack)), 3612 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [1 bsl 64], no_stack)), 3613 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [{tuple}], use_stack)), 3614 {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, <<"binary">>, use_stack)), 3615 3616 ioq_nif(destroy, Q), 3617 3618 %% Test that the example in the docs works 3619 ExampleQ = ioq_nif(create), 3620 true = ioq_nif(example, ExampleQ, nif_ioq_payload(5)), 3621 ioq_nif(destroy, ExampleQ), 3622 3623 ok. 3624 3625 3626nif_ioq_run(Script) -> 3627 nif_ioq_run(Script, #{}). 3628 3629nif_ioq_run([{Action, Name}|T], State) 3630 when Action =:= enqb; Action =:= enqbraw -> 3631 nif_ioq_run([{Action, Name, heapbin}|T], State); 3632nif_ioq_run([{Action, Name, Skip}|T], State) 3633 when Action =:= enqb, is_integer(Skip); 3634 Action =:= enqbraw, is_integer(Skip) -> 3635 nif_ioq_run([{Action, Name, heapbin, Skip}|T], State); 3636nif_ioq_run([{Action, Name, N}|T], State) 3637 when Action =:= enqv; Action =:= enqb; Action =:= enqbraw -> 3638 nif_ioq_run([{Action, Name, N, 0}|T], State); 3639nif_ioq_run([{Action, Name, N, Skip}|T], State) 3640 when Action =:= enqv; Action =:= enqb; Action =:= enqbraw -> 3641 3642 #{ q := IOQ, b := B } = Q = maps:get(Name, State), 3643 true = ioq_nif(size, IOQ) == iolist_size(B), 3644 3645 %% Sanitize the log output a bit so that it doesn't become too large. 3646 H = {Action, Name, try iolist_size(N) of Sz -> Sz catch _:_ -> N end, Skip}, 3647 ct:log("~p", [H]), 3648 3649 Data = nif_ioq_payload(N), 3650 ioq_nif(Action, IOQ, Data, Skip), 3651 3652 <<_:Skip/binary, SkippedData/binary>> = iolist_to_binary(Data), 3653 3654 true = ioq_nif(size, IOQ) == (iolist_size([B|SkippedData])), 3655 3656 nif_ioq_run(T, State#{ Name := Q#{ b := [B|SkippedData]}}); 3657nif_ioq_run([{peek, Name} = H|T], State) -> 3658 #{ q := IOQ, b := B } = maps:get(Name, State), 3659 true = ioq_nif(size, IOQ) == iolist_size(B), 3660 3661 ct:log("~p", [H]), 3662 3663 Data = ioq_nif(peek, IOQ, ioq_nif(size, IOQ)), 3664 3665 true = iolist_to_binary(B) == iolist_to_binary(Data), 3666 nif_ioq_run(T, State); 3667nif_ioq_run([{peek_head, Name} = H|T], State) -> 3668 #{ q := IOQ, b := B } = maps:get(Name, State), 3669 RefData = iolist_to_binary(B), 3670 3671 ct:log("~p", [H]), 3672 3673 {true, QueueHead} = ioq_nif(peek_head, IOQ), 3674 true = byte_size(QueueHead) > 0, 3675 3676 {RefHead, _Tail} = split_binary(RefData, byte_size(QueueHead)), 3677 3678 true = QueueHead =:= RefHead, 3679 3680 nif_ioq_run(T, State); 3681nif_ioq_run([{deq, Name, all}|T], State) -> 3682 #{ q := IOQ, b := B } = maps:get(Name, State), 3683 Size = ioq_nif(size, IOQ), 3684 true = Size == iolist_size(B), 3685 nif_ioq_run([{deq, Name, Size}|T], State); 3686nif_ioq_run([{deq, Name, N} = H|T], State) -> 3687 #{ q := IOQ, b := B } = Q = maps:get(Name, State), 3688 true = ioq_nif(size, IOQ) == iolist_size(B), 3689 3690 ct:log("~p", [H]), 3691 3692 <<_:N/binary,Remain/binary>> = iolist_to_binary(B), 3693 NewQ = Q#{ b := Remain }, 3694 3695 Sz = ioq_nif(deq, IOQ, N), 3696 3697 true = Sz == iolist_size(Remain), 3698 true = ioq_nif(size, IOQ) == iolist_size(Remain), 3699 3700 nif_ioq_run(T, State#{ Name := NewQ }); 3701nif_ioq_run([{create, Name} = H|T], State) -> 3702 ct:log("~p", [H]), 3703 nif_ioq_run(T, State#{ Name => #{ q => ioq_nif(create), b => [] } }); 3704nif_ioq_run([{destroy, Name} = H|T], State) -> 3705 #{ q := IOQ, b := B } = maps:get(Name, State), 3706 true = ioq_nif(size, IOQ) == iolist_size(B), 3707 3708 ct:log("~p", [H]), 3709 3710 ioq_nif(destroy, IOQ), 3711 3712 nif_ioq_run(T, maps:remove(Name, State)); 3713nif_ioq_run([], State) -> 3714 State. 3715 3716nif_ioq_payload(N) when is_integer(N) -> 3717 Tail = if N > 3 -> nif_ioq_payload(N-3); true -> [] end, 3718 Head = element(1, lists:split(N,[nif_ioq_payload(subbin), 3719 nif_ioq_payload(heapbin), 3720 nif_ioq_payload(refcbin), 3721 nif_ioq_payload(unaligned_bin) | Tail])), 3722 erlang:iolist_to_iovec(Head); 3723nif_ioq_payload(subbin) -> 3724 Bin = nif_ioq_payload(refcbin), 3725 Sz = size(Bin) - 1, 3726 <<_:8,SubBin:Sz/binary,_/bits>> = Bin, 3727 SubBin; 3728nif_ioq_payload(unaligned_bin) -> 3729 make_unaligned_binary(<< <<I>> || I <- lists:seq(1, 255) >>); 3730nif_ioq_payload(heapbin) -> 3731 <<"a literal heap binary">>; 3732nif_ioq_payload(refcbin) -> 3733 iolist_to_binary([lists:seq(1,255) || _ <- lists:seq(1,255)]); 3734nif_ioq_payload(Else) -> 3735 Else. 3736 3737make_unaligned_binary(Bin0) -> 3738 Size = byte_size(Bin0), 3739 <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>), 3740 Bin. 3741 3742pid(Config) -> 3743 ensure_lib_loaded(Config), 3744 Self = self(), 3745 {true, ErlNifPid} = get_local_pid_nif(Self), 3746 false = is_pid_undefined_nif(ErlNifPid), 3747 Self = make_pid_nif(ErlNifPid), 3748 3749 UndefPid = set_pid_undefined_nif(), 3750 true = is_pid_undefined_nif(UndefPid), 3751 undefined = make_pid_nif(UndefPid), 3752 0 = send_term(UndefPid, message), 3753 3754 Other = spawn(fun() -> ok end), 3755 {true,OtherNifPid} = get_local_pid_nif(Other), 3756 Cmp = compare_pids_nif(ErlNifPid, OtherNifPid), 3757 true = if Cmp < 0 -> Self < Other; 3758 Cmp > 0 -> Self > Other 3759 end, 3760 0 = compare_pids_nif(ErlNifPid, ErlNifPid), 3761 3762 {false, _} = get_local_pid_nif(undefined), 3763 ok. 3764 3765nif_term_type(Config) -> 3766 ensure_lib_loaded(Config), 3767 3768 atom = term_type_nif(atom), 3769 3770 bitstring = term_type_nif(<<1:1>>), 3771 bitstring = term_type_nif(<<1:8>>), 3772 3773 float = term_type_nif(0.0), 3774 3775 'fun' = term_type_nif(fun external:function/1), 3776 'fun' = term_type_nif(fun(A) -> A end), 3777 'fun' = term_type_nif(fun id/1), 3778 3779 integer = term_type_nif(1 bsl 1024), %Bignum. 3780 integer = term_type_nif(1), 3781 3782 list = term_type_nif([list]), 3783 list = term_type_nif([]), 3784 3785 LargeMap = maps:from_list([{N, N} || N <- lists:seq(1, 256)]), 3786 map = term_type_nif(LargeMap), 3787 map = term_type_nif(#{ small => map }), 3788 3789 pid = term_type_nif(self()), 3790 3791 Port = open_port({spawn,echo_drv},[eof]), 3792 port = term_type_nif(Port), 3793 port_close(Port), 3794 3795 reference = term_type_nif(make_ref()), 3796 3797 tuple = term_type_nif({}), 3798 3799 ok. 3800 3801last_resource_dtor_call() -> 3802 erts_debug:set_internal_state(wait, aux_work), 3803 last_resource_dtor_call_nif(). 3804 3805id(I) -> I. 3806keep_alive(Term) -> ?MODULE:id(Term). 3807 3808%% The NIFs: 3809lib_version() -> undefined. 3810call_history() -> ?nif_stub. 3811hold_nif_mod_priv_data(_Ptr) -> ?nif_stub. 3812nif_mod_call_history() -> ?nif_stub. 3813list_seq(_To) -> ?nif_stub. 3814type_test() -> ?nif_stub. 3815tuple_2_list(_) -> ?nif_stub. 3816is_identical(_,_) -> ?nif_stub. 3817compare(_,_) -> ?nif_stub. 3818hash_nif(_Type, _Term, _Salt) -> ?nif_stub. 3819many_args_100(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub. 3820clone_bin(_) -> ?nif_stub. 3821make_sub_bin(_,_,_) -> ?nif_stub. 3822string_to_bin(_,_) -> ?nif_stub. 3823atom_to_bin(_,_) -> ?nif_stub. 3824macros(_) -> ?nif_stub. 3825tuple_2_list_and_tuple(_) -> ?nif_stub. 3826iolist_2_bin(_) -> ?nif_stub. 3827get_resource_type(_) -> ?nif_stub. 3828alloc_resource(_,_) -> ?nif_stub. 3829make_resource(_) -> ?nif_stub. 3830get_resource(_,_) -> ?nif_stub. 3831release_resource(_) -> ?nif_stub. 3832release_resource_from_thread(_) -> ?nif_stub. 3833last_resource_dtor_call_nif() -> ?nif_stub. 3834make_new_resource(_,_) -> ?nif_stub. 3835check_is(_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub. 3836check_is_exception() -> ?nif_stub. 3837length_test(_,_,_,_,_,_) -> ?nif_stub. 3838make_atoms() -> ?nif_stub. 3839make_strings() -> ?nif_stub. 3840make_new_resource_binary(_) -> ?nif_stub. 3841send_list_seq(_,_) -> ?nif_stub. 3842send_new_blob(_,_) -> ?nif_stub. 3843alloc_msgenv() -> ?nif_stub. 3844clear_msgenv(_) -> ?nif_stub. 3845grow_blob(_,_) -> ?nif_stub. 3846grow_blob(_,_,_) -> ?nif_stub. 3847send_blob(_,_) -> ?nif_stub. 3848send3_blob(_,_,_) -> ?nif_stub. 3849send_blob_thread(_,_,_) -> ?nif_stub. 3850join_send_thread(_) -> ?nif_stub. 3851copy_blob(_) -> ?nif_stub. 3852send_term(_,_) -> ?nif_stub. 3853send_copy_term(_,_) -> ?nif_stub. 3854reverse_list(_) -> ?nif_stub. 3855echo_int(_) -> ?nif_stub. 3856type_sizes() -> ?nif_stub. 3857otp_9668_nif(_) -> ?nif_stub. 3858otp_9828_nif(_) -> ?nif_stub. 3859consume_timeslice_nif(_,_) -> ?nif_stub. 3860call_nif_schedule(_,_) -> ?nif_stub. 3861call_nif_exception(_) -> ?nif_stub. 3862call_nif_nan_or_inf(_) -> ?nif_stub. 3863call_nif_atom_too_long(_) -> ?nif_stub. 3864unique_integer_nif(_) -> ?nif_stub. 3865is_process_alive_nif(_) -> ?nif_stub. 3866is_port_alive_nif(_) -> ?nif_stub. 3867term_to_binary_nif(_, _) -> ?nif_stub. 3868binary_to_term_nif(_, _, _) -> ?nif_stub. 3869port_command_nif(_, _) -> ?nif_stub. 3870format_term_nif(_,_) -> ?nif_stub. 3871select_nif(_,_,_,_,_,_) -> ?nif_stub. 3872dupe_resource_nif(_) -> ?nif_stub. 3873pipe_nif() -> ?nif_stub. 3874write_nif(_,_) -> ?nif_stub. 3875read_nif(_,_) -> ?nif_stub. 3876close_nif(_) -> ?nif_stub. 3877is_closed_nif(_) -> ?nif_stub. 3878clear_select_nif(_) -> ?nif_stub. 3879last_fd_stop_call() -> ?nif_stub. 3880alloc_monitor_resource_nif() -> ?nif_stub. 3881monitor_process_nif(_,_,_,_) -> ?nif_stub. 3882demonitor_process_nif(_,_) -> ?nif_stub. 3883compare_monitors_nif(_,_) -> ?nif_stub. 3884make_monitor_term_nif(_) -> ?nif_stub. 3885monitor_frenzy_nif(_,_,_,_) -> ?nif_stub. 3886ioq_nif(_) -> ?nif_stub. 3887ioq_nif(_,_) -> ?nif_stub. 3888ioq_nif(_,_,_) -> ?nif_stub. 3889ioq_nif(_,_,_,_) -> ?nif_stub. 3890 3891%% whereis 3892whereis_send(_Type,_Name,_Msg) -> ?nif_stub. 3893whereis_term(_Type,_Name) -> ?nif_stub. 3894whereis_thd_lookup(_Type,_Name, _Msg) -> ?nif_stub. 3895whereis_thd_result(_Thd) -> ?nif_stub. 3896 3897%% maps 3898is_map_nif(_) -> ?nif_stub. 3899get_map_size_nif(_) -> ?nif_stub. 3900make_new_map_nif() -> ?nif_stub. 3901make_map_put_nif(_,_,_) -> ?nif_stub. 3902get_map_value_nif(_,_) -> ?nif_stub. 3903make_map_update_nif(_,_,_) -> ?nif_stub. 3904make_map_remove_nif(_,_) -> ?nif_stub. 3905maps_from_list_nif(_) -> ?nif_stub. 3906sorted_list_from_maps_nif(_) -> ?nif_stub. 3907 3908%% Time 3909monotonic_time(_) -> ?nif_stub. 3910time_offset(_) -> ?nif_stub. 3911convert_time_unit(_,_,_) -> ?nif_stub. 3912now_time() -> ?nif_stub. 3913cpu_time() -> ?nif_stub. 3914 3915get_local_pid_nif(_) -> ?nif_stub. 3916make_pid_nif(_) -> ?nif_stub. 3917set_pid_undefined_nif() -> ?nif_stub. 3918is_pid_undefined_nif(_) -> ?nif_stub. 3919compare_pids_nif(_, _) -> ?nif_stub. 3920 3921term_type_nif(_) -> ?nif_stub. 3922 3923dynamic_resource_call(_,_,_,_) -> ?nif_stub. 3924 3925nif_stub_error(Line) -> 3926 exit({nif_not_loaded,module,?MODULE,line,Line}). 3927