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}).