1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2010-2013. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: -module(nif_SUITE).
   21: 
   22: %%-define(line_trace,true).
   23: -define(CHECK(Exp,Got), check(Exp,Got,?LINE)).
   24: %%-define(CHECK(Exp,Got), ?line Exp = Got).
   25: 
   26: -include_lib("test_server/include/test_server.hrl").
   27: 
   28: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   29: 	 init_per_group/2,end_per_group/2, 
   30: 	 init_per_testcase/2, 
   31: 	 end_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1,
   32: 	 types/1, many_args/1, binaries/1, get_string/1, get_atom/1, 
   33: 	 api_macros/1,
   34: 	 from_array/1, iolist_as_binary/1, resource/1, resource_binary/1, 
   35: 	 resource_takeover/1,
   36: 	 threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1, 
   37: 	 is_checks/1,
   38: 	 get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
   39: 	 otp_9668/1, consume_timeslice/1
   40: 	]).
   41: 
   42: -export([many_args_100/100]).
   43: 
   44: 
   45: %% -export([lib_version/0,call_history/0,hold_nif_mod_priv_data/1,nif_mod_call_history/0,
   46: %% 	 list_seq/1,type_test/0,tuple_2_list/1,is_identical/2,compare/2,
   47: %% 	 clone_bin/1,make_sub_bin/3,string_to_bin/2,atom_to_bin/2,macros/1,
   48: %% 	 tuple_2_list_and_tuple/1,iolist_2_bin/1,get_resource_type/1,alloc_resource/2,
   49: %% 	 make_resource/1,get_resource/2,release_resource/1,last_resource_dtor_call/0, suite/0,
   50: %% 	 make_new_resource/2,make_new_resource_binary/1,send_list_seq/2,send_new_blob/2,
   51: %% 	 alloc_msgenv/0,clear_msgenv/1,grow_blob/2,send_blob/2,send_blob_thread/3,
   52: %% 	 join_send_thread/1]).
   53: 
   54: 
   55: -define(nif_stub,nif_stub_error(?LINE)).
   56: 
   57: suite() -> [{ct_hooks,[ts_install_cth]}].
   58: 
   59: all() -> 
   60:     [basic, reload, upgrade, heap_frag, types, many_args,
   61:      binaries, get_string, get_atom, api_macros, from_array,
   62:      iolist_as_binary, resource, resource_binary,
   63:      resource_takeover, threading, send, send2, send3,
   64:      send_threaded, neg, is_checks, get_length, make_atom,
   65:      make_string,reverse_list_test,
   66:      otp_9668, consume_timeslice
   67:     ].
   68: 
   69: groups() -> 
   70:     [].
   71: 
   72: init_per_suite(Config) ->
   73:     Config.
   74: 
   75: end_per_suite(_Config) ->
   76:     ok.
   77: 
   78: init_per_group(_GroupName, Config) ->
   79:     Config.
   80: 
   81: end_per_group(_GroupName, Config) ->
   82:     Config.
   83: 
   84: 
   85: init_per_testcase(_Case, Config) ->
   86: %    ?line Dog = ?t:timetrap(?t:seconds(60*60*24)),
   87:     Config.
   88: 
   89: end_per_testcase(_Func, _Config) ->
   90:     %%Dog = ?config(watchdog, Config),
   91:     %%?t:timetrap_cancel(Dog),
   92:     P1 = code:purge(nif_mod),
   93:     Del = code:delete(nif_mod),
   94:     P2 = code:purge(nif_mod),
   95:     io:format("fin purged=~p, deleted=~p and then purged=~p\n",[P1,Del,P2]).
   96: 
   97: basic(doc) -> ["Basic smoke test of load_nif and a simple NIF call"];
   98: basic(suite) -> [];
   99: basic(Config) when is_list(Config) ->
  100:     ensure_lib_loaded(Config),
  101:     ?line true = (lib_version() =/= undefined),
  102:     ?line [{load,1,1,101},{lib_version,1,2,102}] = call_history(),
  103:     ?line [] = call_history(),
  104:     ?line true = lists:member(?MODULE, erlang:system_info(taints)),
  105:     ok.
  106: 
  107: reload(doc) -> ["Test reload callback in nif lib"];
  108: reload(suite) -> [];  
  109: reload(Config) when is_list(Config) ->    
  110:     TmpMem = tmpmem(),
  111:     ensure_lib_loaded(Config),
  112: 
  113:     ?line Data = ?config(data_dir, Config),
  114:     ?line File = filename:join(Data, "nif_mod"),
  115:     ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
  116:     ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
  117: 
  118:     ?line ok = nif_mod:load_nif_lib(Config, 1),
  119: 
  120:     ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
  121:     ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),    
  122:         
  123:     ?line ok = nif_mod:load_nif_lib(Config, 2),
  124:     ?line 2 = nif_mod:lib_version(),
  125:     ?line [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),    
  126: 
  127:     ?line ok = nif_mod:load_nif_lib(Config, 1),
  128:     ?line 1 = nif_mod:lib_version(),
  129:     ?line [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),    
  130: 
  131:     ?line true = erlang:delete_module(nif_mod),
  132:     ?line [] = nif_mod_call_history(),    
  133: 
  134:     %%?line false= check_process_code(Pid, nif_mod),
  135:     ?line true = erlang:purge_module(nif_mod),
  136:     ?line [{unload,1,3,103}] = nif_mod_call_history(),    
  137: 
  138:     ?line true = lists:member(?MODULE, erlang:system_info(taints)),
  139:     ?line true = lists:member(nif_mod, erlang:system_info(taints)),
  140:     ?line verify_tmpmem(TmpMem),
  141:     ok.
  142: 
  143: upgrade(doc) -> ["Test upgrade callback in nif lib"];
  144: upgrade(suite) -> [];  
  145: upgrade(Config) when is_list(Config) ->    
  146:     TmpMem = tmpmem(),
  147:     ensure_lib_loaded(Config),
  148: 
  149:     ?line Data = ?config(data_dir, Config),
  150:     ?line File = filename:join(Data, "nif_mod"),
  151:     ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
  152:     ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
  153: 
  154:     ?line ok = nif_mod:load_nif_lib(Config, 1),
  155:     ?line {Pid,MRef} = nif_mod:start(),
  156:     ?line 1 = call(Pid,lib_version),
  157: 
  158:     ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
  159:     ?line [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(),    
  160:         
  161:     %% Module upgrade with same lib-version
  162:     ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
  163:     ?line undefined = nif_mod:lib_version(),
  164:     ?line 1 = call(Pid,lib_version),
  165:     ?line [{lib_version,1,4,104}] = nif_mod_call_history(),
  166: 
  167:     ?line ok = nif_mod:load_nif_lib(Config, 1),
  168:     ?line 1 = nif_mod:lib_version(),
  169:     ?line [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
  170: 
  171:     ?line upgraded = call(Pid,upgrade),
  172:     ?line false = check_process_code(Pid, nif_mod),
  173:     ?line true = erlang:purge_module(nif_mod),
  174:     ?line [{unload,1,7,107}] = nif_mod_call_history(),
  175: 
  176:     ?line 1 = nif_mod:lib_version(),
  177:     ?line [{lib_version,1,8,108}] = nif_mod_call_history(),
  178: 
  179:     ?line true = erlang:delete_module(nif_mod),
  180:     ?line [] = nif_mod_call_history(),    
  181: 
  182:     ?line Pid ! die,
  183:     ?line {'DOWN', MRef, process, Pid, normal} = receive_any(),
  184:     ?line false = check_process_code(Pid, nif_mod),
  185:     ?line true = erlang:purge_module(nif_mod),
  186:     ?line [{unload,1,9,109}] = nif_mod_call_history(),    
  187: 
  188:     %% Module upgrade with different lib version
  189:     ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
  190:     ?line undefined = nif_mod:lib_version(),
  191:     ?line {Pid2,MRef2} = nif_mod:start(),
  192:     ?line undefined = call(Pid2,lib_version),
  193: 
  194:     ?line ok = nif_mod:load_nif_lib(Config, 1),
  195:     ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
  196:     ?line 1 = call(Pid2,lib_version),
  197:     ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(),
  198: 
  199:     ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
  200:     ?line undefined = nif_mod:lib_version(),
  201:     ?line [] = nif_mod_call_history(),
  202:     ?line 1 = call(Pid2,lib_version),
  203:     ?line [{lib_version,1,4,104}] = nif_mod_call_history(),
  204: 
  205:     ?line ok = nif_mod:load_nif_lib(Config, 2),
  206:     ?line 2 = nif_mod:lib_version(),
  207:     ?line [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
  208: 
  209:     ?line 1 = call(Pid2,lib_version),
  210:     ?line [{lib_version,1,5,105}] = nif_mod_call_history(),
  211: 
  212:     ?line upgraded = call(Pid2,upgrade),
  213:     ?line false = check_process_code(Pid2, nif_mod),
  214:     ?line true = erlang:purge_module(nif_mod),
  215:     ?line [{unload,1,6,106}] = nif_mod_call_history(),
  216: 
  217:     ?line 2 = nif_mod:lib_version(),
  218:     ?line [{lib_version,2,3,203}] = nif_mod_call_history(),
  219: 
  220:     ?line true = erlang:delete_module(nif_mod),
  221:     ?line [] = nif_mod_call_history(),    
  222: 
  223:     ?line Pid2 ! die,
  224:     ?line {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
  225:     ?line false= check_process_code(Pid2, nif_mod),
  226:     ?line true = erlang:purge_module(nif_mod),
  227:     ?line [{unload,2,4,204}] = nif_mod_call_history(),    
  228: 
  229:     ?line true = lists:member(?MODULE, erlang:system_info(taints)),
  230:     ?line true = lists:member(nif_mod, erlang:system_info(taints)),
  231:     ?line verify_tmpmem(TmpMem),
  232:     ok.
  233: 
  234: heap_frag(doc) -> ["Test NIF building heap fragments"];
  235: heap_frag(suite) -> [];  
  236: heap_frag(Config) when is_list(Config) ->    
  237:     TmpMem = tmpmem(),
  238:     ensure_lib_loaded(Config),
  239:     
  240:     heap_frag_do(1,1000000),
  241:     ?line verify_tmpmem(TmpMem),
  242:     ok.
  243: 
  244: heap_frag_do(N, Max) when N > Max ->
  245:     ok;
  246: heap_frag_do(N, Max) ->
  247:     io:format("Create list of length ~p\n",[N]),
  248:     L = lists:seq(1,N),
  249:     L = list_seq(N),
  250:     heap_frag_do(((N*5) div 4) + 1, Max).
  251: 
  252: types(doc) -> ["Type tests"];
  253: types(suite) -> [];
  254: types(Config) when is_list(Config) ->
  255:     TmpMem = tmpmem(),
  256:     ensure_lib_loaded(Config),
  257:     ?line ok = type_test(),
  258:     lists:foreach(fun(Tpl) ->
  259:                     Lst = erlang:tuple_to_list(Tpl),                 
  260:                     Lst = tuple_2_list(Tpl)
  261:                   end,
  262:                   [{},{ok},{{}},{[],{}},{1,2,3,4,5}]),
  263:     Stuff = [[],{},0,0.0,(1 bsl 100),(fun()-> ok end),make_ref(),self()],
  264:     [eq_cmp(A,clone(B)) || A<-Stuff, B<-Stuff],
  265: 
  266:     {IntSz, LongSz} = type_sizes(),
  267:     UintMax = (1 bsl (IntSz*8)) - 1,
  268:     IntMax = UintMax bsr 1,
  269:     IntMin = -(IntMax+1),
  270:     UlongMax = (1 bsl (LongSz*8)) - 1,
  271:     LongMax = UlongMax bsr 1,
  272:     LongMin = -(LongMax+1),
  273:     Uint64Max = (1 bsl 64) - 1,
  274:     Int64Max = Uint64Max bsr 1,
  275:     Int64Min = -(Int64Max+1),
  276:     Limits = [{IntMin,IntMax},{0,UintMax},{LongMin,LongMax},{0,UlongMax},{Int64Min,Int64Max},{0,Uint64Max}],
  277:     io:format("Limits = ~p\n", [Limits]),
  278:     lists:foreach(fun(I) ->
  279: 			  R1 = echo_int(I),
  280: 			  %%io:format("echo_int(~p) -> ~p\n", [I, R1]),
  281: 			  R2 = my_echo_int(I, Limits),
  282: 			  ?line R1 = R2,
  283: 			  ?line true = (R1 =:= R2),
  284: 			  ?line true = (R1 == R2)
  285: 		  end, int_list()),
  286: 
  287:     ?line verify_tmpmem(TmpMem),
  288:     ?line true = (compare(-1294536544000, -1178704800000) < 0),
  289:     ?line true = (compare(-1178704800000, -1294536544000) > 0),
  290:     ?line true = (compare(-295147905179352825856, -36893488147419103232) < 0),
  291:     ?line true = (compare(-36893488147419103232, -295147905179352825856) > 0),
  292:     ?line true = (compare(-29514790517935282585612345678, -36893488147419103232) < 0),
  293:     ?line true = (compare(-36893488147419103232, -29514790517935282585612345678) > 0),
  294:     ok.
  295: 
  296: int_list() ->
  297:     Start = 1 bsl 200,
  298:     int_list([Start], -Start).
  299: int_list([N | _]=List, End) when N<End ->
  300:     List;
  301: int_list([N | _]=List, End) ->
  302:     int_list([N - (1 + (abs(N) div 3)) | List], End).
  303:     
  304: my_echo_int(I, Limits) ->
  305:     lists:map(fun({Min,Max}) ->
  306: 		      if I < Min -> false;
  307: 			 I > Max -> false;
  308: 			 true -> I
  309: 		      end
  310: 	      end, Limits).
  311: 
  312: clone(X) ->
  313:     binary_to_term(term_to_binary(X)).
  314: 
  315: eq_cmp(A,B) ->
  316:     eq_cmp_do(A,B),
  317:     eq_cmp_do([A,B],[A,B]),
  318:     eq_cmp_do({A,B},{A,B}).
  319: 
  320: eq_cmp_do(A,B) ->
  321:     %%?t:format("compare ~p and ~p\n",[A,B]),
  322:     Eq = (A =:= B),
  323:     ?line Eq = is_identical(A,B),
  324:     ?line Cmp = if
  325:             A < B -> -1;
  326:             A == B -> 0;
  327:             A > B -> 1
  328:         end,
  329:     ?line Cmp = case compare(A,B) of
  330:                     C when is_integer(C), C < 0 -> -1;
  331:                     0 -> 0;
  332:                     C when is_integer(C) -> 1
  333:                 end,       
  334:     ok. 
  335: 
  336: 
  337: many_args(doc) -> ["Test NIF with many arguments"];
  338: many_args(suite) -> [];
  339: many_args(Config) when is_list(Config) ->
  340:     TmpMem = tmpmem(),
  341:     ?line ensure_lib_loaded(Config ,1),
  342:     ?line ok = apply(?MODULE,many_args_100,lists:seq(1,100)),
  343:     ?line 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),
  344:     ?line verify_tmpmem(TmpMem),
  345:     ok.
  346: 
  347: binaries(doc) -> ["Test NIF binary handling."];
  348: binaries(suite) -> [];
  349: binaries(Config) when is_list(Config) ->
  350:     TmpMem = tmpmem(),
  351:     ?line ensure_lib_loaded(Config, 1),
  352:     ?line RefcBin = list_to_binary(lists:seq(1,255)),
  353:     ?line RefcBin = clone_bin(RefcBin),
  354:     ?line HeapBin = list_to_binary(lists:seq(1,20)),
  355:     ?line HeapBin = clone_bin(HeapBin),
  356:     ?line <<_:8,Sub1:6/binary,_/binary>> = RefcBin, 
  357:     ?line <<_:8,Sub2:6/binary,_/binary>> = HeapBin,
  358:     ?line Sub1 = Sub2,
  359:     ?line Sub1 = clone_bin(Sub1),
  360:     ?line Sub2 = clone_bin(Sub2),
  361:     ?line <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin, 
  362:     ?line <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin,
  363:     ?line Sub3 = Sub4,
  364:     ?line Sub3 = clone_bin(Sub3),
  365:     ?line Sub4 = clone_bin(Sub4),
  366:     %% When NIFs get bitstring support
  367:     %%?line <<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin, 
  368:     %%?line <<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin,
  369:     %%?line Sub5 = Sub6,
  370:     %%?line Sub5 = clone_bin(Sub5),
  371:     %%?line Sub6 = clone_bin(Sub6),
  372:     %%?line <<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin, 
  373:     %%?line <<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin,
  374:     %%?line Sub7 = Sub8,
  375:     %%?line Sub7 = clone_bin(Sub7),
  376:     %%?line Sub8 = clone_bin(Sub8),
  377:     %%?line <<>> = clone_bin(<<>>),
  378: 
  379:     <<_:8,SubBinA:200/binary,_/binary>> = RefcBin,
  380:     <<_:9,SubBinB:200/binary,_/bitstring>> = RefcBin,
  381:     <<_:8,SubBinC:17/binary,_/binary>> = HeapBin,
  382:     <<_:9,SubBinD:17/binary,_/bitstring>> = HeapBin,
  383:     test_make_sub_bin(RefcBin),
  384:     test_make_sub_bin(HeapBin),
  385:     test_make_sub_bin(SubBinA),
  386:     test_make_sub_bin(SubBinB),
  387:     test_make_sub_bin(SubBinC),
  388:     test_make_sub_bin(SubBinD),
  389:     
  390:     ?line verify_tmpmem(TmpMem),
  391:     ok.
  392: 
  393: test_make_sub_bin(Bin) ->
  394:     Size = byte_size(Bin),
  395:     Rest10 = Size - 10,
  396:     Rest1 = Size - 1,
  397:     ?line Bin = make_sub_bin(Bin, 0, Size),
  398:     <<_:10/binary,Sub0:Rest10/binary>> = Bin,
  399:     ?line Sub0 = make_sub_bin(Bin, 10, Rest10),
  400:     <<Sub1:10/binary,_/binary>> = Bin,
  401:     ?line Sub1 = make_sub_bin(Bin, 0, 10),
  402:     <<_:7/binary,Sub2:10/binary,_/binary>> = Bin,
  403:     ?line Sub2 = make_sub_bin(Bin, 7, 10),
  404:     ?line <<>> = make_sub_bin(Bin, 0, 0),
  405:     ?line <<>> = make_sub_bin(Bin, 10, 0),
  406:     ?line <<>> = make_sub_bin(Bin, Rest1, 0),
  407:     ?line <<>> = make_sub_bin(Bin, Size, 0),
  408:     ok.
  409:     
  410: get_string(doc) -> ["Test enif_get_string"];
  411: get_string(suite) -> [];
  412: get_string(Config) when is_list(Config) ->
  413:     ?line ensure_lib_loaded(Config, 1),
  414:     ?line {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10),
  415:     ?line {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8),
  416:     ?line {7, <<"hejsan",0>>} = string_to_bin("hejsan",7),
  417:     ?line {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6),
  418:     ?line {-5, <<"hejs",0>>} = string_to_bin("hejsan",5),
  419:     ?line {-1, <<0>>} = string_to_bin("hejsan",1),
  420:     ?line {0, <<>>} = string_to_bin("hejsan",0),
  421:     ?line {1, <<0>>} = string_to_bin("",1),
  422:     ?line {0, <<>>} = string_to_bin("",0),
  423:     ok.
  424: 
  425: get_atom(doc) -> ["Test enif_get_atom"];
  426: get_atom(suite) -> [];
  427: get_atom(Config) when is_list(Config) ->
  428:     ?line ensure_lib_loaded(Config, 1),
  429:     ?line {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10),
  430:     ?line {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8),
  431:     ?line {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7),
  432:     ?line {0, <<_:6/binary>>} = atom_to_bin(hejsan,6),
  433:     ?line {0, <<>>} = atom_to_bin(hejsan,0),
  434:     ?line {1, <<0>>} = atom_to_bin('',1),
  435:     ?line {0, <<>>} = atom_to_bin('',0),
  436:     ok.
  437: 
  438: api_macros(doc) -> ["Test macros enif_make_list<N> and enif_make_tuple<N>"];
  439: api_macros(suite) -> [];
  440: api_macros(Config) when is_list(Config) ->
  441:     ?line ensure_lib_loaded(Config, 1),
  442:     Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)],
  443: 		[list_to_tuple(lists:seq(1,N)) || N <- lists:seq(1,9)]
  444: 	       },
  445:     ?line Expected = macros(list_to_tuple(lists:seq(1,9))),
  446:     ok.
  447: 
  448: from_array(doc) -> ["enif_make_[tuple|list]_from_array"];
  449: from_array(suite) -> [];
  450: from_array(Config) when is_list(Config) ->
  451:     ?line ensure_lib_loaded(Config, 1),
  452:     lists:foreach(fun(Tpl) ->
  453: 			  Lst = tuple_to_list(Tpl),
  454: 			  ?line {Lst,Tpl} = tuple_2_list_and_tuple(Tpl)
  455: 		  end,
  456: 		  [{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]),
  457:     ok.
  458: 
  459: iolist_as_binary(doc) -> ["enif_inspect_iolist_as_binary"];
  460: iolist_as_binary(suite) -> [];
  461: iolist_as_binary(Config) when is_list(Config) ->
  462:     ?line ensure_lib_loaded(Config, 1),
  463:     TmpMem = tmpmem(),
  464:     List = [<<"hejsan">>, <<>>, [], [17], [<<>>],
  465: 	    [127,128,255,0],
  466: 	    [1, 2, 3, <<"abc">>, [<<"def">>,4], 5, <<"ghi">>],
  467: 	    [1, 2, 3, <<"abc">>, [<<"def">>,4], 5 | <<"ghi">>]],
  468: 	    
  469:     lists:foreach(fun(IoL) ->
  470: 			  B1 = erlang:iolist_to_binary(IoL),
  471: 			  ?line B2 = iolist_2_bin(IoL),
  472: 			  ?line B1 = B2
  473: 		  end,
  474: 		  List),
  475:     ?line verify_tmpmem(TmpMem),
  476:     ok.
  477: 
  478: resource(doc) -> ["Test memory managed objects, aka 'resources'"];
  479: resource(suite) -> [];
  480: resource(Config) when is_list(Config) ->
  481:     ?line ensure_lib_loaded(Config, 1),
  482:     ?line Type = get_resource_type(0),
  483:     resource_hugo(Type),
  484:     resource_otto(Type),
  485:     resource_new(Type),
  486:     resource_neg(Type),
  487:     ok.
  488: 
  489: resource_hugo(Type) ->
  490:     DtorCall = resource_hugo_do(Type),
  491:     erlang:garbage_collect(),
  492:     ?line DtorCall = last_resource_dtor_call(),
  493:     ok.
  494: 
  495: resource_hugo_do(Type) ->
  496:     HugoBin = <<"Hugo Hacker">>,
  497:     ?line HugoPtr = alloc_resource(Type, HugoBin),
  498:     ?line Hugo = make_resource(HugoPtr),
  499:     ?line <<>> = Hugo,
  500:     release_resource(HugoPtr),
  501:     erlang:garbage_collect(),
  502:     ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo),
  503:     Pid = spawn_link(fun() -> 			     
  504: 			     receive {Pid, Type, Resource, Ptr, Bin} ->
  505: 				     Pid ! {self(), got_it},
  506: 				     receive {Pid, check_it} ->
  507: 					     ?line {Ptr,Bin} = get_resource(Type,Resource),
  508: 					     Pid ! {self(), ok}
  509: 				     end
  510: 			     end
  511: 		     end),
  512:     Pid ! {self(), Type, Hugo, HugoPtr, HugoBin},
  513:     ?line {Pid, got_it} = receive_any(),
  514:     erlang:garbage_collect(),   % just to make our ProcBin move in memory
  515:     Pid ! {self(), check_it},
  516:     ?line {Pid, ok} = receive_any(),
  517:     ?line [] = last_resource_dtor_call(),
  518:     ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo),
  519:     {HugoPtr, HugoBin, 1}.
  520: 
  521: resource_otto(Type) ->
  522:     {OttoPtr, OttoBin} = resource_otto_do(Type),
  523:     erlang:garbage_collect(),
  524:     ?line [] = last_resource_dtor_call(),
  525:     release_resource(OttoPtr),
  526:     ?line {OttoPtr,OttoBin,1} = last_resource_dtor_call(),
  527:     ok.
  528:     
  529: resource_otto_do(Type) ->
  530:     OttoBin = <<"Otto Ordonnans">>,
  531:     ?line OttoPtr = alloc_resource(Type, OttoBin),
  532:     ?line Otto = make_resource(OttoPtr),
  533:     ?line <<>> = Otto,
  534:     %% forget resource term but keep referenced by NIF
  535:     {OttoPtr, OttoBin}.    
  536: 
  537: resource_new(Type) ->
  538:     ?line {PtrB,BinB} = resource_new_do1(Type),
  539:     erlang:garbage_collect(),
  540:     ?line {PtrB,BinB,1} = last_resource_dtor_call(),
  541:     ok.
  542:     
  543: resource_new_do1(Type) ->
  544:     ?line {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type),
  545:     erlang:garbage_collect(),
  546:     ?line {PtrA,BinA,1} = last_resource_dtor_call(),
  547:     ?line {PtrB,BinB} = get_resource(Type, ResB),
  548:     %% forget ResB and make it garbage
  549:     {PtrB,BinB}.
  550:     
  551: resource_new_do2(Type) ->
  552:     BinA = <<"NewA">>,
  553:     BinB = <<"NewB">>,
  554:     ?line ResA = make_new_resource(Type, BinA),
  555:     ?line ResB = make_new_resource(Type, BinB),
  556:     ?line <<>> = ResA,
  557:     ?line <<>> = ResB,
  558:     ?line {PtrA,BinA} = get_resource(Type, ResA),
  559:     ?line {PtrB,BinB} = get_resource(Type, ResB),
  560:     ?line true = (PtrA =/= PtrB),
  561:     ?line [] = last_resource_dtor_call(),
  562:     %% forget ResA and make it garbage
  563:     {{PtrA,BinA}, {ResB,PtrB,BinB}}.
  564: 
  565: resource_neg(TypeA) ->
  566:     resource_neg_do(TypeA),
  567: 
  568:     catch exit(42), % dummy exception to purge saved stacktraces from earlier exception
  569:     erlang:garbage_collect(),
  570:     ?line {_,_,2} = last_resource_dtor_call(),
  571:     ok.
  572: 
  573: resource_neg_do(TypeA) ->
  574:     TypeB = get_resource_type(1),
  575:     ResA = make_new_resource(TypeA, <<"Arnold">>),
  576:     ResB= make_new_resource(TypeB, <<"Bobo">>),
  577:     ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeA, ResB)),
  578:     ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeB, ResA)),
  579:     ok.
  580: 
  581: resource_binary(doc) -> ["Test enif_make_resource_binary"];
  582: resource_binary(suite) -> [];
  583: resource_binary(Config) when is_list(Config) ->
  584:     ?line ensure_lib_loaded(Config, 1),
  585:     ?line {Ptr,Bin} = resource_binary_do(),
  586:     erlang:garbage_collect(),
  587:     Last = last_resource_dtor_call(),
  588:     ?CHECK({Ptr,Bin,1}, Last),
  589:     ok.
  590: 
  591: resource_binary_do() ->
  592:     Bin = <<"Hej Hopp i lingonskogen">>,
  593:     ?line {Ptr,ResBin1} = make_new_resource_binary(Bin),
  594:     ?line ResBin1 = Bin,          
  595:     ?line ResInfo = {Ptr,_} = get_resource(binary_resource_type,ResBin1),
  596: 
  597:     Papa = self(),
  598:     Forwarder = spawn_link(fun() -> forwarder(Papa) end),
  599:     io:format("sending to forwarder pid=~p\n",[Forwarder]),  
  600:     Forwarder ! ResBin1,
  601:     ResBin2 = receive_any(),
  602:     ?line ResBin2 = ResBin1,
  603:     ?line ResInfo = get_resource(binary_resource_type,ResBin2),
  604:     Forwarder ! terminate,
  605:     ?line {Forwarder, 1} = receive_any(),
  606:     erlang:garbage_collect(),
  607:     ?line ResInfo = get_resource(binary_resource_type,ResBin1),
  608:     ?line ResInfo = get_resource(binary_resource_type,ResBin2),
  609:     ResInfo.
  610: 
  611:     
  612: -define(RT_CREATE,1).
  613: -define(RT_TAKEOVER,2).
  614: 
  615: resource_takeover(doc) -> ["Test resource takeover by module reload and upgrade"];
  616: resource_takeover(suite) -> [];  
  617: resource_takeover(Config) when is_list(Config) ->    
  618:     TmpMem = tmpmem(),
  619:     ensure_lib_loaded(Config),
  620: 
  621:     ?line Data = ?config(data_dir, Config),
  622:     ?line File = filename:join(Data, "nif_mod"),
  623:     ?line {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]),
  624:     ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
  625: 
  626:     ?line ok = nif_mod:load_nif_lib(Config, 1, 
  627: 				    [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A,
  628: 				      ?RT_CREATE},
  629: 				     {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null,
  630: 				      ?RT_CREATE},
  631: 				     {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
  632: 				      ?RT_CREATE},
  633: 				     {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
  634: 				      ?RT_CREATE},
  635: 				     {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null,
  636: 				      ?RT_CREATE},
  637: 				     {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
  638: 				      ?RT_TAKEOVER}
  639: 				    ]),
  640: 
  641:     ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
  642:     ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
  643: 
  644:     ?line {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]),
  645: 
  646:     {A1,BinA1} = make_resource(0,Holder,"A1"),
  647:     {A2,BinA2} = make_resource(0,Holder,"A2"),
  648:     {A3,BinA3} = make_resource(0,Holder,"A3"),
  649: 
  650:     {NA1,_BinNA1} = make_resource(1,Holder,"NA1"),
  651:     {NA2,BinNA2} = make_resource(1,Holder,"NA2"),
  652:     {NA3,_BinNA3} = make_resource(1,Holder,"NA3"),
  653: 
  654:     {AN1,BinAN1} = make_resource(2,Holder,"AN1"),
  655:     {AN2,_BinAN2} = make_resource(2,Holder,"AN2"),
  656:     {AN3,BinAN3} = make_resource(2,Holder,"AN3"),
  657: 
  658:     {BGX1,BinBGX1} = make_resource(3,Holder,"BGX1"),
  659:     {BGX2,BinBGX2} = make_resource(3,Holder,"BGX2"),
  660: 
  661:     {NGX1,_BinNGX1} = make_resource(4,Holder,"NGX1"),
  662:     {NGX2,_BinNGX2} = make_resource(4,Holder,"NGX2"),
  663: 
  664:     ?line [] = nif_mod_call_history(),
  665: 
  666:     ?line ok = forget_resource(A1),
  667:     ?line [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(),    
  668: 
  669:     ?line ok = forget_resource(NA1),
  670:     ?line [] = nif_mod_call_history(), % no dtor
  671: 
  672:     ?line ok = forget_resource(AN1),
  673:     ?CHECK([{{resource_dtor_A_v1,BinAN1},1,4,104}] , nif_mod_call_history()),
  674: 
  675:     ?line ok = forget_resource(BGX1),
  676:     ?CHECK([{{resource_dtor_B_v1,BinBGX1},1,5,105}], nif_mod_call_history()),
  677: 
  678:     ?line ok = forget_resource(NGX1),
  679:     ?CHECK([], nif_mod_call_history()), % no dtor
  680: 
  681:     ?line ok = nif_mod:load_nif_lib(Config, 2,
  682: 				    [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
  683: 				      ?RT_TAKEOVER},
  684: 				     {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
  685: 				      ?RT_TAKEOVER},
  686: 				     {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null,
  687: 				      ?RT_TAKEOVER},
  688: 				     {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
  689: 				      ?RT_TAKEOVER},
  690: 				     {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
  691: 				      ?RT_CREATE},
  692: 				     {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null,
  693: 				      ?RT_CREATE},
  694: 				     {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B,
  695: 				      ?RT_CREATE},
  696: 				     {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null,
  697: 				      ?RT_CREATE}
  698: 				    ]),
  699:     ?CHECK([{reload,2,1,201}], nif_mod_call_history()),
  700: 
  701:     ?line BinA2 = read_resource(0,A2),
  702:     ?line ok = forget_resource(A2),
  703:     ?CHECK([{{resource_dtor_A_v2,BinA2},2,2,202}], nif_mod_call_history()),    
  704: 
  705:     ?line ok = forget_resource(NA2),
  706:     ?CHECK([{{resource_dtor_A_v2,BinNA2},2,3,203}], nif_mod_call_history()),    
  707: 
  708:     ?line ok = forget_resource(AN2),
  709:     ?CHECK([], nif_mod_call_history()),    % no dtor
  710: 
  711:     ?line ok = forget_resource(BGX2),  % calling dtor in orphan library v1 still loaded
  712:     ?CHECK([{{resource_dtor_B_v1,BinBGX2},1,6,106}], nif_mod_call_history()),
  713:     % How to test that lib v1 is closed here?
  714: 
  715:     ?line ok = forget_resource(NGX2),
  716:     ?CHECK([], nif_mod_call_history()),  % no dtor
  717: 
  718:     {BGY1,BinBGY1} = make_resource(3,Holder,"BGY1"),
  719:     {NGY1,_BinNGY1} = make_resource(4,Holder,"NGY1"),
  720: 
  721:     %% Module upgrade with same lib-version
  722:     ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
  723:     ?line undefined = nif_mod:lib_version(),
  724:     ?line ok = nif_mod:load_nif_lib(Config, 2,
  725: 				    [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B,
  726: 				      ?RT_TAKEOVER},
  727: 				     {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null,
  728: 				      ?RT_TAKEOVER},
  729: 				     {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
  730: 				      ?RT_TAKEOVER},
  731: 				     {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A,
  732: 				      ?RT_TAKEOVER},
  733: 				     {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B,
  734: 				      ?RT_CREATE},
  735: 				     {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null,
  736: 				      ?RT_CREATE}
  737: 				    ]),
  738: 
  739:     ?line 2 = nif_mod:lib_version(),
  740:     ?CHECK([{upgrade,2,4,204},{lib_version,2,5,205}], nif_mod_call_history()),
  741: 
  742:     ?line ok = forget_resource(A3),
  743:     ?CHECK([{{resource_dtor_B_v2,BinA3},2,6,206}], nif_mod_call_history()),    
  744: 
  745:     ?line ok = forget_resource(NA3),
  746:     ?CHECK([], nif_mod_call_history()),    
  747: 
  748:     ?line ok = forget_resource(AN3),
  749:     ?CHECK([{{resource_dtor_A_v2,BinAN3},2,7,207}], nif_mod_call_history()),
  750: 
  751:     {A4,BinA4} = make_resource(2,Holder, "A4"),
  752:     {NA4,BinNA4} = make_resource(0,Holder, "NA4"),
  753:     {AN4,_BinAN4} = make_resource(1,Holder, "AN4"),
  754: 
  755:     {BGZ1,BinBGZ1} = make_resource(3,Holder,"BGZ1"),
  756:     {NGZ1,_BinNGZ1} = make_resource(4,Holder,"NGZ1"),
  757: 
  758:     ?line false = code:purge(nif_mod),
  759:     ?line [] = nif_mod_call_history(),
  760: 
  761:     ?line ok = forget_resource(NGY1),
  762:     ?line [] = nif_mod_call_history(),
  763: 
  764:     ?line ok = forget_resource(BGY1),  % calling dtor in orphan library v2 still loaded
  765:     ?line [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(),
  766: 
  767:     %% Module upgrade with other lib-version
  768:     ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
  769:     ?line undefined = nif_mod:lib_version(),
  770:     ?line ok = nif_mod:load_nif_lib(Config, 1,
  771: 				    [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
  772: 				      ?RT_TAKEOVER},
  773: 				     {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
  774: 				      ?RT_TAKEOVER},
  775: 				     {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",null,
  776: 				      ?RT_TAKEOVER},
  777: 				     {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A,
  778: 				      ?RT_TAKEOVER}
  779: 				    ]),
  780: 
  781:     ?line 1 = nif_mod:lib_version(),
  782:     ?line [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
  783: 
  784:     %%?line false= check_process_code(Pid, nif_mod),
  785:     ?line false = code:purge(nif_mod),
  786:     %% no unload here as we still have instances with destructors
  787:     ?line [] = nif_mod_call_history(),
  788: 
  789:     ?line ok = forget_resource(BGZ1),  % calling dtor in orphan library v2 still loaded
  790:     ?line [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(),
  791: 
  792:     ?line ok = forget_resource(NGZ1),
  793:     ?line [] = nif_mod_call_history(),
  794: 
  795:     ?line ok = forget_resource(A4),
  796:     ?line [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(),
  797: 
  798:     ?line ok = forget_resource(NA4),
  799:     ?line [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(),
  800: 
  801:     ?line ok = forget_resource(AN4),
  802:     ?line [] = nif_mod_call_history(),
  803: 
  804:     ?line true = lists:member(?MODULE, erlang:system_info(taints)),
  805:     ?line true = lists:member(nif_mod, erlang:system_info(taints)),
  806:     ?line verify_tmpmem(TmpMem),    
  807:     ok.
  808: 
  809: make_resource(Type,Holder,Str) when is_list(Str) ->
  810:     Bin = list_to_binary(Str),
  811:     A1 = make_resource_do(Type,Holder,Bin),
  812:     ?line Bin = read_resource(Type,A1),
  813:     {A1,Bin}.
  814: 
  815: make_resource_do(Type, Holder, Bin) ->
  816:     Holder ! {self(), make, Type, Bin},
  817:     {Holder, make_ok, Id} = receive_any(),
  818:     {Holder,Id}.
  819: 
  820: read_resource(Type, {Holder,Id}) ->
  821:     Holder ! {self(), get, Type, Id},
  822:     {Holder, get_ok, Bin} = receive_any(),
  823:     Bin.
  824: 
  825: forget_resource({Holder,Id}) ->
  826:     Holder ! {self(), forget, Id},
  827:     {Holder, forget_ok, Id} = receive_any(),
  828:     ok.
  829: 
  830: 
  831: resource_holder() ->
  832:     resource_holder([]).
  833: resource_holder(List) ->
  834:     %%io:format("resource_holder waiting for msg\n", []),
  835:     Msg = receive_any(),
  836:     %%io:format("resource_holder got ~p with list = ~p\n", [Msg,List]),
  837:     case Msg of
  838: 	{Pid, make, Type, Bin} ->	    
  839: 	    ?line Resource = nif_mod:make_new_resource(Type, Bin),
  840: 	    Id = {make_ref(),Bin},
  841: 	    Pid ! {self(), make_ok, Id},
  842: 	    resource_holder([{Id,Resource} | List]);
  843: 	{Pid, get, Type, Id} ->
  844: 	    {Id,Resource} = lists:keyfind(Id, 1, List),
  845: 	    Pid ! {self(), get_ok, nif_mod:get_resource(Type, Resource)},
  846: 	    resource_holder(List);
  847: 	
  848: 	{Pid, forget, Id} ->
  849: 	    NewList = lists:keydelete(Id, 1, List),
  850: 	    %%io:format("resource_holder forget: NewList = ~p\n", [NewList]),
  851: 	    resource_holder(Pid, {self(),forget_ok,Id}, NewList)
  852:     end.
  853: 
  854: resource_holder(Pid,Reply,List) ->
  855:     erlang:garbage_collect(),
  856:     %%io:format("resource_holder GC'ed, now send ~p to ~p\n", [Reply,Pid]),
  857:     Pid ! Reply,
  858:     resource_holder(List).
  859: 
  860: 
  861: threading(doc) -> ["Test the threading API functions (reuse tests from driver API)"];
  862: threading(Config) when is_list(Config) ->
  863:     case erlang:system_info(threads) of
  864:     	true -> threading_do(Config);
  865: 	false -> {skipped,"No thread support"}
  866:     end.
  867: 
  868: threading_do(Config) ->
  869:     ?line Data = ?config(data_dir, Config),
  870:     ?line File = filename:join(Data, "tester"),
  871:     ?line {ok,tester,ModBin} = compile:file(File, [binary,return_errors]),
  872:     ?line {module,tester} = erlang:load_module(tester,ModBin),
  873: 
  874:     ?line ok = tester:load_nif_lib(Config, "basic"),   
  875:     ?line ok = tester:run(),
  876: 
  877:     ?line ok = tester:load_nif_lib(Config, "rwlock"),
  878:     ?line ok = tester:run(),
  879: 
  880:     ?line ok = tester:load_nif_lib(Config, "tsd"),
  881:     ?line ok = tester:run().
  882: 
  883: send(doc) -> ["Test NIF message sending"];
  884: send(Config) when is_list(Config) ->    
  885:     ensure_lib_loaded(Config),
  886: 
  887:     N = 1500,
  888:     List = lists:seq(1,N),
  889:     ?line {ok,1} = send_list_seq(N, self),
  890:     ?line {ok,1} = send_list_seq(N, self()),
  891:     ?line List = receive_any(),
  892:     ?line List = receive_any(),
  893:     Papa = self(),
  894:     spawn_link(fun() -> ?line {ok,1} = send_list_seq(N, Papa) end),
  895:     ?line List = receive_any(),
  896: 
  897:     ?line {ok, 1, BlobS} = send_new_blob(self(), other_term()),
  898:     ?line BlobR = receive_any(),
  899:     io:format("Sent ~p\nGot ~p\n", [BlobS, BlobR]),
  900:     ?line BlobR = BlobS,
  901: 
  902:     %% send to dead pid
  903:     {DeadPid, DeadMon} = spawn_monitor(fun() -> void end),
  904:     ?line {'DOWN', DeadMon, process, DeadPid, normal} = receive_any(),
  905:     {ok,0} = send_list_seq(7, DeadPid),
  906:     ok.
  907: 
  908: send2(doc) -> ["More NIF message sending"];
  909: send2(Config) when is_list(Config) ->
  910:     ensure_lib_loaded(Config),
  911: 
  912:     send2_do1(fun send_blob_dbg/2),
  913:     ok.
  914: 
  915: send_threaded(doc) -> ["Send msg from user thread"];
  916: send_threaded(Config) when is_list(Config) ->
  917:     case erlang:system_info(smp_support) of
  918: 	true ->
  919: 	    send2_do1(fun(ME,To) -> send_blob_thread_dbg(ME,To,join) end),
  920: 	    send2_do1(fun(ME,To) -> send_blob_thread_and_join(ME,To) end),
  921: 	    ok;
  922: 	false ->
  923: 	    {skipped,"No threaded send on non-SMP"}
  924:     end.
  925: 
  926: 
  927: send2_do1(SendBlobF) ->
  928:     io:format("sending to self=~p\n",[self()]),
  929:     send2_do2(SendBlobF, self()),
  930: 
  931:     Papa = self(),
  932:     Forwarder = spawn_link(fun() -> forwarder(Papa) end),
  933:     io:format("sending to forwarder pid=~p\n",[Forwarder]),
  934:     send2_do2(SendBlobF, Forwarder),
  935:     Forwarder ! terminate,
  936:     ?line {Forwarder, 4} = receive_any(),
  937:     ok.
  938: 
  939: send2_do2(SendBlobF, To) ->   
  940:     MsgEnv = alloc_msgenv(),
  941:     repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
  942:     ?line {ok,1,Blob0} = SendBlobF(MsgEnv, To),
  943:     ?line Blob1 = receive_any(),
  944:     ?line Blob1 = Blob0,
  945:     
  946:     clear_msgenv(MsgEnv),
  947:     repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
  948:     ?line {ok,1,Blob2} = SendBlobF(MsgEnv, To),
  949:     ?line Blob3 = receive_any(),
  950:     ?line Blob3 = Blob2,
  951: 
  952:     clear_msgenv(MsgEnv),
  953:     repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
  954: 
  955:     clear_msgenv(MsgEnv),
  956:     repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
  957:     ?line {ok,1,Blob4} = SendBlobF(MsgEnv, To),
  958:     ?line Blob5 = receive_any(),
  959:     ?line Blob5 = Blob4,
  960: 
  961:     clear_msgenv(MsgEnv),
  962:     clear_msgenv(MsgEnv),
  963:     repeat(50, fun(_) -> grow_blob(MsgEnv,other_term()) end, []),
  964:     ?line {ok,1,Blob6} = SendBlobF(MsgEnv, To),
  965:     ?line Blob7 = receive_any(),
  966:     ?line Blob7 = Blob6,
  967: 
  968:     ok.
  969: 
  970: 
  971: send_blob_thread_and_join(MsgEnv, To) ->
  972:     ?line {ok,Blob} = send_blob_thread_dbg(MsgEnv, To, no_join),
  973:     ?line {ok,SendRes} = join_send_thread(MsgEnv),
  974:     {ok,SendRes,Blob}.
  975: 
  976: send_blob_dbg(MsgEnv, To) ->
  977:     Ret = send_blob(MsgEnv, To),
  978:     %%io:format("send_blob to ~p returned ~p\n",[To,Ret]),
  979:     Ret.
  980: 
  981: send_blob_thread_dbg(MsgEnv, To, Join) ->
  982:     Ret = send_blob_thread(MsgEnv, To, Join),
  983:     %%io:format("send_blob_thread to ~p Join=~p returned ~p\n",[To,Join,Ret]),
  984:     Ret.
  985: 
  986: 
  987: forwarder(To) ->
  988:     forwarder(To, 0).
  989: forwarder(To, N) ->
  990:     case receive_any() of
  991: 	terminate ->
  992: 	    To ! {self(), N};
  993: 	Msg ->	    
  994: 	    To ! Msg,	   
  995: 	    forwarder(To, N+1)
  996:     end.
  997: 
  998: other_term() ->
  999:     {fun(X,Y) -> X*Y end, make_ref()}.
 1000: 
 1001: send3(doc) -> ["Message sending stress test"];
 1002: send3(Config) when is_list(Config) ->
 1003:     %% Let a number of processes send random message blobs between each other
 1004:     %% using enif_send. Kill and spawn new ones randomly to keep a ~constant
 1005:     %% number of workers running.
 1006:     Seed = now(),
 1007:     io:format("seed: ~p\n",[Seed]), 
 1008:     random:seed(Seed),    
 1009:     ets:new(nif_SUITE,[named_table,public]),
 1010:     ?line true = ets:insert(nif_SUITE,{send3,0,0,0,0}),
 1011:     timer:send_after(10000, timeout), % Run for 10 seconds
 1012:     SpawnCnt = send3_controller(0, [], [], 20),
 1013:     ?line [{_,Rcv,SndOk,SndFail,Balance}] = ets:lookup(nif_SUITE,send3),
 1014:     io:format("spawns=~p received=~p, sent=~p send-failure=~p balance=~p\n",
 1015:               [SpawnCnt,Rcv,SndOk,SndFail,Balance]),
 1016:     ets:delete(nif_SUITE).
 1017: 
 1018: send3_controller(SpawnCnt, [], _, infinity) ->
 1019:     SpawnCnt;
 1020: send3_controller(SpawnCnt0, Mons0, Pids0, Tick) ->
 1021:     receive 
 1022:         timeout ->
 1023:             io:format("Timeout. Sending 'halt' to ~p\n",[Pids0]),            
 1024:             lists:foreach(fun(P) -> P ! {halt,self()} end, Pids0),
 1025:             lists:foreach(fun(P) -> receive {halted,P} -> ok end end, Pids0),
 1026:             QTot = lists:foldl(fun(P,QSum) ->
 1027:                                  {message_queue_len,QLen} = 
 1028:                                     erlang:process_info(P,message_queue_len),
 1029:                                  QSum + QLen
 1030:                                end, 0, Pids0),
 1031:             io:format("Total queue length ~p\n",[QTot]),            
 1032:             lists:foreach(fun(P) -> P ! die end, Pids0),
 1033:             send3_controller(SpawnCnt0, Mons0, [], infinity);
 1034:         {'DOWN', MonRef, process, _Pid, _} ->
 1035:             Mons1 = lists:delete(MonRef, Mons0),
 1036:             %%io:format("Got DOWN from ~p. Monitors left: ~p\n",[Pid,Mons1]),            
 1037:             send3_controller(SpawnCnt0, Mons1, Pids0, Tick)
 1038:     after Tick -> 
 1039:         Max = 20,
 1040:         N = length(Pids0),
 1041:         PidN = random:uniform(Max),
 1042:         %%io:format("N=~p PidN=~p Pids0=~p\n", [N,PidN,Pids0]), 
 1043:         case PidN > N of
 1044:             true ->
 1045:                 {NewPid,Mon} = spawn_opt(fun send3_proc/0, [link,monitor]),                
 1046:                 lists:foreach(fun(P) -> P ! {is_born,NewPid} end, Pids0),
 1047:                 ?line Balance = ets:lookup_element(nif_SUITE,send3,5),
 1048:                 Inject = (Balance =< 0),
 1049:                 case Inject of
 1050:                     true ->  ok;
 1051:                     false -> ets:update_element(nif_SUITE,send3,{5,-1})
 1052:                 end,
 1053:                 NewPid ! {pids,Pids0,Inject},
 1054:                 send3_controller(SpawnCnt0+1, [Mon|Mons0], [NewPid|Pids0], Tick);
 1055:             false ->
 1056:                 KillPid = lists:nth(PidN,Pids0),
 1057:                 KillPid ! die,
 1058:                 Pids1 = lists:delete(KillPid, Pids0),
 1059:                 lists:foreach(fun(P) -> P ! {is_dead,KillPid} end, Pids1),
 1060:                 send3_controller(SpawnCnt0, Mons0, Pids1, Tick)
 1061:         end        
 1062:    end.
 1063: 
 1064: send3_proc() ->
 1065:     %%io:format("Process ~p spawned\n",[self()]),
 1066:     send3_proc([self()], {0,0,0}, {1,2,3,4,5}).
 1067: send3_proc(Pids0, Counters={Rcv,SndOk,SndFail}, State0) ->
 1068:     %%io:format("~p: Pids0=~p", [self(), Pids0]),
 1069:     %%timer:sleep(10),
 1070:     receive 
 1071:         {pids, Pids1, Inject} ->
 1072:             %%io:format("~p: got ~p Inject=~p\n", [self(), Pids1, Inject]), 
 1073:             ?line Pids0 = [self()],
 1074:             Pids2 = [self() | Pids1],
 1075:             case Inject of
 1076:                 true -> send3_proc_send(Pids2, Counters, State0);
 1077:                 false -> send3_proc(Pids2, Counters, State0)
 1078:             end;
 1079:         {is_born, Pid} ->
 1080:             %%io:format("~p: is_born ~p, got ~p\n", [self(), Pid, Pids0]), 
 1081:             send3_proc([Pid | Pids0], Counters, State0);
 1082:         {is_dead, Pid} ->            
 1083:             Pids1 = lists:delete(Pid,Pids0),
 1084:             %%io:format("~p: is_dead ~p, got ~p\n", [self(), Pid, Pids1]),
 1085:             send3_proc(Pids1, Counters, State0);
 1086:         {blob, Blob0} ->
 1087:             %%io:format("~p: blob ~p\n", [self(), Blob0]),
 1088:             State1 = send3_new_state(State0, Blob0),
 1089:             send3_proc_send(Pids0, {Rcv+1,SndOk,SndFail}, State1);
 1090:         die ->
 1091:             %%io:format("Process ~p terminating, stats = ~p\n",[self(),Counters]),
 1092:             {message_queue_len,Dropped} = erlang:process_info(self(),message_queue_len),
 1093:             _R = ets:update_counter(nif_SUITE,send3,
 1094:                                [{2,Rcv},{3,SndOk},{4,SndFail},{5,1-Dropped}]),
 1095:             %%io:format("~p: dies R=~p\n", [self(), R]),
 1096:             ok;
 1097:         {halt,Papa} ->
 1098:             Papa ! {halted,self()},
 1099:             io:format("~p halted\n",[self()]),
 1100:             receive die -> ok end,
 1101:             io:format("~p dying\n",[self()])
 1102:     end.
 1103: 
 1104: send3_proc_send(Pids, {Rcv,SndOk,SndFail}, State0) ->
 1105:     To = lists:nth(random:uniform(length(Pids)),Pids),
 1106:     Blob = send3_make_blob(),
 1107:     State1 = send3_new_state(State0,Blob), 
 1108:     case send3_send(To, Blob) of
 1109:         true ->
 1110:             send3_proc(Pids, {Rcv,SndOk+1,SndFail}, State1);
 1111:         false ->
 1112:             send3_proc(Pids, {Rcv,SndOk,SndFail+1}, State1)
 1113:     end.
 1114: 
 1115: 
 1116: send3_make_blob() ->    
 1117:     case random:uniform(20)-1 of
 1118:         0 -> {term,[]};
 1119:         N ->
 1120:             MsgEnv = alloc_msgenv(), 
 1121:             repeat(N bsr 1,
 1122:                    fun(_) -> grow_blob(MsgEnv,other_term(),random:uniform(1 bsl 20))
 1123:                    end, void),
 1124:             case (N band 1) of
 1125:                 0 -> {term,copy_blob(MsgEnv)};
 1126:                 1 -> {msgenv,MsgEnv}
 1127:             end
 1128:     end.
 1129: 
 1130: send3_send(Pid, Msg) ->
 1131:     %% 90% enif_send and 10% normal bang
 1132:     case random:uniform(10) of
 1133:         1 -> send3_send_bang(Pid,Msg);
 1134:         _ -> send3_send_nif(Pid,Msg)
 1135:     end.
 1136: send3_send_nif(Pid, {term,Blob}) ->
 1137:     %%io:format("~p send term nif\n",[self()]),
 1138:     send_term(Pid, {blob, Blob}) =:= 1;
 1139: send3_send_nif(Pid, {msgenv,MsgEnv}) ->
 1140:     %%io:format("~p send blob nif\n",[self()]),   
 1141:     send3_blob(MsgEnv, Pid, blob) =:= 1.
 1142: 
 1143: send3_send_bang(Pid, {term,Blob}) ->
 1144:     %%io:format("~p send term bang\n",[self()]),   
 1145:     Pid ! {blob, Blob},
 1146:     true;
 1147: send3_send_bang(Pid, {msgenv,MsgEnv}) ->
 1148:     %%io:format("~p send blob bang\n",[self()]),   
 1149:     Pid ! {blob, copy_blob(MsgEnv)},
 1150:     true.
 1151: 
 1152: send3_new_state(State, Blob) ->
 1153:     case random:uniform(5+2) of
 1154:         N when N =< 5-> setelement(N, State, Blob);
 1155:         _ -> State  % Don't store blob
 1156:     end.
 1157: 
 1158: neg(doc) -> ["Negative testing of load_nif"];
 1159: neg(Config) when is_list(Config) ->
 1160:     TmpMem = tmpmem(),
 1161:     ?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)),
 1162:     ?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0),
 1163:     
 1164:     ?line Data = ?config(data_dir, Config),
 1165:     ?line File = filename:join(Data, "nif_mod"),
 1166:     ?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
 1167:     ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
 1168: 
 1169:     ?line {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init),    
 1170:     ?line verify_tmpmem(TmpMem),
 1171:     ?line ok.
 1172: 
 1173: is_checks(doc) -> ["Test all enif_is functions"];
 1174: is_checks(Config) when is_list(Config) ->
 1175:     ?line ensure_lib_loaded(Config, 1),
 1176:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1177: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1178: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 12),
 1179:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1180: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1181: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -12),
 1182:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1183: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1184: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551617),
 1185:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1186: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1187: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551617),
 1188:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1189: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1190: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 99.146),
 1191:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1192: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1193: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -99.146),
 1194:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1195: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1196: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, 18446744073709551616.2e2),
 1197:     ?line ok = check_is(hejsan, <<19,98>>, make_ref(), ok, fun() -> ok end,
 1198: 			self(), hd(erlang:ports()), [], [1,9,9,8],
 1199: 			{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2),
 1200:     try
 1201:         ?line error = check_is_exception(),
 1202:         ?line throw(expected_badarg)
 1203:     catch
 1204:         error:badarg ->
 1205:             ?line ok
 1206:     end.
 1207: 
 1208: get_length(doc) -> ["Test all enif_get_length functions"];
 1209: get_length(Config) when is_list(Config) ->
 1210:     ?line ensure_lib_loaded(Config, 1),
 1211:     ?line ok = length_test(hejsan, "hejsan", [], [], not_a_list).
 1212: 
 1213: ensure_lib_loaded(Config) ->
 1214:     ensure_lib_loaded(Config, 1).
 1215: ensure_lib_loaded(Config, Ver) ->
 1216:     ?line case lib_version() of
 1217: 	      undefined ->
 1218: 		  ?line Path = ?config(data_dir, Config),    
 1219: 		  ?line Lib = "nif_SUITE." ++ integer_to_list(Ver),
 1220: 		  ?line ok = erlang:load_nif(filename:join(Path,Lib), []);
 1221: 	      Ver when is_integer(Ver) ->
 1222: 		  ok
 1223: 	  end.
 1224: 
 1225: make_atom(Config) when is_list(Config) ->
 1226:     ?line ensure_lib_loaded(Config, 1),
 1227:     An0Atom = an0atom,
 1228:     An0Atom0 = 'an\000atom\000',
 1229:     ?line Atoms = make_atoms(),
 1230:     ?line 7 = size(Atoms),
 1231:     ?line Atoms = {An0Atom,An0Atom,An0Atom,An0Atom0,An0Atom,An0Atom,An0Atom0}.
 1232: 
 1233: make_string(Config) when is_list(Config) ->
 1234:     ?line ensure_lib_loaded(Config, 1),
 1235:     ?line Strings = make_strings(),
 1236:     ?line 5 = size(Strings),
 1237:     A0String = "a0string",
 1238:     A0String0 = [$a,0,$s,$t,$r,$i,$n,$g,0],
 1239:     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],
 1240:     ?line Strings = {A0String,A0String,A0String,A0String0, AStringWithAccents}.
 1241: 
 1242: reverse_list_test(Config) ->
 1243:     ?line ensure_lib_loaded(Config, 1),
 1244:     List = lists:seq(1,100),
 1245:     RevList = lists:reverse(List),
 1246:     ?line RevList = reverse_list(List),
 1247:     ?line badarg = reverse_list(foo).
 1248: 
 1249: otp_9668(doc) -> ["Memory leak of tmp-buffer when inspecting iolist or unaligned binary in unbound environment"];
 1250: otp_9668(Config) ->
 1251:     ensure_lib_loaded(Config, 1),
 1252:     TmpMem = tmpmem(),
 1253:     IOList = ["This",' ',<<"is">>,' ',[<<"an iolist">>,'.']],    
 1254:     otp_9668_nif(IOList),
 1255: 
 1256:     <<_:5/bitstring,UnalignedBin:10/binary,_/bitstring>> = <<"Abuse me as unaligned">>,
 1257:     otp_9668_nif(UnalignedBin),
 1258: 
 1259:     ?line verify_tmpmem(TmpMem),
 1260:     ok.
 1261: 
 1262: consume_timeslice(Config) when is_list(Config) ->
 1263:     CONTEXT_REDS = 2000,
 1264:     Me = self(),
 1265:     Go = make_ref(),
 1266:     RedDiff = make_ref(),
 1267:     Done = make_ref(),
 1268:     DummyMFA = {?MODULE,dummy_call,1},
 1269:     P = spawn(fun () ->
 1270: 		      receive Go -> ok end,
 1271: 		      {reductions, R1} = process_info(self(), reductions),
 1272: 		      1 = consume_timeslice_nif(100, false),
 1273: 		      dummy_call(111),
 1274: 		      0 = consume_timeslice_nif(90, false),
 1275: 		      dummy_call(222),
 1276: 		      1 = consume_timeslice_nif(10, false),
 1277: 		      dummy_call(333),
 1278: 		      0 = consume_timeslice_nif(25, false),
 1279: 		      0 = consume_timeslice_nif(25, false),
 1280: 		      0 = consume_timeslice_nif(25, false),
 1281: 		      1 = consume_timeslice_nif(25, false),
 1282: 		      0 = consume_timeslice_nif(25, false),
 1283: 
 1284: 		      ok = case consume_timeslice_nif(1, true) of
 1285: 			       Cnt when Cnt > 70, Cnt < 80 -> ok;
 1286: 			       Other -> Other
 1287: 			   end,
 1288: 		      dummy_call(444),
 1289: 
 1290: 		      {reductions, R2} = process_info(self(), reductions),
 1291: 		      Me ! {RedDiff, R2 - R1},
 1292: 		      exit(Done)
 1293: 	end),
 1294:     erlang:yield(),
 1295: 
 1296:     erlang:trace_pattern(DummyMFA, [], [local]),
 1297:     ?line 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]),
 1298: 
 1299:     P ! Go,
 1300: 
 1301:     %% receive Go -> ok end,
 1302:     ?line {trace, P, in, _} = next_tmsg(P),
 1303:     
 1304:     %% consume_timeslice_nif(100),
 1305:     %% dummy_call(111)
 1306:     ?line {trace, P, out, _} = next_tmsg(P),
 1307:     ?line {trace, P, in, _} = next_tmsg(P),
 1308:     ?line {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P),
 1309: 
 1310:     %% consume_timeslice_nif(90),
 1311:     %% dummy_call(222)
 1312:     ?line {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P),
 1313: 
 1314:     %% consume_timeslice_nif(10),
 1315:     %% dummy_call(333)
 1316:     ?line {trace, P, out, _} = next_tmsg(P),
 1317:     ?line {trace, P, in, _} = next_tmsg(P),
 1318:     ?line {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P),
 1319: 
 1320:     %% 25,25,25,25, 25
 1321:     ?line {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
 1322:     ?line {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
 1323: 
 1324:     %% consume_timeslice(1,true)
 1325:     %% dummy_call(444)
 1326:     ?line {trace, P, out, DummyMFA} = next_tmsg(P),
 1327:     ?line {trace, P, in, DummyMFA} = next_tmsg(P),
 1328:     ?line {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P),
 1329: 
 1330:     %% exit(Done)
 1331:     ?line {trace, P, exit, Done} = next_tmsg(P),
 1332: 
 1333:     ExpReds = (100 + 90 + 10 + 25*5 + 75) * CONTEXT_REDS div 100,
 1334:     receive
 1335: 	{RedDiff, Reductions} when Reductions < (ExpReds + 10), Reductions > (ExpReds - 10) ->
 1336: 	    io:format("Reductions = ~p~n", [Reductions]),
 1337: 	    ok;
 1338: 	{RedDiff, Reductions} ->
 1339: 	    ?t:fail({unexpected_reduction_count, Reductions})
 1340:     end,
 1341:     
 1342:     none = next_msg(P),
 1343: 
 1344:     ok.
 1345: 
 1346: next_msg(Pid) ->
 1347:     receive
 1348: 	M -> M
 1349:     after 100 ->
 1350: 	  none
 1351:     end.
 1352: 
 1353: next_tmsg(Pid) ->    
 1354:     receive TMsg when is_tuple(TMsg),
 1355: 		      element(1, TMsg) == trace,
 1356: 		      element(2, TMsg) == Pid ->
 1357:      	    TMsg
 1358:      after 100 ->
 1359:      	    none
 1360:      end.
 1361: 
 1362: dummy_call(_) ->    
 1363:     ok.
 1364: 
 1365: tmpmem() ->
 1366:     case erlang:system_info({allocator,temp_alloc}) of
 1367: 	false -> undefined;
 1368: 	MemInfo ->
 1369: 	    MSBCS = lists:foldl(
 1370: 		      fun ({instance, _, L}, Acc) ->
 1371: 			      {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
 1372: 			      {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
 1373: 			      [MBCS,SBCS | Acc]
 1374: 		      end,
 1375: 		      [],
 1376: 		      MemInfo),
 1377: 	    lists:foldl(
 1378: 	      fun(L, {Bl0,BlSz0}) ->
 1379: 		      {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L),
 1380: 		      {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
 1381: 		      {Bl0+Bl,BlSz0+BlSz}
 1382: 	      end, {0,0}, MSBCS)
 1383:     end.
 1384: 
 1385: verify_tmpmem(MemInfo) ->
 1386:     %%wait_for_test_procs(),
 1387:     case tmpmem() of
 1388: 	MemInfo ->
 1389: 	    io:format("Tmp mem info: ~p", [MemInfo]),
 1390: 	    case MemInfo of
 1391: 		{notsup,undefined} ->
 1392: 		    %% Use 'erl +Mea max' to do more complete memory leak testing.
 1393: 		    {comment,"Incomplete or no mem leak testing"};
 1394: 		_ ->
 1395: 		    ok
 1396: 	    end;
 1397: 	Other ->
 1398: 	    io:format("Expected: ~p", [MemInfo]),
 1399: 	    io:format("Actual:   ~p", [Other]),
 1400: 	    ?t:fail()
 1401:     end.
 1402: 
 1403: call(Pid,Cmd) ->
 1404:     %%io:format("~p calling ~p with ~p\n",[self(), Pid, Cmd]),
 1405:     Pid ! {self(), Cmd},
 1406:     receive
 1407: 	{Pid,Reply} -> Reply
 1408:     end.
 1409: 
 1410: receive_any() ->
 1411:     receive M -> M end.	     
 1412: 
 1413: repeat(0, _, Arg) ->
 1414:     Arg;
 1415: repeat(N, Fun, Arg0) ->
 1416:     repeat(N-1, Fun, Fun(Arg0)).
 1417: 
 1418: check(Exp,Got,Line) ->
 1419:     case Got of
 1420:  	Exp -> Exp;	    
 1421:   	_ ->
 1422:   	    io:format("CHECK at ~p: Expected ~p but got ~p\n",[Line,Exp,Got]),
 1423:  	    Got
 1424:     end.
 1425: 	    
 1426: 
 1427: %% The NIFs:
 1428: lib_version() -> undefined.
 1429: call_history() -> ?nif_stub.
 1430: hold_nif_mod_priv_data(_Ptr) -> ?nif_stub.
 1431: nif_mod_call_history() -> ?nif_stub.
 1432: list_seq(_To) -> ?nif_stub.
 1433: type_test() -> ?nif_stub.
 1434: tuple_2_list(_) -> ?nif_stub.    
 1435: is_identical(_,_) -> ?nif_stub.
 1436: compare(_,_) -> ?nif_stub.
 1437: many_args_100(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub.
 1438: clone_bin(_) -> ?nif_stub.
 1439: make_sub_bin(_,_,_) -> ?nif_stub.
 1440: string_to_bin(_,_) -> ?nif_stub.
 1441: atom_to_bin(_,_) -> ?nif_stub.    
 1442: macros(_) -> ?nif_stub.
 1443: tuple_2_list_and_tuple(_) -> ?nif_stub.
 1444: iolist_2_bin(_) -> ?nif_stub.
 1445: get_resource_type(_) -> ?nif_stub.
 1446: alloc_resource(_,_) -> ?nif_stub.
 1447: make_resource(_) -> ?nif_stub.
 1448: get_resource(_,_) -> ?nif_stub.
 1449: release_resource(_) -> ?nif_stub.
 1450: last_resource_dtor_call() -> ?nif_stub.
 1451: make_new_resource(_,_) -> ?nif_stub.
 1452: check_is(_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub.
 1453: check_is_exception() -> ?nif_stub.
 1454: length_test(_,_,_,_,_) -> ?nif_stub.
 1455: make_atoms() -> ?nif_stub.
 1456: make_strings() -> ?nif_stub.
 1457: make_new_resource_binary(_) -> ?nif_stub.
 1458: send_list_seq(_,_) -> ?nif_stub.     
 1459: send_new_blob(_,_) -> ?nif_stub.     
 1460: alloc_msgenv() -> ?nif_stub.
 1461: clear_msgenv(_) -> ?nif_stub.
 1462: grow_blob(_,_) -> ?nif_stub.
 1463: grow_blob(_,_,_) -> ?nif_stub.
 1464: send_blob(_,_) -> ?nif_stub.
 1465: send3_blob(_,_,_) -> ?nif_stub.
 1466: send_blob_thread(_,_,_) -> ?nif_stub.
 1467: join_send_thread(_) -> ?nif_stub.
 1468: copy_blob(_) -> ?nif_stub.
 1469: send_term(_,_) -> ?nif_stub.
 1470: reverse_list(_) -> ?nif_stub.
 1471: echo_int(_) -> ?nif_stub.
 1472: type_sizes() -> ?nif_stub.
 1473: otp_9668_nif(_) -> ?nif_stub.
 1474: consume_timeslice_nif(_,_) -> ?nif_stub.
 1475: 
 1476: nif_stub_error(Line) ->
 1477:     exit({nif_not_loaded,module,?MODULE,line,Line}).