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