1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1996-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: -module(ets_SUITE). 20: 21: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 22: init_per_group/2,end_per_group/2]). 23: -export([default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1, 24: privacy/1,privacy_owner/2]). 25: -export([empty/1,badinsert/1]). 26: -export([time_lookup/1,badlookup/1,lookup_order/1]). 27: -export([delete_elem/1,delete_tab/1,delete_large_tab/1, 28: delete_large_named_table/1, 29: evil_delete/1,baddelete/1,match_delete/1,table_leak/1]). 30: -export([match_delete3/1]). 31: -export([firstnext/1,firstnext_concurrent/1]). 32: -export([slot/1]). 33: -export([ match1/1, match2/1, match_object/1, match_object2/1]). 34: -export([ dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]). 35: -export([ tab2file/1, tab2file2/1, tabfile_ext1/1, 36: tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1]). 37: -export([ heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]). 38: -export([ lookup_element_mult/1]). 39: -export([]). 40: -export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]). 41: -export([t_delete_object/1, t_init_table/1, t_whitebox/1, 42: t_delete_all_objects/1, t_insert_list/1, t_test_ms/1, 43: t_select_delete/1,t_ets_dets/1]). 44: 45: -export([do_lookup/2, do_lookup_element/3]). 46: 47: -export([ordered/1, ordered_match/1, interface_equality/1, 48: fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1, 49: update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]). 50: -export([member/1]). 51: -export([memory/1]). 52: -export([select_fail/1]). 53: -export([t_insert_new/1]). 54: -export([t_repair_continuation/1]). 55: -export([t_match_spec_run/1]). 56: -export([t_bucket_disappears/1]). 57: -export([otp_5340/1]). 58: -export([otp_6338/1]). 59: -export([otp_6842_select_1000/1]). 60: -export([otp_7665/1]). 61: -export([meta_wb/1]). 62: -export([grow_shrink/1, grow_pseudo_deleted/1, shrink_pseudo_deleted/1]). 63: -export([ 64: meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1, 65: meta_lookup_named_read/1, meta_lookup_named_write/1, 66: meta_newdel_unnamed/1, meta_newdel_named/1]). 67: -export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, 68: otp_8166/1, otp_8732/1]). 69: -export([exit_large_table_owner/1, 70: exit_many_large_table_owner/1, 71: exit_many_tables_owner/1, 72: exit_many_many_tables_owner/1]). 73: -export([write_concurrency/1, heir/1, give_away/1, setopts/1]). 74: -export([bad_table/1, types/1]). 75: -export([otp_9932/1]). 76: -export([otp_9423/1]). 77: -export([otp_10182/1]). 78: -export([memory_check_summary/1]). 79: 80: -export([init_per_testcase/2, end_per_testcase/2]). 81: %% Convenience for manual testing 82: -export([random_test/0]). 83: 84: % internal exports 85: -export([dont_make_worse_sub/0, make_better_sub1/0, make_better_sub2/0]). 86: -export([t_repair_continuation_do/1, t_bucket_disappears_do/1, 87: select_fail_do/1, whitebox_1/1, whitebox_2/1, t_delete_all_objects_do/1, 88: t_delete_object_do/1, t_init_table_do/1, t_insert_list_do/1, 89: update_element_opts/1, update_element_opts/4, update_element/4, update_element_do/4, 90: update_element_neg/1, update_element_neg_do/1, update_counter_do/1, update_counter_neg/1, 91: evil_update_counter_do/1, fixtable_next_do/1, heir_do/1, give_away_do/1, setopts_do/1, 92: rename_do/1, rename_unnamed_do/1, interface_equality_do/1, ordered_match_do/1, 93: ordered_do/1, privacy_do/1, empty_do/1, badinsert_do/1, time_lookup_do/1, 94: lookup_order_do/1, lookup_element_mult_do/1, delete_tab_do/1, delete_elem_do/1, 95: match_delete_do/1, match_delete3_do/1, firstnext_do/1, 96: slot_do/1, match1_do/1, match2_do/1, match_object_do/1, match_object2_do/1, 97: misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1, 98: heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1, 99: do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2, 100: types_do/1, sleeper/0, memory_do/1, 101: ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4 102: ]). 103: 104: -export([t_select_reverse/1]). 105: 106: -include_lib("test_server/include/test_server.hrl"). 107: 108: -define(m(A,B), ?line assert_eq(A,B)). 109: 110: init_per_testcase(Case, Config) -> 111: Seed = {S1,S2,S3} = random:seed0(), %now(), 112: random:seed(S1,S2,S3), 113: io:format("*** SEED: ~p ***\n", [Seed]), 114: start_spawn_logger(), 115: wait_for_test_procs(), %% Ensure previous case cleaned up 116: Dog=test_server:timetrap(test_server:minutes(20)), 117: [{watchdog, Dog}, {test_case, Case} | Config]. 118: 119: end_per_testcase(_Func, Config) -> 120: Dog=?config(watchdog, Config), 121: wait_for_test_procs(true), 122: test_server:timetrap_cancel(Dog). 123: 124: 125: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 126: 127: suite() -> [{ct_hooks,[ts_install_cth]}]. 128: 129: all() -> 130: [{group, new}, {group, insert}, {group, lookup}, 131: {group, delete}, firstnext, firstnext_concurrent, slot, 132: {group, match}, t_match_spec_run, 133: {group, lookup_element}, {group, misc}, {group, files}, 134: {group, heavy}, ordered, ordered_match, 135: interface_equality, fixtable_next, fixtable_insert, 136: rename, rename_unnamed, evil_rename, update_element, 137: update_counter, evil_update_counter, partly_bound, 138: match_heavy, {group, fold}, member, t_delete_object, 139: t_init_table, t_whitebox, t_delete_all_objects, 140: t_insert_list, t_test_ms, t_select_delete, t_ets_dets, 141: memory, t_select_reverse, t_bucket_disappears, 142: select_fail, t_insert_new, t_repair_continuation, 143: otp_5340, otp_6338, otp_6842_select_1000, otp_7665, 144: otp_8732, meta_wb, grow_shrink, grow_pseudo_deleted, 145: shrink_pseudo_deleted, {group, meta_smp}, smp_insert, 146: smp_fixed_delete, smp_unfix_fix, smp_select_delete, 147: otp_8166, exit_large_table_owner, 148: exit_many_large_table_owner, exit_many_tables_owner, 149: exit_many_many_tables_owner, write_concurrency, heir, 150: give_away, setopts, bad_table, types, 151: otp_10182, 152: otp_9932, 153: otp_9423, 154: 155: memory_check_summary]. % MUST BE LAST 156: 157: groups() -> 158: [{new, [], 159: [default, setbag, badnew, verybadnew, named, keypos2, 160: privacy]}, 161: {insert, [], [empty, badinsert]}, 162: {lookup, [], [time_lookup, badlookup, lookup_order]}, 163: {lookup_element, [], [lookup_element_mult]}, 164: {delete, [], 165: [delete_elem, delete_tab, delete_large_tab, 166: delete_large_named_table, evil_delete, table_leak, 167: baddelete, match_delete, match_delete3]}, 168: {match, [], 169: [match1, match2, match_object, match_object2]}, 170: {misc, [], 171: [misc1, safe_fixtable, info, dups, tab2list]}, 172: {files, [], 173: [tab2file, tab2file2, tabfile_ext1, 174: tabfile_ext2, tabfile_ext3, tabfile_ext4]}, 175: {heavy, [], 176: [heavy_lookup, heavy_lookup_element, heavy_concurrent]}, 177: {fold, [], 178: [foldl_ordered, foldr_ordered, foldl, foldr, 179: fold_empty]}, 180: {meta_smp, [], 181: [meta_lookup_unnamed_read, meta_lookup_unnamed_write, 182: meta_lookup_named_read, meta_lookup_named_write, 183: meta_newdel_unnamed, meta_newdel_named]}]. 184: 185: init_per_suite(Config) -> 186: erts_debug:set_internal_state(available_internal_state, true), 187: Config. 188: 189: end_per_suite(_Config) -> 190: stop_spawn_logger(), 191: catch erts_debug:set_internal_state(available_internal_state, false), 192: ok. 193: 194: init_per_group(_GroupName, Config) -> 195: Config. 196: 197: end_per_group(_GroupName, Config) -> 198: Config. 199: 200: %% Test that we did not have "too many" failed verify_etsmem()'s 201: %% in the test suite. 202: %% verify_etsmem() may give a low number of false positives 203: %% as concurrent activities, such as lingering processes 204: %% from earlier test suites, may do unrelated ets (de)allocations. 205: memory_check_summary(_Config) -> 206: case whereis(ets_test_spawn_logger) of 207: undefined -> 208: ?t:fail("No spawn logger exist"); 209: _ -> 210: ets_test_spawn_logger ! {self(), get_failed_memchecks}, 211: receive {get_failed_memchecks, FailedMemchecks} -> ok end, 212: io:format("Failed memchecks: ~p\n",[FailedMemchecks]), 213: if FailedMemchecks > 3 -> 214: ct:fail("Too many failed (~p) memchecks", [FailedMemchecks]); 215: true -> 216: ok 217: end 218: end. 219: 220: 221: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 222: 223: t_bucket_disappears(suite) -> 224: []; 225: t_bucket_disappears(doc) -> 226: ["Test that a disappearing bucket during select of a non-fixed table works."]; 227: t_bucket_disappears(Config) when is_list(Config) -> 228: repeat_for_opts(t_bucket_disappears_do). 229: 230: t_bucket_disappears_do(Opts) -> 231: ?line EtsMem = etsmem(), 232: ?line ets_new(abcd, [named_table, public, {keypos, 2} | Opts]), 233: ?line ets:insert(abcd, {abcd,1,2}), 234: ?line ets:insert(abcd, {abcd,2,2}), 235: ?line ets:insert(abcd, {abcd,3,2}), 236: ?line {_, Cont} = ets:select(abcd, [{{'_', '$1', '_'}, 237: [{'<', '$1', {const, 10}}], 238: ['$1']}], 1), 239: ?line ets:delete(abcd, 2), 240: ?line ets:select(Cont), 241: ?line true = ets:delete(abcd), 242: ?line verify_etsmem(EtsMem). 243: 244: 245: t_match_spec_run(suite) -> 246: []; 247: t_match_spec_run(doc) -> 248: ["Check ets:match_spec_run/2."]; 249: t_match_spec_run(Config) when is_list(Config) -> 250: init_externals(), 251: ?line EtsMem = etsmem(), 252: 253: t_match_spec_run_test([{1},{2},{3}], 254: [{{'$1'},[{'>','$1',1}],['$1']}], 255: [2,3]), 256: 257: ?line Huge = [{X} || X <- lists:seq(1,2500)], 258: ?line L = lists:seq(2476,2500), 259: t_match_spec_run_test(Huge, [{{'$1'},[{'>','$1',2475}],['$1']}], L), 260: 261: ?line L2 = [{X*16#FFFFFFF} || X <- L], 262: t_match_spec_run_test(Huge, 263: [{{'$1'}, [{'>','$1',2475}], [{{{'*','$1',16#FFFFFFF}}}]}], 264: L2), 265: 266: t_match_spec_run_test(Huge, [{{'$1'}, [{'=:=',{'rem','$1',500},0}], ['$1']}], 267: [500,1000,1500,2000,2500]), 268: 269: %% More matching fun with several match clauses and guards, 270: %% applied to a variety of terms. 271: Fun = fun(Term) -> 272: CTerm = {const, Term}, 273: 274: N_List = [{Term, "0", "v-element"}, 275: {"=hidden_node", "0", Term}, 276: {"0", Term, Term}, 277: {"something", Term, "something else"}, 278: {"guard and res", Term, 872346}, 279: {Term, {'and',Term,'again'}, 3.14}, 280: {Term, {'and',Term,'again'}, "m&g"}, 281: {Term, {'and',Term,'again'}, "m&g&r"}, 282: {[{second,Term}, 'and', "tail"], Term, ['and',"tail"]}], 283: 284: N_MS = [{{'$1','$2','$3'}, 285: [{'=:=','$1',CTerm}, {'=:=','$2',{const,"0"}}], 286: [{{"Guard only for $1",'$3'}}]}, 287: 288: {{'$3','$1','$4'}, 289: [{'=:=','$3',"=hidden_node"}, {'=:=','$1',{const,"0"}}], 290: [{{"Result only for $4",'$4'}}]}, 291: 292: {{'$2','$1','$1'}, 293: [{'=:=','$2',{const,"0"}}], 294: [{{"Match only for $1",'$2'}}]}, 295: 296: {{'$2',Term,['$3'|'_']}, 297: [{is_list,'$2'},{'=:=','$3',$s}], 298: [{{"Matching term",'$2'}}]}, 299: 300: {{'$1','$2',872346}, 301: [{'=:=','$2',CTerm}, {is_list,'$1'}], 302: [{{"Guard and result",'$2'}}]}, 303: 304: {{'$1', {'and','$1','again'}, '$2'}, 305: [{is_float,'$2'}], 306: [{{"Match and result",'$1'}}]}, 307: 308: {{'$1', {'and','$1','again'}, '$2'}, 309: [{'=:=','$1',CTerm}, {'=:=', '$2', "m&g"}], 310: [{{"Match and guard",'$2'}}]}, 311: 312: {{'$1', {'and','$1','again'}, "m&g&r"}, 313: [{'=:=','$1',CTerm}], 314: [{{"Match, guard and result",'$1'}}]}, 315: 316: {{'$1', '$2', '$3'}, 317: [{'=:=','$1',[{{second,'$2'}} | '$3']}], 318: [{{"Building guard"}}]} 319: ], 320: 321: N_Result = [{"Guard only for $1", "v-element"}, 322: {"Result only for $4", Term}, 323: {"Match only for $1", "0"}, 324: {"Matching term","something"}, 325: {"Guard and result",Term}, 326: {"Match and result",Term}, 327: {"Match and guard","m&g"}, 328: {"Match, guard and result",Term}, 329: {"Building guard"}], 330: 331: F = fun(N_MS_Perm) -> 332: t_match_spec_run_test(N_List, N_MS_Perm, N_Result) 333: end, 334: repeat_for_permutations(F, N_MS) 335: end, 336: test_terms(Fun, skip_refc_check), 337: 338: ?line verify_etsmem(EtsMem). 339: 340: t_match_spec_run_test(List, MS, Result) -> 341: 342: %%io:format("ms = ~p\n",[MS]), 343: 344: ?m(Result, ets:match_spec_run(List, ets:match_spec_compile(MS))), 345: 346: %% Check that ets:select agree 347: Tab = ets:new(xxx, [bag]), 348: ets:insert(Tab, List), 349: SRes = lists:sort(Result), 350: ?m(SRes, lists:sort(ets:select(Tab, MS))), 351: ets:delete(Tab), 352: 353: %% Check that tracing agree 354: Self = self(), 355: {Tracee, MonRef} = my_spawn_monitor(fun() -> ms_tracee(Self, List) end), 356: receive {Tracee, ready} -> ok end, 357: 358: MST = lists:map(fun(Clause) -> ms_clause_ets_to_trace(Clause) end, MS), 359: 360: %%io:format("MS = ~p\nMST= ~p\n",[MS,MST]), 361: 362: erlang:trace_pattern({?MODULE,ms_tracee_dummy,'_'}, MST , [local]), 363: erlang:trace(Tracee, true, [call]), 364: Tracee ! start, 365: TRes = ms_tracer_collect(Tracee, MonRef, []), 366: %erlang:trace(Tracee, false, [call]), 367: %Tracee ! stop, 368: case TRes of 369: SRes -> ok; 370: _ -> 371: io:format("TRACE MATCH FAILED\n"), 372: io:format("Input = ~p\nMST = ~p\nExpected = ~p\nGot = ~p\n", [List, MST, SRes, TRes]), 373: ?t:fail("TRACE MATCH FAILED") 374: end, 375: ok. 376: 377: 378: 379: ms_tracer_collect(Tracee, Ref, Acc) -> 380: receive 381: {trace, Tracee, call, _Args, [Msg]} -> 382: %io:format("trace Args=~p Msg=~p\n", [_Args, Msg]), 383: ms_tracer_collect(Tracee, Ref, [Msg | Acc]); 384: 385: {'DOWN', Ref, process, Tracee, _} -> 386: %io:format("monitor DOWN for ~p\n", [Tracee]), 387: TDRef = erlang:trace_delivered(Tracee), 388: ms_tracer_collect(Tracee, TDRef, Acc); 389: 390: {trace_delivered, Tracee, Ref} -> 391: %%io:format("trace delivered for ~p\n", [Tracee]), 392: lists:sort(Acc); 393: 394: Other -> 395: io:format("Unexpected message = ~p\n", [Other]), 396: ?t:fail("Unexpected tracer msg") 397: end. 398: 399: 400: ms_tracee(Parent, CallArgList) -> 401: %io:format("ms_tracee ~p started with ArgList = ~p\n", [self(), CallArgList]), 402: Parent ! {self(), ready}, 403: receive start -> ok end, 404: lists:foreach(fun(Args) -> 405: erlang:apply(?MODULE, ms_tracee_dummy, tuple_to_list(Args)) 406: end, CallArgList). 407: %%receive stop -> ok end. 408: 409: 410: 411: ms_tracee_dummy(_) -> ok. 412: ms_tracee_dummy(_,_) -> ok. 413: ms_tracee_dummy(_,_,_) -> ok. 414: ms_tracee_dummy(_,_,_,_) -> ok. 415: 416: ms_clause_ets_to_trace({Head, Guard, Body}) -> 417: {tuple_to_list(Head), Guard, [{message, Body}]}. 418: 419: assert_eq(A,A) -> ok; 420: assert_eq(A,B) -> 421: io:format("FAILED MATCH:\n~p\n =/=\n~p\n",[A,B]), 422: ?t:fail("assert_eq failed"). 423: 424: 425: t_repair_continuation(suite) -> 426: []; 427: t_repair_continuation(doc) -> 428: ["Check ets:repair_continuation/2."]; 429: t_repair_continuation(Config) when is_list(Config) -> 430: repeat_for_opts(t_repair_continuation_do). 431: 432: 433: t_repair_continuation_do(Opts) -> 434: ?line EtsMem = etsmem(), 435: ?line MS = [{'_',[],[true]}], 436: ?line MS2 = [{{{'$1','_'},'_'},[],['$1']}], 437: (fun() -> 438: ?line T = ets_new(x,[ordered_set|Opts]), 439: ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end, 440: ?line F(1000,F), 441: ?line {_,C} = ets:select(T,MS,5), 442: ?line C2 = erlang:setelement(5,C,<<>>), 443: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 444: ?line C3 = ets:repair_continuation(C2,MS), 445: ?line {[true,true,true,true,true],_} = ets:select(C3), 446: ?line {[true,true,true,true,true],_} = ets:select(C), 447: ?line true = ets:delete(T) 448: end)(), 449: (fun() -> 450: ?line T = ets_new(x,[ordered_set|Opts]), 451: ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end, 452: ?line F(1000,F), 453: ?line {_,C} = ets:select(T,MS,1001), 454: ?line C = '$end_of_table', 455: ?line C3 = ets:repair_continuation(C,MS), 456: ?line '$end_of_table' = ets:select(C3), 457: ?line '$end_of_table' = ets:select(C), 458: ?line true = ets:delete(T) 459: end)(), 460: 461: (fun() -> 462: ?line T = ets_new(x,[ordered_set|Opts]), 463: ?line F = fun(0,_)->ok;(N,F) -> 464: ets:insert(T,{integer_to_list(N),N}), 465: F(N-1,F) 466: end, 467: ?line F(1000,F), 468: ?line {_,C} = ets:select(T,MS,5), 469: ?line C2 = erlang:setelement(5,C,<<>>), 470: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 471: ?line C3 = ets:repair_continuation(C2,MS), 472: ?line {[true,true,true,true,true],_} = ets:select(C3), 473: ?line {[true,true,true,true,true],_} = ets:select(C), 474: ?line true = ets:delete(T) 475: end)(), 476: (fun() -> 477: ?line T = ets_new(x,[ordered_set|Opts]), 478: ?line F = fun(0,_)->ok;(N,F) -> 479: ets:insert(T,{{integer_to_list(N),N},N}), 480: F(N-1,F) 481: end, 482: ?line F(1000,F), 483: ?line {_,C} = ets:select(T,MS2,5), 484: ?line C2 = erlang:setelement(5,C,<<>>), 485: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 486: ?line C3 = ets:repair_continuation(C2,MS2), 487: ?line {[_,_,_,_,_],_} = ets:select(C3), 488: ?line {[_,_,_,_,_],_} = ets:select(C), 489: ?line true = ets:delete(T) 490: end)(), 491: 492: (fun() -> 493: ?line T = ets_new(x,[set|Opts]), 494: ?line F = fun(0,_)->ok;(N,F) -> 495: ets:insert(T,{N,N}), 496: F(N-1,F) 497: end, 498: ?line F(1000,F), 499: ?line {_,C} = ets:select(T,MS,5), 500: ?line C2 = erlang:setelement(4,C,<<>>), 501: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 502: ?line C3 = ets:repair_continuation(C2,MS), 503: ?line {[true,true,true,true,true],_} = ets:select(C3), 504: ?line {[true,true,true,true,true],_} = ets:select(C), 505: ?line true = ets:delete(T) 506: end)(), 507: (fun() -> 508: ?line T = ets_new(x,[set|Opts]), 509: ?line F = fun(0,_)->ok;(N,F) -> 510: ets:insert(T,{integer_to_list(N),N}), 511: F(N-1,F) 512: end, 513: ?line F(1000,F), 514: ?line {_,C} = ets:select(T,MS,5), 515: ?line C2 = erlang:setelement(4,C,<<>>), 516: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 517: ?line C3 = ets:repair_continuation(C2,MS), 518: ?line {[true,true,true,true,true],_} = ets:select(C3), 519: ?line {[true,true,true,true,true],_} = ets:select(C), 520: ?line true = ets:delete(T) 521: end)(), 522: (fun() -> 523: ?line T = ets_new(x,[bag|Opts]), 524: ?line F = fun(0,_)->ok;(N,F) -> 525: ets:insert(T,{integer_to_list(N),N}), 526: F(N-1,F) 527: end, 528: ?line F(1000,F), 529: ?line {_,C} = ets:select(T,MS,5), 530: ?line C2 = erlang:setelement(4,C,<<>>), 531: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 532: ?line C3 = ets:repair_continuation(C2,MS), 533: ?line {[true,true,true,true,true],_} = ets:select(C3), 534: ?line {[true,true,true,true,true],_} = ets:select(C), 535: ?line true = ets:delete(T) 536: end)(), 537: (fun() -> 538: ?line T = ets_new(x,[duplicate_bag|Opts]), 539: ?line F = fun(0,_)->ok;(N,F) -> 540: ets:insert(T,{integer_to_list(N),N}), 541: F(N-1,F) 542: end, 543: ?line F(1000,F), 544: ?line {_,C} = ets:select(T,MS,5), 545: ?line C2 = erlang:setelement(4,C,<<>>), 546: ?line {'EXIT',{badarg,_}} = (catch ets:select(C2)), 547: ?line C3 = ets:repair_continuation(C2,MS), 548: ?line {[true,true,true,true,true],_} = ets:select(C3), 549: ?line {[true,true,true,true,true],_} = ets:select(C), 550: ?line true = ets:delete(T) 551: end)(), 552: ?line false = ets:is_compiled_ms(<<>>), 553: ?line true = ets:is_compiled_ms(ets:match_spec_compile(MS)), 554: ?line verify_etsmem(EtsMem). 555: 556: 557: default(doc) -> 558: ["Check correct default vaules of a new ets table"]; 559: default(suite) -> []; 560: default(Config) when is_list(Config) -> 561: %% Default should be set,protected 562: ?line EtsMem = etsmem(), 563: ?line Def = ets_new(def,[]), 564: ?line set = ets:info(Def,type), 565: ?line protected = ets:info(Def,protection), 566: Compressed = erlang:system_info(ets_always_compress), 567: ?line Compressed = ets:info(Def,compressed), 568: Self = self(), 569: ?line Self = ets:info(Def,owner), 570: ?line none = ets:info(Def, heir), 571: ?line false = ets:info(Def,named_table), 572: ?line ets:delete(Def), 573: ?line verify_etsmem(EtsMem). 574: 575: select_fail(doc) -> 576: ["Test that select fails even if nothing can match"]; 577: select_fail(suite) -> 578: []; 579: select_fail(Config) when is_list(Config) -> 580: ?line EtsMem = etsmem(), 581: repeat_for_opts(select_fail_do, [all_types,write_concurrency]), 582: ?line verify_etsmem(EtsMem). 583: 584: select_fail_do(Opts) -> 585: ?line T = ets_new(x,Opts), 586: ?line ets:insert(T,{a,a}), 587: ?line case (catch 588: ets:select(T,[{{a,'_'},[],[{snuffla}]}])) of 589: {'EXIT',{badarg,_}} -> 590: ok; 591: Else0 -> 592: exit({type,ets:info(T,type), 593: expected,'EXIT',got,Else0}) 594: end, 595: ?line case (catch 596: ets:select(T,[{{b,'_'},[],[{snuffla}]}])) of 597: {'EXIT',{badarg,_}} -> 598: ok; 599: Else1 -> 600: exit({type,ets:info(T,type), 601: expected,'EXIT',got,Else1}) 602: end, 603: ?line ets:delete(T). 604: 605: 606: -define(S(T),ets:info(T,memory)). 607: -define(TAB_STRUCT_SZ, erts_debug:get_internal_state('DbTable_words')). 608: %%-define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap 609: %% 610: %% The hardcoded expected memory sizes (in words) are the ones we expect on: 611: %% SunOS5.8, 32-bit, non smp, private heap 612: %% 613: memory(doc) -> ["Whitebox test of ets:info(X,memory)"]; 614: memory(suite) -> []; 615: memory(Config) when is_list(Config) -> 616: ?line ok = chk_normal_tab_struct_size(), 617: repeat_for_opts(memory_do,[compressed]), 618: ?line catch erts_debug:set_internal_state(available_internal_state, false). 619: 620: memory_do(Opts) -> 621: ?line L = [T1,T2,T3,T4] = fill_sets_int(1000,Opts), 622: XR1 = case mem_mode(T1) of 623: {normal,_} -> {13836,13046,13046,13052}; %{13862,13072,13072,13078}; 624: {compressed,4} -> {11041,10251,10251,10252}; %{11067,10277,10277,10278}; 625: {compressed,8} -> {10050,9260,9260,9260} %{10076,9286,9286,9286} 626: end, 627: ?line XRes1 = adjust_xmem(L, XR1), 628: ?line Res1 = {?S(T1),?S(T2),?S(T3),?S(T4)}, 629: ?line lists:foreach(fun(T) -> 630: Before = ets:info(T,size), 631: Key = 2, %894, %%ets:first(T), 632: Objs = ets:lookup(T,Key), 633: ?line ets:delete(T,Key), 634: io:format("deleted key ~p from ~p changed size ~p to ~p: ~p\n", 635: [Key, ets:info(T,type), Before, ets:info(T,size), Objs]) 636: end, 637: L), 638: XR2 = case mem_mode(T1) of 639: {normal,_} -> {13826,13037,13028,13034}; %{13852,13063,13054,13060}; 640: {compressed,4} -> {11031,10242,10233,10234}; %{11057,10268,10259,10260}; 641: {compressed,8} -> {10040,9251,9242,9242} %10066,9277,9268,9268} 642: end, 643: ?line XRes2 = adjust_xmem(L, XR2), 644: ?line Res2 = {?S(T1),?S(T2),?S(T3),?S(T4)}, 645: ?line lists:foreach(fun(T) -> 646: Before = ets:info(T,size), 647: Key = 4, %802, %ets:first(T), 648: Objs = ets:lookup(T,Key), 649: ?line ets:match_delete(T,{Key,'_'}), 650: io:format("match_deleted key ~p from ~p changed size ~p to ~p: ~p\n", 651: [Key, ets:info(T,type), Before, ets:info(T,size), Objs]) 652: end, 653: L), 654: XR3 = case mem_mode(T1) of 655: {normal,_} -> {13816,13028,13010,13016}; %{13842,13054,13036,13042}; 656: {compressed,4} -> {11021,10233,10215,10216}; %{11047,10259,10241,10242}; 657: {compressed,8} -> {10030,9242,9224,9224} %{10056,9268,9250,9250} 658: end, 659: ?line XRes3 = adjust_xmem(L, XR3), 660: ?line Res3 = {?S(T1),?S(T2),?S(T3),?S(T4)}, 661: ?line lists:foreach(fun(T) -> 662: ?line ets:delete_all_objects(T) 663: end, 664: L), 665: ?line XRes4 = adjust_xmem(L, {50,260,260,260}), %{76,286,286,286}), 666: ?line Res4 = {?S(T1),?S(T2),?S(T3),?S(T4)}, 667: lists:foreach(fun(T) -> 668: ?line ets:delete(T) 669: end, 670: L), 671: ?line L2 = [T11,T12,T13,T14] = fill_sets_int(1000), 672: ?line lists:foreach(fun(T) -> 673: ?line ets:select_delete(T,[{'_',[],[true]}]) 674: end, 675: L2), 676: ?line XRes5 = adjust_xmem(L2, {50,260,260,260}), %{76,286,286,286}), 677: ?line Res5 = {?S(T11),?S(T12),?S(T13),?S(T14)}, 678: ?line io:format("XRes1 = ~p~n" 679: " Res1 = ~p~n~n" 680: "XRes2 = ~p~n" 681: " Res2 = ~p~n~n" 682: "XRes3 = ~p~n" 683: " Res3 = ~p~n~n" 684: "XRes4 = ~p~n" 685: " Res4 = ~p~n~n" 686: "XRes5 = ~p~n" 687: " Res5 = ~p~n~n", 688: [XRes1, Res1, 689: XRes2, Res2, 690: XRes3, Res3, 691: XRes4, Res4, 692: XRes5, Res5]), 693: ?line XRes1 = Res1, 694: ?line XRes2 = Res2, 695: ?line XRes3 = Res3, 696: ?line XRes4 = Res4, 697: ?line XRes5 = Res5, 698: ?line ok. 699: 700: mem_mode(T) -> 701: {case ets:info(T,compressed) of 702: true -> compressed; 703: false -> normal 704: end, 705: erlang:system_info(wordsize)}. 706: 707: chk_normal_tab_struct_size() -> 708: ?line System = {os:type(), 709: os:version(), 710: erlang:system_info(wordsize), 711: erlang:system_info(smp_support), 712: erlang:system_info(heap_type)}, 713: ?line ?t:format("System = ~p~n", [System]), 714: %%?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]), 715: ?line ?t:format("?TAB_STRUCT_SZ=~p~n", [?TAB_STRUCT_SZ]), 716: ok. 717: % ?line case System of 718: % {{unix, sunos}, {5, 8, 0}, 4, false, private} -> 719: % ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ, 720: % ?line ok; 721: % _ -> 722: % ?line ok 723: % end. 724: 725: -define(DB_TREE_STACK_NEED,50). % The static stack for a tree, in halfword pointers are two internal words 726: % so the stack gets twice as big 727: -define(DB_HASH_SIZEOF_EXTSEG,260). % The segment size in words, in halfword this will be twice as large. 728: 729: adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = _Mem0) -> 730: %% Adjust for 64-bit, smp, and os: 731: %% Table struct size may differ. 732: 733: % Mem1 = case ?TAB_STRUCT_SZ of 734: % ?NORMAL_TAB_STRUCT_SZ -> 735: % Mem0; 736: % TabStructSz -> 737: % TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ, 738: % {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff} 739: % end, 740: 741: TabDiff = ?TAB_STRUCT_SZ, 742: Mem1 = {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}, 743: 744: case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of 745: %% Halfword, corrections for regular pointers occupying two internal words. 746: {4,8} -> 747: {A1,B1,C1,D1} = Mem1, 748: {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED, 749: B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG, 750: C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG, 751: D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG}; 752: _ -> 753: Mem1 754: end. 755: 756: t_whitebox(doc) -> 757: ["Diverse whitebox testes"]; 758: t_whitebox(suite) -> 759: []; 760: t_whitebox(Config) when is_list(Config) -> 761: ?line EtsMem = etsmem(), 762: repeat_for_opts(whitebox_1), 763: repeat_for_opts(whitebox_1), 764: repeat_for_opts(whitebox_1), 765: repeat_for_opts(whitebox_2), 766: repeat_for_opts(whitebox_2), 767: repeat_for_opts(whitebox_2), 768: ?line verify_etsmem(EtsMem). 769: 770: whitebox_1(Opts) -> 771: ?line T=ets_new(x,[bag | Opts]), 772: ?line ets:insert(T,[{du,glade},{ta,en}]), 773: ?line ets:insert(T,[{hej,hopp2},{du,glade2},{ta,en2}]), 774: ?line {_,C}=ets:match(T,{ta,'$1'},1), 775: ?line ets:select(C), 776: ?line ets:match(C), 777: ?line ets:delete(T), 778: ok. 779: 780: whitebox_2(Opts) -> 781: ?line T=ets_new(x,[ordered_set, {keypos,2} | Opts]), 782: ?line T2=ets_new(x,[set, {keypos,2}| Opts]), 783: ?line 0 = ets:select_delete(T,[{{hej},[],[true]}]), 784: ?line 0 = ets:select_delete(T,[{{hej,hopp},[],[true]}]), 785: ?line 0 = ets:select_delete(T2,[{{hej},[],[true]}]), 786: ?line 0 = ets:select_delete(T2,[{{hej,hopp},[],[true]}]), 787: ?line ets:delete(T), 788: ?line ets:delete(T2), 789: ok. 790: 791: 792: t_ets_dets(doc) -> 793: ["Test ets:to/from_dets"]; 794: t_ets_dets(suite) -> 795: []; 796: t_ets_dets(Config) when is_list(Config) -> 797: repeat_for_opts(fun(Opts) -> t_ets_dets(Config,Opts) end). 798: 799: t_ets_dets(Config, Opts) -> 800: ?line Fname = gen_dets_filename(Config,1), 801: ?line (catch file:delete(Fname)), 802: ?line {ok,DTab} = dets:open_file(testdets_1, 803: [{file, Fname}]), 804: ?line ETab = ets_new(x,Opts), 805: ?line filltabint(ETab,3000), 806: ?line DTab = ets:to_dets(ETab,DTab), 807: ?line ets:delete_all_objects(ETab), 808: ?line 0 = ets:info(ETab,size), 809: ?line true = ets:from_dets(ETab,DTab), 810: ?line 3000 = ets:info(ETab,size), 811: ?line ets:delete(ETab), 812: ?line check_badarg(catch ets:to_dets(ETab,DTab), 813: ets, to_dets, [ETab,DTab]), 814: ?line check_badarg(catch ets:from_dets(ETab,DTab), 815: ets, from_dets, [ETab,DTab]), 816: ?line ETab2 = ets_new(x,Opts), 817: ?line filltabint(ETab2,3000), 818: ?line dets:close(DTab), 819: ?line check_badarg(catch ets:to_dets(ETab2,DTab), 820: ets, to_dets, [ETab2,DTab]), 821: ?line check_badarg(catch ets:from_dets(ETab2,DTab), 822: ets, from_dets, [ETab2,DTab]), 823: ?line ets:delete(ETab2), 824: ?line (catch file:delete(Fname)), 825: ok. 826: 827: check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) -> 828: true; 829: check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) -> 830: true = test_server:is_native(M) andalso length(Args) =:= A. 831: 832: t_delete_all_objects(doc) -> 833: ["Test ets:delete_all_objects/1"]; 834: t_delete_all_objects(suite) -> 835: []; 836: t_delete_all_objects(Config) when is_list(Config) -> 837: ?line EtsMem = etsmem(), 838: repeat_for_opts(t_delete_all_objects_do), 839: ?line verify_etsmem(EtsMem). 840: 841: get_kept_objects(T) -> 842: case ets:info(T,stats) of 843: false -> 844: 0; 845: {_,_,_,_,_,_,KO} -> 846: KO 847: end. 848: 849: t_delete_all_objects_do(Opts) -> 850: ?line T=ets_new(x,Opts), 851: ?line filltabint(T,4000), 852: ?line O=ets:first(T), 853: ?line ets:next(T,O), 854: ?line ets:safe_fixtable(T,true), 855: ?line true = ets:delete_all_objects(T), 856: ?line '$end_of_table' = ets:next(T,O), 857: ?line 0 = ets:info(T,size), 858: ?line 4000 = get_kept_objects(T), 859: ?line ets:safe_fixtable(T,false), 860: ?line 0 = ets:info(T,size), 861: ?line 0 = get_kept_objects(T), 862: ?line filltabint(T,4000), 863: ?line 4000 = ets:info(T,size), 864: ?line true = ets:delete_all_objects(T), 865: ?line 0 = ets:info(T,size), 866: ?line ets:delete(T). 867: 868: 869: t_delete_object(doc) -> 870: ["Test ets:delete_object/2"]; 871: t_delete_object(suite) -> 872: []; 873: t_delete_object(Config) when is_list(Config) -> 874: ?line EtsMem = etsmem(), 875: repeat_for_opts(t_delete_object_do), 876: ?line verify_etsmem(EtsMem). 877: 878: t_delete_object_do(Opts) -> 879: ?line T = ets_new(x,Opts), 880: ?line filltabint(T,4000), 881: ?line del_one_by_one_set(T,1,4001), 882: ?line filltabint(T,4000), 883: ?line del_one_by_one_set(T,4000,0), 884: ?line filltabint(T,4000), 885: ?line First = ets:first(T), 886: ?line Next = ets:next(T,First), 887: ?line ets:safe_fixtable(T,true), 888: ?line ets:delete_object(T,{First, integer_to_list(First)}), 889: ?line Next = ets:next(T,First), 890: ?line 3999 = ets:info(T,size), 891: ?line 1 = get_kept_objects(T), 892: ?line ets:safe_fixtable(T,false), 893: ?line 3999 = ets:info(T,size), 894: ?line 0 = get_kept_objects(T), 895: ?line ets:delete(T), 896: ?line T1 = ets_new(x,[ordered_set | Opts]), 897: ?line filltabint(T1,4000), 898: ?line del_one_by_one_set(T1,1,4001), 899: ?line filltabint(T1,4000), 900: ?line del_one_by_one_set(T1,4000,0), 901: ?line ets:delete(T1), 902: ?line T2 = ets_new(x,[bag | Opts]), 903: ?line filltabint2(T2,4000), 904: ?line del_one_by_one_bag(T2,1,4001), 905: ?line filltabint2(T2,4000), 906: ?line del_one_by_one_bag(T2,4000,0), 907: ?line ets:delete(T2), 908: ?line T3 = ets_new(x,[duplicate_bag | Opts]), 909: ?line filltabint3(T3,4000), 910: ?line del_one_by_one_dbag_1(T3,1,4001), 911: ?line filltabint3(T3,4000), 912: ?line del_one_by_one_dbag_1(T3,4000,0), 913: ?line filltabint(T3,4000), 914: ?line filltabint3(T3,4000), 915: ?line del_one_by_one_dbag_2(T3,1,4001), 916: ?line filltabint(T3,4000), 917: ?line filltabint3(T3,4000), 918: ?line del_one_by_one_dbag_2(T3,4000,0), 919: 920: ?line filltabint2(T3,4000), 921: ?line filltabint(T3,4000), 922: ?line del_one_by_one_dbag_3(T3,4000,0), 923: ?line ets:delete(T3), 924: ok. 925: 926: make_init_fun(N) when N > 4000-> 927: fun(read) -> 928: end_of_input; 929: (close) -> 930: exit(close_not_expected) 931: end; 932: make_init_fun(N) -> 933: fun(read) -> 934: case N rem 2 of 935: 0 -> 936: {[{N, integer_to_list(N)}, {N, integer_to_list(N)}], 937: make_init_fun(N + 1)}; 938: 1 -> 939: {[], make_init_fun(N + 1)} 940: end; 941: (close) -> 942: exit(close_not_expected) 943: end. 944: 945: t_init_table(doc) -> 946: ["Test ets:init_table/2"]; 947: t_init_table(suite) -> 948: []; 949: t_init_table(Config) when is_list(Config)-> 950: ?line EtsMem = etsmem(), 951: repeat_for_opts(t_init_table_do), 952: ?line verify_etsmem(EtsMem). 953: 954: t_init_table_do(Opts) -> 955: ?line T = ets_new(x,[duplicate_bag | Opts]), 956: ?line filltabint(T,4000), 957: ?line ets:init_table(T, make_init_fun(1)), 958: ?line del_one_by_one_dbag_1(T,4000,0), 959: ?line ets:delete(T), 960: ok. 961: 962: do_fill_dbag_using_lists(T,0) -> 963: T; 964: do_fill_dbag_using_lists(T,N) -> 965: ets:insert(T,[{N,integer_to_list(N)}, 966: {N + N rem 2,integer_to_list(N + N rem 2)}]), 967: do_fill_dbag_using_lists(T,N - 1). 968: 969: 970: t_insert_new(doc) -> 971: ["Test the insert_new function"]; 972: t_insert_new(suite) -> 973: []; 974: t_insert_new(Config) when is_list(Config) -> 975: ?line EtsMem = etsmem(), 976: ?line L = fill_sets_int(1000) ++ fill_sets_int(1000,[{write_concurrency,true}]), 977: lists:foreach(fun(Tab) -> 978: ?line false = ets:insert_new(Tab,{2,"2"}), 979: ?line true = ets:insert_new(Tab,{2002,"2002"}), 980: ?line false = ets:insert_new(Tab,{2002,"2002"}), 981: ?line true = ets:insert(Tab,{2002,"2002"}), 982: ?line false = ets:insert_new(Tab,[{2002,"2002"}]), 983: ?line false = ets:insert_new(Tab,[{2002,"2002"}, 984: {2003,"2003"}]), 985: ?line false = ets:insert_new(Tab,[{2001,"2001"}, 986: {2002,"2002"}, 987: {2003,"2003"}]), 988: ?line false = ets:insert_new(Tab,[{2001,"2001"}, 989: {2002,"2002"}]), 990: ?line true = ets:insert_new(Tab,[{2001,"2001"}, 991: {2003,"2003"}]), 992: ?line false = ets:insert_new(Tab,{2001,"2001"}), 993: ?line false = ets:insert_new(Tab,{2002,"2002"}), 994: ?line false = ets:insert_new(Tab,{2003,"2003"}), 995: ?line true = ets:insert_new(Tab,{2004,"2004"}), 996: ?line true = ets:insert_new(Tab,{2000,"2000"}), 997: ?line true = ets:insert_new(Tab,[{2005,"2005"}, 998: {2006,"2006"}, 999: {2007,"2007"}]), 1000: ?line Num = 1001: case ets:info(Tab,type) of 1002: bag -> 1003: ?line true = 1004: ets:insert(Tab,{2004,"2004-2"}), 1005: ?line false = 1006: ets:insert_new(Tab,{2004,"2004-3"}), 1007: 1009; 1008: duplicate_bag -> 1009: ?line true = 1010: ets:insert(Tab,{2004,"2004"}), 1011: ?line false = 1012: ets:insert_new(Tab,{2004,"2004"}), 1013: 1010; 1014: _ -> 1015: 1008 1016: end, 1017: ?line Num = ets:info(Tab,size), 1018: ?line List = ets:tab2list(Tab), 1019: ?line ets:delete_all_objects(Tab), 1020: ?line true = ets:insert_new(Tab,List), 1021: ?line false = ets:insert_new(Tab,List), 1022: ?line ets:delete(Tab) 1023: end, 1024: L), 1025: ?line verify_etsmem(EtsMem). 1026: 1027: t_insert_list(doc) -> 1028: ["Test ets:insert/2 with list of objects."]; 1029: t_insert_list(suite) -> 1030: []; 1031: t_insert_list(Config) when is_list(Config) -> 1032: ?line EtsMem = etsmem(), 1033: repeat_for_opts(t_insert_list_do), 1034: ?line verify_etsmem(EtsMem). 1035: 1036: t_insert_list_do(Opts) -> 1037: ?line T = ets_new(x,[duplicate_bag | Opts]), 1038: ?line do_fill_dbag_using_lists(T,4000), 1039: ?line del_one_by_one_dbag_2(T,4000,0), 1040: ?line ets:delete(T). 1041: 1042: 1043: t_test_ms(doc) -> 1044: ["Test interface of ets:test_ms/2"]; 1045: t_test_ms(suite) -> 1046: []; 1047: t_test_ms(Config) when is_list(Config) -> 1048: ?line EtsMem = etsmem(), 1049: ?line {ok,[a,b]} = ets:test_ms({a,b}, 1050: [{{'$1','$2'},[{'<','$1','$2'}],['$$']}]), 1051: ?line {ok,false} = ets:test_ms({a,b}, 1052: [{{'$1','$2'},[{'>','$1','$2'}],['$$']}]), 1053: Tpl = {a,gb_sets:new()}, 1054: ?line {ok,Tpl} = ets:test_ms(Tpl, [{{'_','_'}, [], ['$_']}]), % OTP-10190 1055: ?line {error,[{error,String}]} = ets:test_ms({a,b}, 1056: [{{'$1','$2'}, 1057: [{'flurp','$1','$2'}], 1058: ['$$']}]), 1059: ?line true = (if is_list(String) -> true; true -> false end), 1060: ?line verify_etsmem(EtsMem). 1061: 1062: t_select_reverse(doc) -> 1063: ["Test the select reverse BIF's"]; 1064: t_select_reverse(suite) -> 1065: []; 1066: t_select_reverse(Config) when is_list(Config) -> 1067: ?line Table = ets_new(xxx, [ordered_set]), 1068: ?line filltabint(Table,1000), 1069: ?line A = lists:reverse(ets:select(Table,[{{'$1', '_'}, 1070: [{'>', 1071: {'rem', 1072: '$1', 5}, 1073: 2}], 1074: ['$_']}])), 1075: ?line A = ets:select_reverse(Table,[{{'$1', '_'}, 1076: [{'>', 1077: {'rem', 1078: '$1', 5}, 1079: 2}], 1080: ['$_']}]), 1081: ?line A = reverse_chunked(Table,[{{'$1', '_'}, 1082: [{'>', 1083: {'rem', 1084: '$1', 5}, 1085: 2}], 1086: ['$_']}],3), 1087: % A set/bag/duplicate_bag should get the same result regardless 1088: % of select or select_reverse 1089: ?line Table2 = ets_new(xxx, [set]), 1090: ?line filltabint(Table2,1000), 1091: ?line Table3 = ets_new(xxx, [bag]), 1092: ?line filltabint(Table3,1000), 1093: ?line Table4 = ets_new(xxx, [duplicate_bag]), 1094: ?line filltabint(Table4,1000), 1095: ?line lists:map(fun(Tab) -> 1096: B = ets:select(Tab,[{{'$1', '_'}, 1097: [{'>', 1098: {'rem', 1099: '$1', 5}, 1100: 2}], 1101: ['$_']}]), 1102: B = ets:select_reverse(Tab,[{{'$1', '_'}, 1103: [{'>', 1104: {'rem', 1105: '$1', 5}, 1106: 2}], 1107: ['$_']}]) 1108: end,[Table2, Table3, Table4]), 1109: ok. 1110: 1111: 1112: 1113: reverse_chunked(T,MS,N) -> 1114: do_reverse_chunked(ets:select_reverse(T,MS,N),[]). 1115: 1116: do_reverse_chunked('$end_of_table',Acc) -> 1117: lists:reverse(Acc); 1118: do_reverse_chunked({L,C},Acc) -> 1119: NewAcc = lists:reverse(L)++Acc, 1120: do_reverse_chunked(ets:select_reverse(C), NewAcc). 1121: 1122: 1123: t_select_delete(doc) -> 1124: ["Test the ets:select_delete/2 and ets:select_count/2 BIF's"]; 1125: t_select_delete(suite) -> 1126: []; 1127: t_select_delete(Config) when is_list(Config) -> 1128: ?line EtsMem = etsmem(), 1129: ?line Tables = fill_sets_int(10000) ++ fill_sets_int(10000,[{write_concurrency,true}]), 1130: lists:foreach 1131: (fun(Table) -> 1132: ?line 4000 = ets:select_count(Table,[{{'$1', '_'}, 1133: [{'>', 1134: {'rem', 1135: '$1', 5}, 1136: 2}], 1137: [true]}]), 1138: ?line 4000 = ets:select_delete(Table,[{{'$1', '_'}, 1139: [{'>', 1140: {'rem', 1141: '$1', 5}, 1142: 2}], 1143: [true]}]), 1144: ?line check(Table, 1145: fun({N,_}) when (N rem 5) =< 2 -> 1146: true; 1147: (_) -> 1148: false 1149: end, 1150: 6000) 1151: 1152: end, 1153: Tables), 1154: lists:foreach 1155: (fun(Table) -> 1156: ?line ets:select_delete(Table,[{'_',[],[true]}]), 1157: ?line xfilltabint(Table,4000), 1158: ?line successive_delete(Table,1,4001,bound), 1159: ?line 0 = ets:info(Table,size), 1160: ?line xfilltabint(Table,4000), 1161: ?line successive_delete(Table,4000,0, bound), 1162: ?line 0 = ets:info(Table,size), 1163: ?line xfilltabint(Table,4000), 1164: ?line successive_delete(Table,1,4001,unbound), 1165: ?line 0 = ets:info(Table,size), 1166: ?line xfilltabint(Table,4000), 1167: ?line successive_delete(Table,4000,0, unbound), 1168: ?line 0 = ets:info(Table,size) 1169: 1170: end, 1171: Tables), 1172: lists:foreach 1173: (fun(Table) -> 1174: F = case ets:info(Table,type) of 1175: X when X == bag; X == duplicate_bag -> 1176: 2; 1177: _ -> 1178: 1 1179: end, 1180: ?line xfilltabstr(Table, 4000), 1181: ?line 1000 = ets:select_count(Table, 1182: [{{[$3 | '$1'], '_'}, 1183: [{'==', 1184: {'length', '$1'}, 1185: 3}],[true]}]) div F, 1186: ?line 1000 = ets:select_delete(Table, 1187: [{{[$3 | '$1'], '_'}, 1188: [{'==', 1189: {'length', '$1'}, 1190: 3}],[true]}]) div F, 1191: ?line check(Table, fun({[3,_,_,_],_}) -> false; 1192: (_) -> true 1193: end, 3000*F), 1194: ?line 8 = ets:select_count(Table, 1195: [{{"7",'_'},[],[false]}, 1196: {{['_'], '_'}, 1197: [],[true]}]) div F, 1198: ?line 8 = ets:select_delete(Table, 1199: [{{"7",'_'},[],[false]}, 1200: {{['_'], '_'}, 1201: [],[true]}]) div F, 1202: ?line check(Table, fun({"7",_}) -> true; 1203: ({[_],_}) -> false; 1204: (_) -> true 1205: end, 2992*F), 1206: ?line xfilltabstr(Table, 4000), 1207: %% This happens to be interesting for other select types too 1208: ?line 200 = length(ets:select(Table, 1209: [{{[$3,'_','_'],'_'}, 1210: [],[true]}, 1211: {{[$1,'_','_'],'_'}, 1212: [],[true]}])) div F, 1213: ?line 200 = ets:select_count(Table, 1214: [{{[$3,'_','_'],'_'}, 1215: [],[true]}, 1216: {{[$1,'_','_'],'_'}, 1217: [],[true]}]) div F, 1218: ?line 200 = length(element(1,ets:select(Table, 1219: [{{[$3,'_','_'],'_'}, 1220: [],[true]}, 1221: {{[$1,'_','_'],'_'}, 1222: [],[true]}], 1223: 1000))) div F, 1224: ?line 200 = length( 1225: ets:select_reverse(Table, 1226: [{{[$3,'_','_'],'_'}, 1227: [],[true]}, 1228: {{[$1,'_','_'],'_'}, 1229: [],[true]}])) div F, 1230: ?line 200 = length( 1231: element(1, 1232: ets:select_reverse 1233: (Table, 1234: [{{[$3,'_','_'],'_'}, 1235: [],[true]}, 1236: {{[$1,'_','_'],'_'}, 1237: [],[true]}], 1238: 1000))) div F, 1239: ?line 200 = ets:select_delete(Table, 1240: [{{[$3,'_','_'],'_'}, 1241: [],[true]}, 1242: {{[$1,'_','_'],'_'}, 1243: [],[true]}]) div F, 1244: ?line 0 = ets:select_count(Table, 1245: [{{[$3,'_','_'],'_'}, 1246: [],[true]}, 1247: {{[$1,'_','_'],'_'}, 1248: [],[true]}]) div F, 1249: ?line check(Table, fun({[$3,_,_],_}) -> false; 1250: ({[$1,_,_],_}) -> false; 1251: (_) -> true 1252: end, 3800*F) 1253: end, 1254: Tables), 1255: lists:foreach(fun(Tab) -> ets:delete(Tab) end,Tables), 1256: ?line verify_etsmem(EtsMem). 1257: 1258: partly_bound(doc) -> 1259: ["Test that partly bound keys gives faster matches"]; 1260: partly_bound(suite) -> 1261: []; 1262: partly_bound(Config) when is_list(Config) -> 1263: case os:type() of 1264: {win32,_} -> 1265: {skip,"Inaccurate measurements on Windows"}; 1266: _ -> 1267: ?line EtsMem = etsmem(), 1268: ?line dont_make_worse(), 1269: ?line make_better(), 1270: ?line verify_etsmem(EtsMem) 1271: end. 1272: 1273: dont_make_worse() -> 1274: seventyfive_percent_success({?MODULE,dont_make_worse_sub,[]},0,0,10). 1275: 1276: dont_make_worse_sub() -> 1277: ?line T = build_table([a,b],[a,b],15000), 1278: ?line T1 = time_match_object(T,{'_',a,a,1500}, [{{a,a,1500},a,a,1500}]), 1279: ?line T2 = time_match_object(T,{{a,a,'_'},a,a,1500}, 1280: [{{a,a,1500},a,a,1500}]), 1281: ?line ets:delete(T), 1282: ?line true = (T1 > T2), 1283: ok. 1284: 1285: make_better() -> 1286: fifty_percent_success({?MODULE,make_better_sub2,[]},0,0,10), 1287: fifty_percent_success({?MODULE,make_better_sub1,[]},0,0,10). 1288: make_better_sub1() -> 1289: ?line T = build_table2([a,b],[a,b],15000), 1290: ?line T1 = time_match_object(T,{'_',1500,a,a}, [{{1500,a,a},1500,a,a}]), 1291: ?line T2 = time_match_object(T,{{1500,a,'_'},1500,a,a}, 1292: [{{1500,a,a},1500,a,a}]), 1293: ?line ets:delete(T), 1294: ?line io:format("~p>~p~n",[(T1 / 100),T2]), 1295: ?line true = ((T1 / 100) > T2), % More marginal than needed. 1296: ok. 1297: 1298: make_better_sub2() -> 1299: ?line T = build_table2([a,b],[a,b],15000), 1300: ?line T1 = time_match(T,{'$1',1500,a,a}), 1301: ?line T2 = time_match(T,{{1500,a,'$1'},1500,a,a}), 1302: ?line ets:delete(T), 1303: ?line io:format("~p>~p~n",[(T1 / 100),T2]), 1304: ?line true = ((T1 / 100) > T2), % More marginal than needed. 1305: ok. 1306: 1307: 1308: match_heavy(doc) -> 1309: ["Heavy random matching, comparing set with ordered_set."]; 1310: match_heavy(suite) -> 1311: []; 1312: match_heavy(Config) when is_list(Config) -> 1313: PrivDir = ?config(priv_dir,Config), 1314: DataDir = ?config(data_dir, Config), 1315: %% Easier to have in process dictionary when manually 1316: %% running the test function. 1317: put(where_to_read,DataDir), 1318: put(where_to_write,PrivDir), 1319: Dog=?config(watchdog, Config), 1320: test_server:timetrap_cancel(Dog), 1321: NewDog=test_server:timetrap(test_server:seconds(1000)), 1322: NewConfig = [{watchdog, NewDog} | lists:keydelete(watchdog,1,Config)], 1323: random_test(), 1324: drop_match(), 1325: NewConfig. 1326: 1327: %%% Extra safety for the very low probability that this is not 1328: %%% caught by the random test (Statistically impossible???) 1329: drop_match() -> 1330: ?line EtsMem = etsmem(), 1331: ?line T = build_table([a,b],[a],1500), 1332: ?line [{{a,a,1},a,a,1},{{b,a,1},b,a,1}] = 1333: ets:match_object(T, {'_','_','_',1}), 1334: ?line true = ets:delete(T), 1335: ?line verify_etsmem(EtsMem). 1336: 1337: 1338: 1339: ets_match(Tab,Expr) -> 1340: case random:uniform(2) of 1341: 1 -> 1342: ets:match(Tab,Expr); 1343: _ -> 1344: match_chunked(Tab,Expr) 1345: end. 1346: 1347: match_chunked(Tab,Expr) -> 1348: match_chunked_collect(ets:match(Tab,Expr, 1349: random:uniform(1999) + 1)). 1350: match_chunked_collect('$end_of_table') -> 1351: []; 1352: match_chunked_collect({Results, Continuation}) -> 1353: Results ++ match_chunked_collect(ets:match(Continuation)). 1354: 1355: ets_match_object(Tab,Expr) -> 1356: case random:uniform(2) of 1357: 1 -> 1358: ets:match_object(Tab,Expr); 1359: _ -> 1360: match_object_chunked(Tab,Expr) 1361: end. 1362: 1363: match_object_chunked(Tab,Expr) -> 1364: match_object_chunked_collect(ets:match_object(Tab,Expr, 1365: random:uniform(1999) + 1)). 1366: match_object_chunked_collect('$end_of_table') -> 1367: []; 1368: match_object_chunked_collect({Results, Continuation}) -> 1369: Results ++ match_object_chunked_collect(ets:match_object(Continuation)). 1370: 1371: 1372: 1373: random_test() -> 1374: ?line ReadDir = get(where_to_read), 1375: ?line WriteDir = get(where_to_write), 1376: ?line (catch file:make_dir(WriteDir)), 1377: ?line Seed = case file:consult(filename:join([ReadDir, 1378: "preset_random_seed.txt"])) of 1379: {ok,[X]} -> 1380: X; 1381: _ -> 1382: {A,B,C} = erlang:now(), 1383: random:seed(A,B,C), 1384: get(random_seed) 1385: end, 1386: put(random_seed,Seed), 1387: ?line {ok, F} = file:open(filename:join([WriteDir, 1388: "last_random_seed.txt"]), 1389: [write]), 1390: io:format(F,"~p. ~n",[Seed]), 1391: file:close(F), 1392: io:format("Random seed ~p written to ~s, copy to ~s to rerun with " 1393: "same seed.",[Seed, 1394: filename:join([WriteDir, "last_random_seed.txt"]), 1395: filename:join([ReadDir, 1396: "preset_random_seed.txt"])]), 1397: do_random_test(). 1398: 1399: do_random_test() -> 1400: ?line EtsMem = etsmem(), 1401: ?line OrdSet = ets_new(xxx,[ordered_set]), 1402: ?line Set = ets_new(xxx,[]), 1403: ?line do_n_times(fun() -> 1404: ?line Key = create_random_string(25), 1405: ?line Value = create_random_tuple(25), 1406: ?line ets:insert(OrdSet,{Key,Value}), 1407: ?line ets:insert(Set,{Key,Value}) 1408: end, 5000), 1409: ?line io:format("~nData inserted~n"), 1410: ?line do_n_times(fun() -> 1411: ?line I = random:uniform(25), 1412: ?line Key = create_random_string(I) ++ '_', 1413: ?line L1 = ets_match_object(OrdSet,{Key,'_'}), 1414: ?line L2 = lists:sort(ets_match_object(Set,{Key,'_'})), 1415: case L1 == L2 of 1416: false -> 1417: io:format("~p != ~p~n", 1418: [L1,L2]), 1419: ?line exit({not_eq, L1, L2}); 1420: true -> 1421: ok 1422: end 1423: end, 1424: 2000), 1425: ?line io:format("~nData matched~n"), 1426: ?line ets:match_delete(OrdSet,'_'), 1427: ?line ets:match_delete(Set,'_'), 1428: ?line do_n_times(fun() -> 1429: ?line Value = create_random_string(25), 1430: ?line Key = create_random_tuple(25), 1431: ?line ets:insert(OrdSet,{Key,Value}), 1432: ?line ets:insert(Set,{Key,Value}) 1433: end, 2000), 1434: ?line io:format("~nData inserted~n"), 1435: (fun() -> 1436: ?line Key = list_to_tuple(lists:duplicate(25,'_')), 1437: ?line L1 = ets_match_object(OrdSet,{Key,'_'}), 1438: ?line L2 = lists:sort(ets_match_object(Set,{Key,'_'})), 1439: ?line 2000 = length(L1), 1440: case L1 == L2 of 1441: false -> 1442: io:format("~p != ~p~n", 1443: [L1,L2]), 1444: ?line exit({not_eq, L1, L2}); 1445: true -> 1446: ok 1447: end 1448: end)(), 1449: (fun() -> 1450: ?line Key = {'$1','$2','$3','$4', 1451: '$5','$6','$7','$8', 1452: '$9','$10','$11','$12', 1453: '$13','$14','$15','$16', 1454: '$17','$18','$19','$20', 1455: '$21','$22','$23','$24', 1456: '$25'}, 1457: ?line L1 = ets_match_object(OrdSet,{Key,'_'}), 1458: ?line L2 = lists:sort(ets_match_object(Set,{Key,'_'})), 1459: ?line 2000 = length(L1), 1460: case L1 == L2 of 1461: false -> 1462: io:format("~p != ~p~n", 1463: [L1,L2]), 1464: ?line exit({not_eq, L1, L2}); 1465: true -> 1466: ok 1467: end 1468: end)(), 1469: (fun() -> 1470: ?line Key = {'$1','$2','$3','$4', 1471: '$5','$6','$7','$8', 1472: '$9','$10','$11','$12', 1473: '$13','$14','$15','$16', 1474: '$17','$18','$19','$20', 1475: '$21','$22','$23','$24', 1476: '$25'}, 1477: ?line L1 = ets_match(OrdSet,{Key,'_'}), 1478: ?line L2 = lists:sort(ets_match(Set,{Key,'_'})), 1479: ?line 2000 = length(L1), 1480: case L1 == L2 of 1481: false -> 1482: io:format("~p != ~p~n", 1483: [L1,L2]), 1484: ?line exit({not_eq, L1, L2}); 1485: true -> 1486: ok 1487: end 1488: end)(), 1489: ?line ets:match_delete(OrdSet,'_'), 1490: ?line ets:match_delete(Set,'_'), 1491: ?line do_n_times(fun() -> 1492: ?line Value = create_random_string(25), 1493: ?line Key = create_random_tuple(25), 1494: ?line ets:insert(OrdSet,{Key,Value}), 1495: ?line ets:insert(Set,{Key,Value}) 1496: end, 2000), 1497: ?line io:format("~nData inserted~n"), 1498: do_n_times(fun() -> 1499: ?line Key = create_partly_bound_tuple(25), 1500: ?line L1 = ets_match_object(OrdSet,{Key,'_'}), 1501: ?line L2 = lists:sort(ets_match_object(Set,{Key,'_'})), 1502: case L1 == L2 of 1503: false -> 1504: io:format("~p != ~p~n", 1505: [L1,L2]), 1506: ?line exit({not_eq, L1, L2}); 1507: true -> 1508: ok 1509: end 1510: end, 1511: 2000), 1512: ?line do_n_times(fun() -> 1513: ?line Key = create_partly_bound_tuple2(25), 1514: ?line L1 = ets_match_object(OrdSet,{Key,'_'}), 1515: ?line L2 = lists:sort(ets_match_object(Set,{Key,'_'})), 1516: case L1 == L2 of 1517: false -> 1518: io:format("~p != ~p~n", 1519: [L1,L2]), 1520: ?line exit({not_eq, L1, L2}); 1521: true -> 1522: ok 1523: end 1524: end, 1525: 2000), 1526: do_n_times(fun() -> 1527: ?line Key = create_partly_bound_tuple2(25), 1528: ?line L1 = ets_match(OrdSet,{Key,'_'}), 1529: ?line L2 = lists:sort(ets_match(Set,{Key,'_'})), 1530: case L1 == L2 of 1531: false -> 1532: io:format("~p != ~p~n", 1533: [L1,L2]), 1534: ?line exit({not_eq, L1, L2}); 1535: true -> 1536: ok 1537: end 1538: end, 1539: 2000), 1540: io:format("~nData matched~n"), 1541: ets:match_delete(OrdSet,'_'), 1542: ets:match_delete(Set,'_'), 1543: do_n_times(fun() -> 1544: do_n_times(fun() -> 1545: ?line Value = 1546: create_random_string(25), 1547: ?line Key = create_random_tuple(25), 1548: ?line ets:insert(OrdSet,{Key,Value}), 1549: ?line ets:insert(Set,{Key,Value}) 1550: end, 500), 1551: io:format("~nData inserted~n"), 1552: do_n_times(fun() -> 1553: ?line Key = 1554: create_partly_bound_tuple(25), 1555: ets:match_delete(OrdSet,{Key,'_'}), 1556: ets:match_delete(Set,{Key,'_'}), 1557: L1 = ets:info(OrdSet,size), 1558: L2 = ets:info(Set,size), 1559: [] = ets_match_object(OrdSet, 1560: {Key,'_'}), 1561: case L1 == L2 of 1562: false -> 1563: io:format("~p != ~p " 1564: "(deleted ~p)~n", 1565: [L1,L2,Key]), 1566: exit({not_eq, L1, L2, 1567: {deleted,Key}}); 1568: true -> 1569: ok 1570: end 1571: end, 1572: 50), 1573: io:format("~nData deleted~n") 1574: end, 1575: 10), 1576: ets:delete(OrdSet), 1577: ets:delete(Set), 1578: ?line verify_etsmem(EtsMem). 1579: 1580: update_element(doc) -> 1581: ["test various variants of update_element"]; 1582: update_element(suite) -> 1583: []; 1584: update_element(Config) when is_list(Config) -> 1585: ?line EtsMem = etsmem(), 1586: repeat_for_opts(update_element_opts), 1587: ?line verify_etsmem(EtsMem). 1588: 1589: update_element_opts(Opts) -> 1590: TupleCases = [{{key,val}, 1 ,2}, 1591: {{val,key}, 2, 1}, 1592: {{key,val}, 1 ,[2]}, 1593: {{key,val,val}, 1, [2,3]}, 1594: {{val,key,val,val}, 2, [3,4,1]}, 1595: {{val,val,key,val}, 3, [1,4,1,2]}, % update pos1 twice 1596: {{val,val,val,key}, 4, [2,1,2,3]}],% update pos2 twice 1597: 1598: lists:foreach(fun({Tuple,KeyPos,UpdPos}) -> update_element_opts(Tuple,KeyPos,UpdPos,Opts) end, 1599: TupleCases), 1600: 1601: update_element_neg(Opts). 1602: 1603: 1604: 1605: update_element_opts(Tuple,KeyPos,UpdPos,Opts) -> 1606: Set = ets_new(set,[{keypos,KeyPos} | Opts]), 1607: OrdSet = ets_new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]), 1608: update_element(Set,Tuple,KeyPos,UpdPos), 1609: update_element(OrdSet,Tuple,KeyPos,UpdPos), 1610: true = ets:delete(Set), 1611: true = ets:delete(OrdSet), 1612: ok. 1613: 1614: update_element(T,Tuple,KeyPos,UpdPos) -> 1615: KeyList = [17,"seventeen",<<"seventeen">>,{17},list_to_binary(lists:seq(1,100)),make_ref(), self()], 1616: lists:foreach(fun(Key) -> 1617: TupleWithKey = setelement(KeyPos,Tuple,Key), 1618: update_element_do(T,TupleWithKey,Key,UpdPos) 1619: end, 1620: KeyList). 1621: 1622: update_element_do(Tab,Tuple,Key,UpdPos) -> 1623: 1624: % Strategy: Step around in Values array and call ets:update_element for the values. 1625: % Take Length number of steps of size 1, then of size 2, ..., Length-1. 1626: % This will try all combinations of {fromValue,toValue} 1627: % 1628: % IMPORTANT: size(Values) must be a prime number for this to work!!! 1629: 1630: %io:format("update_element_do for key=~p\n",[Key]), 1631: Big32 = 16#12345678, 1632: Big64 = 16#123456789abcdef0, 1633: Values = { 623, -27, 0, Big32, -Big32, Big64, -Big64, Big32*Big32, 1634: -Big32*Big32, Big32*Big64, -Big32*Big64, Big64*Big64, -Big64*Big64, 1635: "A", "Sverker", [], {12,-132}, {}, 1636: <<45,232,0,12,133>>, <<234,12,23>>, list_to_binary(lists:seq(1,100)), 1637: (fun(X) -> X*Big32 end), 1638: make_ref(), make_ref(), self(), ok, update_element, 28, 29 }, 1639: Length = size(Values), 1640: 1641: PosValArgF = fun(ToIx, ResList, [Pos | PosTail], Rand, MeF) -> 1642: NextIx = (ToIx+Rand) rem Length, 1643: MeF(NextIx, [{Pos,element(ToIx+1,Values)} | ResList], PosTail, Rand, MeF); 1644: 1645: (_ToIx, ResList, [], _Rand, _MeF) -> 1646: ResList; 1647: 1648: (ToIx, [], Pos, _Rand, _MeF) -> 1649: {Pos, element(ToIx+1,Values)} % single {pos,value} arg 1650: end, 1651: 1652: UpdateF = fun(ToIx,Rand) -> 1653: PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF), 1654: %%io:format("update_element(~p)~n",[PosValArg]), 1655: ArgHash = erlang:phash2({Tab,Key,PosValArg}), 1656: ?line true = ets:update_element(Tab, Key, PosValArg), 1657: ?line ArgHash = erlang:phash2({Tab,Key,PosValArg}), 1658: NewTuple = update_tuple(PosValArg,Tuple), 1659: ?line [NewTuple] = ets:lookup(Tab,Key) 1660: end, 1661: 1662: LoopF = fun(_FromIx, Incr, _Times, Checksum, _MeF) when Incr >= Length -> 1663: Checksum; % done 1664: 1665: (FromIx, Incr, 0, Checksum, MeF) -> 1666: MeF(FromIx, Incr+1, Length, Checksum, MeF); 1667: 1668: (FromIx, Incr, Times, Checksum, MeF) -> 1669: ToIx = (FromIx + Incr) rem Length, 1670: UpdateF(ToIx,Checksum), 1671: if 1672: Incr =:= 0 -> UpdateF(ToIx,Checksum); % extra update to same value 1673: true -> true 1674: end, 1675: MeF(ToIx, Incr, Times-1, Checksum+ToIx+1, MeF) 1676: end, 1677: 1678: FirstTuple = Tuple, 1679: ?line true = ets:insert(Tab,FirstTuple), 1680: ?line [FirstTuple] = ets:lookup(Tab,Key), 1681: 1682: Checksum = LoopF(0, 1, Length, 0, LoopF), 1683: ?line Checksum = (Length-1)*Length*(Length+1) div 2, % if Length is a prime 1684: ok. 1685: 1686: update_tuple({Pos,Val}, Tpl) -> 1687: setelement(Pos, Tpl, Val); 1688: update_tuple([{Pos,Val} | Tail], Tpl) -> 1689: update_tuple(Tail,setelement(Pos, Tpl, Val)); 1690: update_tuple([], Tpl) -> 1691: Tpl. 1692: 1693: 1694: 1695: update_element_neg(Opts) -> 1696: Set = ets_new(set,Opts), 1697: OrdSet = ets_new(ordered_set,[ordered_set | Opts]), 1698: update_element_neg_do(Set), 1699: update_element_neg_do(OrdSet), 1700: ets:delete(Set), 1701: ?line {'EXIT',{badarg,_}} = (catch ets:update_element(Set,key,{2,1})), 1702: ets:delete(OrdSet), 1703: ?line {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key,{2,1})), 1704: 1705: ?line Bag = ets_new(bag,[bag | Opts]), 1706: ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]), 1707: ?line {'EXIT',{badarg,_}} = (catch ets:update_element(Bag,key,{2,1})), 1708: ?line {'EXIT',{badarg,_}} = (catch ets:update_element(DBag,key,{2,1})), 1709: true = ets:delete(Bag), 1710: true = ets:delete(DBag), 1711: ok. 1712: 1713: 1714: update_element_neg_do(T) -> 1715: Object = {key, 0, "Hej"}, 1716: ?line true = ets:insert(T,Object), 1717: 1718: UpdateF = fun(Arg3) -> 1719: ArgHash = erlang:phash2({T,key,Arg3}), 1720: ?line {'EXIT',{badarg,_}} = (catch ets:update_element(T,key,Arg3)), 1721: ?line ArgHash = erlang:phash2({T,key,Arg3}), 1722: ?line [Object] = ets:lookup(T,key) 1723: end, 1724: 1725: %% List of invalid {Pos,Value} tuples 1726: InvList = [false, {2}, {2,1,false}, {false,1}, {0,1}, {1,1}, {-1,1}, {4,1}], 1727: 1728: lists:foreach(UpdateF, InvList), 1729: lists:foreach(fun(InvTpl) -> UpdateF([{2,1},InvTpl]) end, InvList), 1730: lists:foreach(fun(InvTpl) -> UpdateF([InvTpl,{2,1}]) end, InvList), 1731: lists:foreach(fun(InvTpl) -> UpdateF([{2,1},{3,"Hello"},InvTpl]) end, InvList), 1732: lists:foreach(fun(InvTpl) -> UpdateF([{3,"Hello"},{2,1},InvTpl]) end, InvList), 1733: lists:foreach(fun(InvTpl) -> UpdateF([{2,1},InvTpl,{3,"Hello"}]) end, InvList), 1734: lists:foreach(fun(InvTpl) -> UpdateF([InvTpl,{3,"Hello"},{2,1}]) end, InvList), 1735: UpdateF([{2,1} | {3,1}]), 1736: lists:foreach(fun(InvTpl) -> UpdateF([{2,1} | InvTpl]) end, InvList), 1737: 1738: ?line true = ets:update_element(T,key,[]), 1739: ?line false = ets:update_element(T,false,[]), 1740: ?line false = ets:update_element(T,false,{2,1}), 1741: ?line ets:delete(T,key), 1742: ?line false = ets:update_element(T,key,{2,1}), 1743: ok. 1744: 1745: 1746: update_counter(doc) -> 1747: ["test various variants of update_counter"]; 1748: update_counter(suite) -> 1749: []; 1750: update_counter(Config) when is_list(Config) -> 1751: ?line EtsMem = etsmem(), 1752: repeat_for_opts(update_counter_do), 1753: ?line verify_etsmem(EtsMem). 1754: 1755: update_counter_do(Opts) -> 1756: Set = ets_new(set,Opts), 1757: OrdSet = ets_new(ordered_set,[ordered_set | Opts]), 1758: update_counter_for(Set), 1759: update_counter_for(OrdSet), 1760: ets:delete(Set), 1761: ets:delete(OrdSet), 1762: update_counter_neg(Opts). 1763: 1764: update_counter_for(T) -> 1765: ?line ets:insert(T,{a,1,1}), 1766: ?line 101 = ets:update_counter(T,a,100), 1767: ?line [{a,101,1}] = ets:lookup(T,a), 1768: ?line 101 = ets:update_counter(T,a,{3,100}), 1769: ?line [{a,101,101}] = ets:lookup(T,a), 1770: 1771: 1772: LooperF = fun(Obj, 0, _, _) -> 1773: Obj; 1774: 1775: (Obj, Times, Arg3, Myself) -> 1776: ?line {NewObj, Ret} = uc_mimic(Obj,Arg3), 1777: ArgHash = erlang:phash2({T,a,Arg3}), 1778: %%io:format("update_counter(~p, ~p, ~p) expecting ~p\n",[T,a,Arg3,Ret]), 1779: ?line Ret = ets:update_counter(T,a,Arg3), 1780: ?line ArgHash = erlang:phash2({T,a,Arg3}), 1781: %%io:format("NewObj=~p~n ",[NewObj]), 1782: ?line [NewObj] = ets:lookup(T,a), 1783: Myself(NewObj,Times-1,Arg3,Myself) 1784: end, 1785: 1786: LoopF = fun(Obj, Times, Arg3) -> 1787: %%io:format("Loop start:\nObj = ~p\nArg3=~p\n",[Obj,Arg3]), 1788: LooperF(Obj,Times,Arg3,LooperF) 1789: end, 1790: 1791: SmallMax32 = (1 bsl 27) - 1, 1792: SmallMax64 = (1 bsl (27+32)) - 1, 1793: Big1Max32 = (1 bsl 32) - 1, 1794: Big1Max64 = (1 bsl 64) - 1, 1795: 1796: Steps = 100, 1797: Obj0 = {a,0,0,0,0}, 1798: ?line ets:insert(T,Obj0), 1799: ?line Obj1 = LoopF(Obj0, Steps, {2,(SmallMax32 div Steps)*2}), 1800: ?line Obj2 = LoopF(Obj1, Steps, {3,(SmallMax64 div Steps)*2}), 1801: ?line Obj3 = LoopF(Obj2, Steps, {4,(Big1Max32 div Steps)*2}), 1802: ?line Obj4 = LoopF(Obj3, Steps, {5,(Big1Max64 div Steps)*2}), 1803: 1804: ?line Obj5 = LoopF(Obj4, Steps, {2,-(SmallMax32 div Steps)*4}), 1805: ?line Obj6 = LoopF(Obj5, Steps, {3,-(SmallMax64 div Steps)*4}), 1806: ?line Obj7 = LoopF(Obj6, Steps, {4,-(Big1Max32 div Steps)*4}), 1807: ?line Obj8 = LoopF(Obj7, Steps, {5,-(Big1Max64 div Steps)*4}), 1808: 1809: ?line Obj9 = LoopF(Obj8, Steps, {2,(SmallMax32 div Steps)*2}), 1810: ?line ObjA = LoopF(Obj9, Steps, {3,(SmallMax64 div Steps)*2}), 1811: ?line ObjB = LoopF(ObjA, Steps, {4,(Big1Max32 div Steps)*2}), 1812: ?line Obj0 = LoopF(ObjB, Steps, {5,(Big1Max64 div Steps)*2}), 1813: 1814: %% back at zero, same trip again with lists 1815: 1816: ?line Obj4 = LoopF(Obj0,Steps,[{2, (SmallMax32 div Steps)*2}, 1817: {3, (SmallMax64 div Steps)*2}, 1818: {4, (Big1Max32 div Steps)*2}, 1819: {5, (Big1Max64 div Steps)*2}]), 1820: 1821: ?line Obj8 = LoopF(Obj4,Steps,[{4, -(Big1Max32 div Steps)*4}, 1822: {2, -(SmallMax32 div Steps)*4}, 1823: {5, -(Big1Max64 div Steps)*4}, 1824: {3, -(SmallMax64 div Steps)*4}]), 1825: 1826: ?line Obj0 = LoopF(Obj8,Steps,[{5, (Big1Max64 div Steps)*2}, 1827: {2, (SmallMax32 div Steps)*2}, 1828: {4, (Big1Max32 div Steps)*2}, 1829: {3, (SmallMax64 div Steps)*2}]), 1830: 1831: %% make them shift size at the same time 1832: ?line ObjC = LoopF(Obj0,Steps,[{5, (Big1Max64 div Steps)*2}, 1833: {3, (Big1Max64 div Steps)*2 + 1}, 1834: {2, -(Big1Max64 div Steps)*2}, 1835: {4, -(Big1Max64 div Steps)*2 + 1}]), 1836: 1837: %% update twice in same list 1838: ?line ObjD = LoopF(ObjC,Steps,[{5, -(Big1Max64 div Steps) + 1}, 1839: {3, -(Big1Max64 div Steps)*2 - 1}, 1840: {5, -(Big1Max64 div Steps) - 1}, 1841: {4, (Big1Max64 div Steps)*2 - 1}]), 1842: 1843: ?line Obj0 = LoopF(ObjD,Steps,[{2, (Big1Max64 div Steps) - 1}, 1844: {4, Big1Max64*2}, 1845: {2, (Big1Max64 div Steps) + 1}, 1846: {4, -Big1Max64*2}]), 1847: 1848: %% warping with list 1849: ?line ObjE = LoopF(Obj0,1000, 1850: [{3,SmallMax32*4 div 5,SmallMax32*2,-SmallMax32*2}, 1851: {5,-SmallMax64*4 div 7,-SmallMax64*2,SmallMax64*2}, 1852: {4,-Big1Max32*4 div 11,-Big1Max32*2,Big1Max32*2}, 1853: {2,Big1Max64*4 div 13,Big1Max64*2,-Big1Max64*2}]), 1854: 1855: %% warping without list 1856: ?line ObjF = LoopF(ObjE,1000,{3,SmallMax32*4 div 5,SmallMax32*2,-SmallMax32*2}), 1857: ?line ObjG = LoopF(ObjF,1000,{5,-SmallMax64*4 div 7,-SmallMax64*2,SmallMax64*2}), 1858: ?line ObjH = LoopF(ObjG,1000,{4,-Big1Max32*4 div 11,-Big1Max32*2,Big1Max32*2}), 1859: ?line ObjI = LoopF(ObjH,1000,{2,Big1Max64*4 div 13,Big1Max64*2,-Big1Max64*2}), 1860: 1861: %% mixing it up 1862: ?line LoopF(ObjI,1000, 1863: [{3,SmallMax32*4 div 5,SmallMax32*2,-SmallMax32*2}, 1864: {5,-SmallMax64*4 div 3}, 1865: {3,-SmallMax32*4 div 11}, 1866: {5,0}, 1867: {4,1}, 1868: {5,-SmallMax64*4 div 7,-SmallMax64*2,SmallMax64*2}, 1869: {2,Big1Max64*4 div 13,Big1Max64*2,-Big1Max64*2}]), 1870: ok. 1871: 1872: %% uc_mimic works kind of like the real ets:update_counter 1873: %% Obj = Tuple in ets 1874: %% Pits = {Pos,Incr} | {Pos,Incr,Thres,Warp} 1875: %% Returns {Updated tuple in ets, Return value from update_counter} 1876: uc_mimic(Obj, Pits) when is_tuple(Pits) -> 1877: ?line Pos = element(1,Pits), 1878: ?line NewObj = setelement(Pos, Obj, uc_adder(element(Pos,Obj),Pits)), 1879: ?line {NewObj, element(Pos,NewObj)}; 1880: 1881: uc_mimic(Obj, PitsList) when is_list(PitsList) -> 1882: ?line {NewObj,ValList} = uc_mimic(Obj,PitsList,[]), 1883: ?line {NewObj,lists:reverse(ValList)}. 1884: 1885: uc_mimic(Obj, [], Acc) -> 1886: ?line {Obj,Acc}; 1887: uc_mimic(Obj, [Pits|Tail], Acc) -> 1888: ?line {NewObj,NewVal} = uc_mimic(Obj,Pits), 1889: ?line uc_mimic(NewObj,Tail,[NewVal|Acc]). 1890: 1891: uc_adder(Init, {_Pos, Add}) -> 1892: Init + Add; 1893: uc_adder(Init, {_Pos, Add, Thres, Warp}) -> 1894: case Init + Add of 1895: X when X > Thres, Add > 0 -> 1896: Warp; 1897: Y when Y < Thres, Add < 0 -> 1898: Warp; 1899: Z -> 1900: Z 1901: end. 1902: 1903: update_counter_neg(Opts) -> 1904: Set = ets_new(set,Opts), 1905: OrdSet = ets_new(ordered_set,[ordered_set | Opts]), 1906: update_counter_neg_for(Set), 1907: update_counter_neg_for(OrdSet), 1908: ets:delete(Set), 1909: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(Set,key,1)), 1910: ets:delete(OrdSet), 1911: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(OrdSet,key,1)), 1912: 1913: ?line Bag = ets_new(bag,[bag | Opts]), 1914: ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]), 1915: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(Bag,key,1)), 1916: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(DBag,key,1)), 1917: true = ets:delete(Bag), 1918: true = ets:delete(DBag), 1919: ok. 1920: 1921: update_counter_neg_for(T) -> 1922: Object = {key,0,false,1}, 1923: ?line true = ets:insert(T,Object), 1924: 1925: UpdateF = fun(Arg3) -> 1926: ArgHash = erlang:phash2({T,key,Arg3}), 1927: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(T,key,Arg3)), 1928: ?line ArgHash = erlang:phash2({T,key,Arg3}), 1929: ?line [Object] = ets:lookup(T,key) 1930: end, 1931: 1932: %% List of invalid arg3-tuples 1933: InvList = [false, {2}, {2,false}, {false,1}, 1934: {0,1}, {-1,1}, % BUG < R12B-2 1935: {1,1}, {3,1}, {5,1}, {2,1,100}, {2,1,100,0,false}, {2,1,false,0}, {2,1,0,false}], 1936: 1937: lists:foreach(UpdateF, InvList), 1938: lists:foreach(fun(Inv) -> UpdateF([{2,1},Inv]) end, InvList), 1939: lists:foreach(fun(Inv) -> UpdateF([Inv,{2,1}]) end, InvList), 1940: lists:foreach(fun(Inv) -> UpdateF([{2,1},{4,-100},Inv]) end, InvList), 1941: lists:foreach(fun(Inv) -> UpdateF([{4,100,50,0},{2,1},Inv]) end, InvList), 1942: lists:foreach(fun(Inv) -> UpdateF([{2,1},Inv,{4,100,50,0}]) end, InvList), 1943: lists:foreach(fun(Inv) -> UpdateF([Inv,{4,100,50,0},{2,1}]) end, InvList), 1944: UpdateF([{2,1} | {4,1}]), 1945: lists:foreach(fun(Inv) -> UpdateF([{2,1} | Inv]) end, InvList), 1946: 1947: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(T,false,1)), 1948: ?line ets:delete(T,key), 1949: ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(T,key,1)), 1950: ok. 1951: 1952: 1953: evil_update_counter(Config) when is_list(Config) -> 1954: %% The code server uses ets table. Pre-load modules that might not be 1955: %% already loaded. 1956: gb_sets:module_info(), 1957: math:module_info(), 1958: ordsets:module_info(), 1959: random:module_info(), 1960: 1961: repeat_for_opts(evil_update_counter_do). 1962: 1963: evil_update_counter_do(Opts) -> 1964: ?line EtsMem = etsmem(), 1965: ?line process_flag(trap_exit, true), 1966: ?line Pids = [my_spawn_link(fun() -> evil_counter(I,Opts) end) || I <- lists:seq(1, 40)], 1967: ?line wait_for_all(gb_sets:from_list(Pids)), 1968: ?line verify_etsmem(EtsMem), 1969: ok. 1970: 1971: wait_for_all(Pids0) -> 1972: case gb_sets:is_empty(Pids0) of 1973: true -> 1974: ok; 1975: false -> 1976: receive 1977: {'EXIT',Pid,normal} -> 1978: ?line Pids = gb_sets:delete(Pid, Pids0), 1979: wait_for_all(Pids); 1980: Other -> 1981: io:format("unexpected: ~p\n", [Other]), 1982: ?line ?t:fail() 1983: end 1984: end. 1985: 1986: evil_counter(I,Opts) -> 1987: T = ets_new(a, Opts), 1988: Start0 = case I rem 3 of 1989: 0 -> 16#12345678; 1990: 1 -> 16#12345678FFFFFFFF; 1991: 2 -> 16#7777777777FFFFFFFF863648726743 1992: end, 1993: Start = Start0 + random:uniform(100000), 1994: ets:insert(T, {dracula,Start}), 1995: Iter = 40000, 1996: End = Start + Iter, 1997: End = evil_counter_1(Iter, T), 1998: ets:delete(T). 1999: 2000: evil_counter_1(0, T) -> 2001: [{dracula,Count}] = ets:lookup(T, dracula), 2002: Count; 2003: evil_counter_1(Iter, T) -> 2004: ets:update_counter(T, dracula, 1), 2005: evil_counter_1(Iter-1, T). 2006: 2007: fixtable_next(doc) -> 2008: ["Check that a first-next sequence always works on a fixed table"]; 2009: fixtable_next(suite) -> 2010: []; 2011: fixtable_next(Config) when is_list(Config) -> 2012: repeat_for_opts(fixtable_next_do, [write_concurrency,all_types]). 2013: 2014: fixtable_next_do(Opts) -> 2015: ?line EtsMem = etsmem(), 2016: ?line do_fixtable_next(ets_new(set,[public | Opts])), 2017: ?line verify_etsmem(EtsMem). 2018: 2019: do_fixtable_next(Tab) -> 2020: ?line F = fun(X,T,FF) -> case X of 2021: 0 -> true; 2022: _ -> 2023: ets:insert(T, {X, 2024: integer_to_list(X), 2025: X rem 10}), 2026: FF(X-1,T,FF) 2027: end 2028: end, 2029: ?line F(100,Tab,F), 2030: ?line ets:safe_fixtable(Tab,true), 2031: ?line First = ets:first(Tab), 2032: ?line ets:delete(Tab, First), 2033: ?line ets:next(Tab, First), 2034: ?line ets:match_delete(Tab,{'_','_','_'}), 2035: ?line '$end_of_table' = ets:next(Tab, First), 2036: ?line true = ets:info(Tab, fixed), 2037: ?line ets:safe_fixtable(Tab, false), 2038: ?line false = ets:info(Tab, fixed), 2039: ?line ets:delete(Tab). 2040: 2041: fixtable_insert(doc) -> 2042: ["Check inserts of deleted keys in fixed bags"]; 2043: fixtable_insert(suite) -> 2044: []; 2045: fixtable_insert(Config) when is_list(Config) -> 2046: Combos = [[Type,{write_concurrency,WC}] || Type<- [bag,duplicate_bag], 2047: WC <- [false,true]], 2048: lists:foreach(fun(Opts) -> fixtable_insert_do(Opts) end, 2049: Combos), 2050: ok. 2051: 2052: fixtable_insert_do(Opts) -> 2053: io:format("Opts = ~p\n",[Opts]), 2054: Ets = make_table(ets, Opts, [{a,1}, {a,2}, {b,1}, {b,2}]), 2055: ets:safe_fixtable(Ets,true), 2056: ets:match_delete(Ets,{b,1}), 2057: First = ets:first(Ets), 2058: ?line Next = case First of 2059: a -> b; 2060: b -> a 2061: end, 2062: ?line Next = ets:next(Ets,First), 2063: ets:delete(Ets,Next), 2064: ?line '$end_of_table' = ets:next(Ets,First), 2065: ets:insert(Ets, {Next,1}), 2066: ?line false = ets:insert_new(Ets, {Next,1}), 2067: ?line Next = ets:next(Ets,First), 2068: ?line '$end_of_table' = ets:next(Ets,Next), 2069: ets:delete(Ets,Next), 2070: '$end_of_table' = ets:next(Ets,First), 2071: ets:insert(Ets, {Next,2}), 2072: ?line false = ets:insert_new(Ets, {Next,1}), 2073: Next = ets:next(Ets,First), 2074: '$end_of_table' = ets:next(Ets,Next), 2075: ets:delete(Ets,First), 2076: ?line Next = ets:first(Ets), 2077: ?line '$end_of_table' = ets:next(Ets,Next), 2078: ets:delete(Ets,Next), 2079: ?line '$end_of_table' = ets:next(Ets,First), 2080: ?line true = ets:insert_new(Ets,{Next,1}), 2081: ?line false = ets:insert_new(Ets,{Next,2}), 2082: ?line Next = ets:next(Ets,First), 2083: ets:delete_object(Ets,{Next,1}), 2084: ?line '$end_of_table' = ets:next(Ets,First), 2085: ?line true = ets:insert_new(Ets,{Next,2}), 2086: ?line false = ets:insert_new(Ets,{Next,1}), 2087: ?line Next = ets:next(Ets,First), 2088: ets:delete(Ets,First), 2089: ets:safe_fixtable(Ets,false), 2090: {'EXIT',{badarg,_}} = (catch ets:next(Ets,First)), 2091: ok. 2092: 2093: write_concurrency(doc) -> ["The 'write_concurrency' option"]; 2094: write_concurrency(suite) -> []; 2095: write_concurrency(Config) when is_list(Config) -> 2096: ?line EtsMem = etsmem(), 2097: Yes1 = ets_new(foo,[public,{write_concurrency,true}]), 2098: Yes2 = ets_new(foo,[protected,{write_concurrency,true}]), 2099: No1 = ets_new(foo,[private,{write_concurrency,true}]), 2100: 2101: Yes3 = ets_new(foo,[bag,public,{write_concurrency,true}]), 2102: Yes4 = ets_new(foo,[bag,protected,{write_concurrency,true}]), 2103: No2 = ets_new(foo,[bag,private,{write_concurrency,true}]), 2104: 2105: Yes5 = ets_new(foo,[duplicate_bag,public,{write_concurrency,true}]), 2106: Yes6 = ets_new(foo,[duplicate_bag,protected,{write_concurrency,true}]), 2107: No3 = ets_new(foo,[duplicate_bag,private,{write_concurrency,true}]), 2108: 2109: No4 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]), 2110: No5 = ets_new(foo,[ordered_set,protected,{write_concurrency,true}]), 2111: No6 = ets_new(foo,[ordered_set,private,{write_concurrency,true}]), 2112: 2113: No7 = ets_new(foo,[public,{write_concurrency,false}]), 2114: No8 = ets_new(foo,[protected,{write_concurrency,false}]), 2115: 2116: ?line YesMem = ets:info(Yes1,memory), 2117: ?line NoHashMem = ets:info(No1,memory), 2118: ?line NoTreeMem = ets:info(No4,memory), 2119: io:format("YesMem=~p NoHashMem=~p NoTreeMem=~p\n",[YesMem,NoHashMem,NoTreeMem]), 2120: 2121: ?line YesMem = ets:info(Yes2,memory), 2122: ?line YesMem = ets:info(Yes3,memory), 2123: ?line YesMem = ets:info(Yes4,memory), 2124: ?line YesMem = ets:info(Yes5,memory), 2125: ?line YesMem = ets:info(Yes6,memory), 2126: ?line NoHashMem = ets:info(No2,memory), 2127: ?line NoHashMem = ets:info(No3,memory), 2128: ?line NoTreeMem = ets:info(No5,memory), 2129: ?line NoTreeMem = ets:info(No6,memory), 2130: ?line NoHashMem = ets:info(No7,memory), 2131: ?line NoHashMem = ets:info(No8,memory), 2132: 2133: case erlang:system_info(smp_support) of 2134: true -> 2135: ?line true = YesMem > NoHashMem, 2136: ?line true = YesMem > NoTreeMem; 2137: false -> 2138: ?line true = YesMem =:= NoHashMem 2139: end, 2140: 2141: ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,foo}])), 2142: ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency}])), 2143: ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,true,foo}])), 2144: ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,write_concurrency])), 2145: 2146: lists:foreach(fun(T) -> ets:delete(T) end, 2147: [Yes1,Yes2,Yes3,Yes4,Yes5,Yes6, 2148: No1,No2,No3,No4,No5,No6,No7,No8]), 2149: ?line verify_etsmem(EtsMem), 2150: ok. 2151: 2152: 2153: heir(doc) -> ["The 'heir' option"]; 2154: heir(suite) -> []; 2155: heir(Config) when is_list(Config) -> 2156: repeat_for_opts(heir_do). 2157: 2158: heir_do(Opts) -> 2159: ?line EtsMem = etsmem(), 2160: Master = self(), 2161: 2162: %% Different types of heir data and link/monitor relations 2163: TestFun = fun(Arg) -> {EtsMem,Arg} end, 2164: Combos = [{Data,Mode} || Data<-[foo_data, <<"binary">>, 2165: lists:seq(1,10), {17,TestFun,self()}, 2166: "The busy heir"], 2167: Mode<-[none,link,monitor]], 2168: ?line lists:foreach(fun({Data,Mode})-> heir_1(Data,Mode,Opts) end, 2169: Combos), 2170: 2171: %% No heir 2172: {Founder1,MrefF1} = my_spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end), 2173: Founder1 ! {go, none}, 2174: ?line {"No heir",Founder1} = receive_any(), 2175: ?line {'DOWN', MrefF1, process, Founder1, normal} = receive_any(), 2176: ?line undefined = ets:info(foo), 2177: 2178: %% An already dead heir 2179: {Heir2,MrefH2} = my_spawn_monitor(fun()->die end), 2180: ?line {'DOWN', MrefH2, process, Heir2, normal} = receive_any(), 2181: {Founder2,MrefF2} = my_spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end), 2182: Founder2 ! {go, Heir2}, 2183: ?line {"No heir",Founder2} = receive_any(), 2184: ?line {'DOWN', MrefF2, process, Founder2, normal} = receive_any(), 2185: ?line undefined = ets:info(foo), 2186: 2187: %% When heir dies before founder 2188: {Founder3,MrefF3} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), 2189: {Heir3,MrefH3} = my_spawn_monitor(fun()->heir_heir(Founder3)end), 2190: Founder3 ! {go, Heir3}, 2191: ?line {'DOWN', MrefH3, process, Heir3, normal} = receive_any(), 2192: Founder3 ! die_please, 2193: ?line {'DOWN', MrefF3, process, Founder3, normal} = receive_any(), 2194: ?line undefined = ets:info(foo), 2195: 2196: %% When heir dies and pid reused before founder dies 2197: repeat_while(fun() -> 2198: NextPidIx = erts_debug:get_internal_state(next_pid), 2199: {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), 2200: {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end), 2201: Founder4 ! {go, Heir4}, 2202: ?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(), 2203: erts_debug:set_internal_state(next_pid, NextPidIx), 2204: DoppelGanger = spawn_monitor_with_pid(Heir4, 2205: fun()-> ?line die_please = receive_any() end), 2206: Founder4 ! die_please, 2207: ?line {'DOWN', MrefF4, process, Founder4, normal} = receive_any(), 2208: case DoppelGanger of 2209: {Heir4,MrefH4_B} -> 2210: Heir4 ! die_please, 2211: ?line {'DOWN', MrefH4_B, process, Heir4, normal} = receive_any(), 2212: ?line undefined = ets:info(foo), 2213: false; 2214: failed -> 2215: io:format("Failed to spawn process with pid ~p\n", [Heir4]), 2216: true % try again 2217: end 2218: end), 2219: 2220: ?line verify_etsmem(EtsMem). 2221: 2222: heir_founder(Master, HeirData, Opts) -> 2223: ?line {go,Heir} = receive_any(), 2224: HeirTpl = case Heir of 2225: none -> {heir,none}; 2226: _ -> {heir, Heir, HeirData} 2227: end, 2228: ?line T = ets_new(foo,[named_table, private, HeirTpl | Opts]), 2229: ?line true = ets:insert(T,{key,1}), 2230: ?line [{key,1}] = ets:lookup(T,key), 2231: Self = self(), 2232: ?line Self = ets:info(T,owner), 2233: ?line case ets:info(T,heir) of 2234: none -> 2235: ?line true = (Heir =:= none) orelse (not is_process_alive(Heir)), 2236: Master ! {"No heir",self()}; 2237: 2238: Heir -> 2239: ?line true = is_process_alive(Heir), 2240: Heir ! {table,T,HeirData}, 2241: die_please = receive_any() 2242: end. 2243: 2244: 2245: heir_heir(Founder) -> 2246: heir_heir(Founder, none). 2247: heir_heir(Founder, Mode) -> 2248: ?line {table,T,HeirData} = receive_any(), 2249: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)), 2250: ?line case HeirData of 2251: "The dying heir" -> exit(normal); 2252: _ -> ok 2253: end, 2254: 2255: ?line Mref = case Mode of 2256: link -> process_flag(trap_exit, true), 2257: link(Founder); 2258: monitor -> erlang:monitor(process,Founder); 2259: none -> ok 2260: end, 2261: ?line Founder ! die_please, 2262: ?line Msg = case HeirData of 2263: "The busy heir" -> receive_any_spinning(); 2264: _ -> receive_any() 2265: end, 2266: ?line {'ETS-TRANSFER', T, Founder, HeirData} = Msg, 2267: ?line foo = T, 2268: ?line Self = self(), 2269: ?line Self = ets:info(T,owner), 2270: ?line Self = ets:info(T,heir), 2271: ?line [{key,1}] = ets:lookup(T,key), 2272: ?line true = ets:insert(T,{key,2}), 2273: ?line [{key,2}] = ets:lookup(T,key), 2274: ?line case Mode of % Verify that EXIT or DOWN comes after ETS-TRANSFER 2275: link -> 2276: {'EXIT',Founder,normal} = receive_any(), 2277: process_flag(trap_exit, false); 2278: monitor -> 2279: {'DOWN', Mref, process, Founder, normal} = receive_any(); 2280: none -> ok 2281: end. 2282: 2283: 2284: heir_1(HeirData,Mode,Opts) -> 2285: io:format("test with heir_data = ~p\n", [HeirData]), 2286: Master = self(), 2287: ?line Founder = my_spawn_link(fun() -> heir_founder(Master,HeirData,Opts) end), 2288: io:format("founder spawned = ~p\n", [Founder]), 2289: ?line {Heir,Mref} = my_spawn_monitor(fun() -> heir_heir(Founder,Mode) end), 2290: io:format("heir spawned = ~p\n", [{Heir,Mref}]), 2291: ?line Founder ! {go, Heir}, 2292: ?line {'DOWN', Mref, process, Heir, normal} = receive_any(). 2293: 2294: give_away(doc) -> ["ets:give_way/3"]; 2295: give_away(suite) -> []; 2296: give_away(Config) when is_list(Config) -> 2297: repeat_for_opts(give_away_do). 2298: 2299: give_away_do(Opts) -> 2300: ?line T = ets_new(foo,[named_table, private | Opts]), 2301: ?line true = ets:insert(T,{key,1}), 2302: ?line [{key,1}] = ets:lookup(T,key), 2303: Parent = self(), 2304: 2305: %% Give and then give back 2306: ?line {Receiver,Mref} = my_spawn_monitor(fun()-> give_away_receiver(T,Parent) end), 2307: ?line give_me = receive_any(), 2308: ?line true = ets:give_away(T,Receiver,here_you_are), 2309: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)), 2310: ?line Receiver ! give_back, 2311: ?line {'ETS-TRANSFER',T,Receiver,"Tillbakakaka"} = receive_any(), 2312: ?line [{key,2}] = ets:lookup(T,key), 2313: ?line {'DOWN', Mref, process, Receiver, normal} = receive_any(), 2314: 2315: %% Give and then let receiver keep it 2316: ?line true = ets:insert(T,{key,1}), 2317: ?line {Receiver3,Mref3} = my_spawn_monitor(fun()-> give_away_receiver(T,Parent) end), 2318: ?line give_me = receive_any(), 2319: ?line true = ets:give_away(T,Receiver3,here_you_are), 2320: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)), 2321: ?line Receiver3 ! die_please, 2322: ?line {'DOWN', Mref3, process, Receiver3, normal} = receive_any(), 2323: ?line undefined = ets:info(T), 2324: 2325: %% Give and then kill receiver to get back 2326: ?line T2 = ets_new(foo,[private | Opts]), 2327: ?line true = ets:insert(T2,{key,1}), 2328: ?line ets:setopts(T2,{heir,self(),"Som en gummiboll..."}), 2329: ?line {Receiver2,Mref2} = my_spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), 2330: ?line give_me = receive_any(), 2331: ?line true = ets:give_away(T2,Receiver2,here_you_are), 2332: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T2,key)), 2333: ?line Receiver2 ! die_please, 2334: ?line {'ETS-TRANSFER',T2,Receiver2,"Som en gummiboll..."} = receive_any(), 2335: ?line [{key,2}] = ets:lookup(T2,key), 2336: ?line {'DOWN', Mref2, process, Receiver2, normal} = receive_any(), 2337: 2338: %% Some negative testing 2339: ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,Receiver,"To a dead one")), 2340: ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,self(),"To myself")), 2341: ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,"not a pid","To wrong type")), 2342: 2343: ?line true = ets:delete(T2), 2344: ?line {ReceiverNeg,MrefNeg} = my_spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), 2345: ?line give_me = receive_any(), 2346: ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,ReceiverNeg,"A deleted table")), 2347: 2348: ?line T3 = ets_new(foo,[public | Opts]), 2349: my_spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")), 2350: Parent ! done 2351: end), 2352: ?line done = receive_any(), 2353: ?line ReceiverNeg ! no_soup_for_you, 2354: ?line {'DOWN', MrefNeg, process, ReceiverNeg, normal} = receive_any(), 2355: ok. 2356: 2357: give_away_receiver(T, Giver) -> 2358: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)), 2359: ?line Giver ! give_me, 2360: ?line case receive_any() of 2361: {'ETS-TRANSFER',T,Giver,here_you_are} -> 2362: ?line [{key,1}] = ets:lookup(T,key), 2363: ?line true = ets:insert(T,{key,2}), 2364: ?line case receive_any() of 2365: give_back -> 2366: ?line true = ets:give_away(T,Giver,"Tillbakakaka"), 2367: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)); 2368: die_please -> 2369: ok 2370: end; 2371: no_soup_for_you -> 2372: ok 2373: end. 2374: 2375: 2376: setopts(doc) -> ["ets:setopts/2"]; 2377: setopts(suite) -> []; 2378: setopts(Config) when is_list(Config) -> 2379: repeat_for_opts(setopts_do,[write_concurrency,all_types]). 2380: 2381: setopts_do(Opts) -> 2382: Self = self(), 2383: ?line T = ets_new(foo,[named_table, private | Opts]), 2384: ?line none = ets:info(T,heir), 2385: Heir = my_spawn_link(fun()->heir_heir(Self) end), 2386: ?line ets:setopts(T,{heir,Heir,"Data"}), 2387: ?line Heir = ets:info(T,heir), 2388: ?line ets:setopts(T,{heir,self(),"Data"}), 2389: ?line Self = ets:info(T,heir), 2390: ?line ets:setopts(T,[{heir,Heir,"Data"}]), 2391: ?line Heir = ets:info(T,heir), 2392: ?line ets:setopts(T,[{heir,none}]), 2393: ?line none = ets:info(T,heir), 2394: 2395: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,[{heir,self(),"Data"},false])), 2396: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{heir,self()})), 2397: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{heir,false})), 2398: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,heir)), 2399: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{heir,false,"Data"})), 2400: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{false,self(),"Data"})), 2401: 2402: ?line ets:setopts(T,{protection,protected}), 2403: ?line ets:setopts(T,{protection,public}), 2404: ?line ets:setopts(T,{protection,private}), 2405: ?line ets:setopts(T,[{protection,protected}]), 2406: ?line ets:setopts(T,[{protection,public}]), 2407: ?line ets:setopts(T,[{protection,private}]), 2408: 2409: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{protection})), 2410: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{protection,false})), 2411: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,{protection,private,false})), 2412: ?line {'EXIT',{badarg,_}} = (catch ets:setopts(T,protection)), 2413: ?line ets:delete(T), 2414: unlink(Heir), 2415: exit(Heir, bang), 2416: ok. 2417: 2418: bad_table(doc) -> ["All kinds of operations with bad table argument"]; 2419: bad_table(suite) -> []; 2420: bad_table(Config) when is_list(Config) -> 2421: 2422: %% Open and close disk_log to stabilize etsmem. 2423: Name = make_ref(), 2424: ?line File = filename:join([?config(priv_dir, Config),"bad_table.dummy"]), 2425: ?line {ok, Name} = disk_log:open([{name, Name}, {file, File}]), 2426: ?line disk_log:close(Name), 2427: file:delete(File), 2428: 2429: ?line EtsMem = etsmem(), 2430: 2431: repeat_for_opts(fun(Opts) -> bad_table_do(Opts,File) end, 2432: [write_concurrency, all_types]), 2433: ?line verify_etsmem(EtsMem), 2434: ok. 2435: 2436: bad_table_do(Opts, DummyFile) -> 2437: Parent = self(), 2438: {Pid,Mref} = my_spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]), 2439: Priv = ets_new(priv,[private | Opts]), 2440: ets_new(prot,[protected,named_table | Opts]), 2441: Prot = ets_new(prot,[protected | Opts]), 2442: Parent ! {self(),Priv,Prot}, 2443: die_please = receive_any() 2444: end, 2445: [link, monitor]), 2446: {Pid,Priv,Prot} = receive_any(), 2447: MatchSpec = {{key,'_'}, [], ['$$']}, 2448: Fun = fun(X,_) -> X end, 2449: OpList = [{delete,[key],update}, 2450: {delete_all_objects,[],update}, 2451: {delete_object,[{key,data}],update}, 2452: {first,[],read}, 2453: {foldl,[Fun, 0], read, tabarg_last}, 2454: {foldr,[Fun, 0], read, tabarg_last}, 2455: %%{from_dets,[DetsTab], update}, 2456: {give_away,[Pid, data], update}, 2457: %%{info, [], read}, 2458: %%{info, [safe_fixed], read}, 2459: %%{init_table,[Name, InitFun],update}, 2460: {insert, [{key,data}], update}, 2461: {insert_new, [{key,data}], update}, 2462: {insert_new, [[{key,data},{other,data}]], update}, 2463: {last, [], read}, 2464: {lookup, [key], read}, 2465: {lookup_element, [key, 2], read}, 2466: {match, [{}], read}, 2467: {match, [{},17], read}, 2468: {match_delete, [{}], update}, 2469: {match_object, [{}], read}, 2470: {match_object, [{},17], read}, 2471: {member,[key], read}, 2472: {next, [key], read}, 2473: {prev, [key], read}, 2474: {rename, [new_name], update}, 2475: {safe_fixtable, [true], read}, 2476: {select,[MatchSpec], read}, 2477: {select,[MatchSpec,17], read}, 2478: {select_count,[MatchSpec], read}, 2479: {select_delete,[MatchSpec], update}, 2480: {setopts, [{heir,none}], update}, 2481: {slot, [0], read}, 2482: {tab2file, [DummyFile], read, {return,{error,badtab}}}, 2483: {tab2file, [DummyFile,[]], read, {return,{error,badtab}}}, 2484: {tab2list, [], read}, 2485: %%{table,[], read}, 2486: %%{to_dets, [DetsTab], read}, 2487: {update_counter,[key,1], update}, 2488: {update_element,[key,{2,new_data}], update} 2489: ], 2490: Info = {Opts, Priv, Prot}, 2491: lists:foreach(fun(Op) -> bad_table_op(Info, Op) end, 2492: OpList), 2493: Pid ! die_please, 2494: {'DOWN', Mref, process, Pid, normal} = receive_any(), 2495: ok. 2496: 2497: bad_table_op({Opts,Priv,Prot}, Op) -> 2498: %%io:format("Doing Op=~p on ~p's\n",[Op,Type]), 2499: T1 = ets_new(noname,Opts), 2500: bad_table_call(noname,Op), 2501: ets:delete(T1), 2502: bad_table_call(T1,Op), 2503: T2 = ets_new(named,[named_table | Opts]), 2504: ets:delete(T2), 2505: bad_table_call(named,Op), 2506: bad_table_call(T2,Op), 2507: bad_table_call(priv,Op), 2508: bad_table_call(Priv,Op), 2509: case element(3,Op) of 2510: update -> 2511: bad_table_call(prot,Op), 2512: bad_table_call(Prot,Op); 2513: read -> ok 2514: end. 2515: 2516: bad_table_call(T,{F,Args,_}) -> 2517: ?line {'EXIT',{badarg,_}} = (catch apply(ets, F, [T|Args])); 2518: bad_table_call(T,{F,Args,_,tabarg_last}) -> 2519: ?line {'EXIT',{badarg,_}} = (catch apply(ets, F, Args++[T])); 2520: bad_table_call(T,{F,Args,_,{return,Return}}) -> 2521: try 2522: ?line Return = apply(ets, F, [T|Args]) 2523: catch 2524: error:badarg -> ok 2525: end. 2526: 2527: 2528: rename(doc) -> 2529: ["Check rename of ets tables"]; 2530: rename(suite) -> 2531: []; 2532: rename(Config) when is_list(Config) -> 2533: repeat_for_opts(rename_do, [write_concurrency, all_types]). 2534: 2535: rename_do(Opts) -> 2536: ?line EtsMem = etsmem(), 2537: ets_new(foobazz,[named_table, public | Opts]), 2538: ets:insert(foobazz,{foo,bazz}), 2539: ungermanbazz = ets:rename(foobazz,ungermanbazz), 2540: {'EXIT',{badarg, _}} = (catch ets:lookup(foobazz,foo)), 2541: [{foo,bazz}] = ets:lookup(ungermanbazz,foo), 2542: {'EXIT',{badarg,_}} = (catch ets:rename(ungermanbazz,"no atom")), 2543: ets:delete(ungermanbazz), 2544: ?line verify_etsmem(EtsMem). 2545: 2546: rename_unnamed(doc) -> 2547: ["Check rename of unnamed ets table"]; 2548: rename_unnamed(suite) -> 2549: []; 2550: rename_unnamed(Config) when is_list(Config) -> 2551: repeat_for_opts(rename_unnamed_do,[write_concurrency,all_types]). 2552: 2553: rename_unnamed_do(Opts) -> 2554: ?line EtsMem = etsmem(), 2555: ?line Tab = ets_new(bonkz,[public | Opts]), 2556: ?line {'EXIT',{badarg, _}} = (catch ets:insert(bonkz,{foo,bazz})), 2557: ?line bonkz = ets:info(Tab, name), 2558: ?line Tab = ets:rename(Tab, tjabonkz), 2559: ?line {'EXIT',{badarg, _}} = (catch ets:insert(tjabonkz,{foo,bazz})), 2560: ?line tjabonkz = ets:info(Tab, name), 2561: ?line ets:delete(Tab), 2562: ?line verify_etsmem(EtsMem). 2563: 2564: evil_rename(doc) -> 2565: "Rename a table with many fixations, and at the same time delete it."; 2566: evil_rename(Config) when is_list(Config) -> 2567: ?line evil_rename_1(old_hash, new_hash, [public,named_table]), 2568: ?line EtsMem = etsmem(), 2569: ?line evil_rename_1(old_tree, new_tree, [public,ordered_set,named_table]), 2570: ?line verify_etsmem(EtsMem). 2571: 2572: evil_rename_1(Old, New, Flags) -> 2573: ?line process_flag(trap_exit, true), 2574: ?line Old = ets_new(Old, Flags), 2575: ?line Fixer = fun() -> ets:safe_fixtable(Old, true) end, 2576: ?line crazy_fixtable(15000, Fixer), 2577: ?line erlang:yield(), 2578: ?line New = ets:rename(Old, New), 2579: ?line erlang:yield(), 2580: ets:delete(New), 2581: ok. 2582: 2583: crazy_fixtable(N, Fixer) -> 2584: Dracula = ets_new(count_dracula, [public]), 2585: ets:insert(Dracula, {count,0}), 2586: SpawnFun = fun() -> 2587: Fixer(), 2588: case ets:update_counter(Dracula, count, 1) rem 15 of 2589: 0 -> evil_creater_destroyer(); 2590: _ -> erlang:hibernate(erlang, error, [dont_wake_me]) 2591: end 2592: end, 2593: crazy_fixtable_1(N, SpawnFun), 2594: crazy_fixtable_wait(N, Dracula), 2595: Dracula. 2596: 2597: crazy_fixtable_wait(N, Dracula) -> 2598: case ets:lookup(Dracula, count) of 2599: [{count,N}] -> 2600: ets:delete(Dracula); 2601: Other -> 2602: io:format("~p\n", [Other]), 2603: receive after 10 -> ok end, 2604: crazy_fixtable_wait(N, Dracula) 2605: end. 2606: 2607: crazy_fixtable_1(0, _) -> 2608: ok; 2609: crazy_fixtable_1(N, Fun) -> 2610: spawn_link(Fun), 2611: crazy_fixtable_1(N-1, Fun). 2612: 2613: evil_creater_destroyer() -> 2614: T1 = evil_create_fixed_tab(), 2615: ets:delete(T1). 2616: 2617: evil_create_fixed_tab() -> 2618: T = ets_new(arne, [public]), 2619: ets:safe_fixtable(T, true), 2620: T. 2621: 2622: interface_equality(doc) -> 2623: ["Tests that the return values and errors are equal for set's and" 2624: " ordered_set's where applicable"]; 2625: interface_equality(suite) -> 2626: []; 2627: interface_equality(Config) when is_list(Config) -> 2628: repeat_for_opts(interface_equality_do). 2629: 2630: interface_equality_do(Opts) -> 2631: ?line EtsMem = etsmem(), 2632: ?line Set = ets_new(set,[set | Opts]), 2633: ?line OrderedSet = ets_new(ordered_set,[ordered_set | Opts]), 2634: ?line F = fun(X,T,FF) -> case X of 2635: 0 -> true; 2636: _ -> 2637: ets:insert(T, {X, 2638: integer_to_list(X), 2639: X rem 10}), 2640: FF(X-1,T,FF) 2641: end 2642: end, 2643: ?line F(100,Set,F), 2644: ?line F(100,OrderedSet,F), 2645: ?line equal_results(ets, insert, Set, OrderedSet, [{a,"a"}]), 2646: ?line equal_results(ets, insert, Set, OrderedSet, [{1,1,"1"}]), 2647: ?line equal_results(ets, lookup, Set, OrderedSet, [10]), 2648: ?line equal_results(ets, lookup, Set, OrderedSet, [1000]), 2649: ?line equal_results(ets, delete, Set, OrderedSet, [10]), 2650: ?line equal_results(ets, delete, Set, OrderedSet, [nott]), 2651: ?line equal_results(ets, lookup, Set, OrderedSet, [1000]), 2652: ?line equal_results(ets, insert, Set, OrderedSet, [10]), 2653: ?line equal_results(ets, next, Set, OrderedSet, ['$end_of_table']), 2654: ?line equal_results(ets, prev, Set, OrderedSet, ['$end_of_table']), 2655: ?line equal_results(ets, match, Set, OrderedSet, [{'_','_','_'}]), 2656: ?line equal_results(ets, match, Set, OrderedSet, [{'_','_','_','_'}]), 2657: ?line equal_results(ets, match, Set, OrderedSet, [{$3,$2,2}]), 2658: ?line equal_results(ets, match, Set, OrderedSet, ['_']), 2659: ?line equal_results(ets, match, Set, OrderedSet, ['$1']), 2660: ?line equal_results(ets, match, Set, OrderedSet, [{'_','$50',3}]), 2661: ?line equal_results(ets, match, Set, OrderedSet, [['_','$50',3]]), 2662: ?line equal_results(ets, match_delete, Set, OrderedSet, [{'_','_',4}]), 2663: ?line equal_results(ets, match_delete, Set, OrderedSet, [{'_','_',4}]), 2664: ?line equal_results(ets, match_object, Set, OrderedSet, [{'_','_',4}]), 2665: ?line equal_results(ets, match_object, Set, OrderedSet, [{'_','_',5}]), 2666: ?line equal_results(ets, match_object, Set, OrderedSet, [{'_','_',4}]), 2667: ?line equal_results(ets, match_object, Set, OrderedSet, ['_']), 2668: ?line equal_results(ets, match_object, Set, OrderedSet, ['$5011']), 2669: ?line equal_results(ets, match_delete, Set, OrderedSet, ['$20']), 2670: ?line equal_results(ets, lookup_element, Set, OrderedSet, [13,2]), 2671: ?line equal_results(ets, lookup_element, Set, OrderedSet, [13,4]), 2672: ?line equal_results(ets, lookup_element, Set, OrderedSet, [14,2]), 2673: ?line equal_results(ets, delete, Set, OrderedSet, []), 2674: ?line verify_etsmem(EtsMem). 2675: 2676: equal_results(M, F, FirstArg1, FirstArg2 ,ACommon) -> 2677: Res = maybe_sort((catch apply(M,F, [FirstArg1 | ACommon]))), 2678: Res = maybe_sort((catch apply(M,F,[FirstArg2 | ACommon]))). 2679: 2680: maybe_sort(L) when is_list(L) -> 2681: lists:sort(L); 2682: %maybe_sort({'EXIT',{Reason, [{Module, Function, _}|_]}}) -> 2683: % {'EXIT',{Reason, [{Module, Function, '_'}]}}; 2684: maybe_sort({'EXIT',{Reason, List}}) when is_list(List) -> 2685: {'EXIT',{Reason, lists:map(fun({Module, Function, _, _}) -> 2686: {Module, Function, '_'} 2687: end, 2688: List)}}; 2689: maybe_sort(Any) -> 2690: Any. 2691: 2692: ordered_match(doc) -> 2693: ["Test match, match_object and match_delete in ordered set's"]; 2694: ordered_match(suite) -> 2695: []; 2696: ordered_match(Config) when is_list(Config)-> 2697: repeat_for_opts(ordered_match_do). 2698: 2699: ordered_match_do(Opts) -> 2700: ?line EtsMem = etsmem(), 2701: ?line F = fun(X,T,FF) -> case X of 2702: 0 -> true; 2703: _ -> 2704: ets:insert(T, {X, 2705: integer_to_list(X), 2706: X rem 10, 2707: X rem 100, 2708: X rem 1000}), 2709: FF(X-1,T,FF) 2710: end 2711: end, 2712: ?line T1 = ets_new(xxx,[ordered_set| Opts]), 2713: ?line F(3000,T1,F), 2714: ?line [[3,3],[3,3],[3,3]] = ets:match(T1, {'_','_','$1','$2',3}), 2715: ?line F2 = fun(X,Rem,Res,FF) -> case X of 2716: 0 -> []; 2717: _ -> 2718: case X rem Rem of 2719: Res -> 2720: FF(X-1,Rem,Res,FF) ++ 2721: [{X, 2722: integer_to_list(X), 2723: X rem 10, 2724: X rem 100, 2725: X rem 1000}]; 2726: _ -> 2727: FF(X-1,Rem,Res,FF) 2728: end 2729: end 2730: end, 2731: ?line OL1 = F2(3000,100,2,F2), 2732: ?line OL1 = ets:match_object(T1, {'_','_','_',2,'_'}), 2733: ?line true = ets:match_delete(T1,{'_','_','_',2,'_'}), 2734: ?line [] = ets:match_object(T1, {'_','_','_',2,'_'}), 2735: ?line OL2 = F2(3000,100,3,F2), 2736: ?line OL2 = ets:match_object(T1, {'_','_','_',3,'_'}), 2737: ?line ets:delete(T1), 2738: ?line verify_etsmem(EtsMem). 2739: 2740: 2741: ordered(doc) -> 2742: ["Test basic functionality in ordered_set's."]; 2743: ordered(suite) -> 2744: []; 2745: ordered(Config) when is_list(Config) -> 2746: repeat_for_opts(ordered_do). 2747: 2748: ordered_do(Opts) -> 2749: ?line EtsMem = etsmem(), 2750: ?line T = ets_new(oset, [ordered_set | Opts]), 2751: ?line InsList = [ 2752: 25,26,27,28, 2753: 5,6,7,8, 2754: 21,22,23,24, 2755: 9,10,11,12, 2756: 1,2,3,4, 2757: 17,18,19,20, 2758: 13,14,15,16, 2759: 1 bsl 33 2760: ], 2761: ?line lists:foreach(fun(X) -> 2762: ets:insert(T,{X,integer_to_list(X)}) 2763: end, 2764: InsList), 2765: ?line IL2 = lists:map(fun(X) -> {X,integer_to_list(X)} end, InsList), 2766: ?line L1 = pick_all_forward(T), 2767: ?line L2 = pick_all_backwards(T), 2768: ?line S1 = lists:sort(IL2), 2769: ?line S2 = lists:reverse(lists:sort(IL2)), 2770: ?line S1 = L1, 2771: ?line S2 = L2, 2772: ?line [{1,"1"}] = ets:slot(T,0), 2773: ?line [{28,"28"}] = ets:slot(T,27), 2774: ?line [{1 bsl 33,_}] = ets:slot(T,28), 2775: ?line 27 = ets:prev(T,28), 2776: ?line [{7,"7"}] = ets:slot(T,6), 2777: ?line '$end_of_table' = ets:next(T,1 bsl 33), 2778: ?line [{12,"12"}] = ets:slot(T,11), 2779: ?line '$end_of_table' = ets:slot(T,29), 2780: ?line [{1,"1"}] = ets:slot(T,0), 2781: ?line 28 = ets:prev(T,1 bsl 33), 2782: ?line 1 = ets:next(T,0), 2783: ?line pick_all_forward(T), 2784: ?line [{7,"7"}] = ets:slot(T,6), 2785: ?line L2 = pick_all_backwards(T), 2786: ?line [{7,"7"}] = ets:slot(T,6), 2787: ?line ets:delete(T), 2788: ?line verify_etsmem(EtsMem). 2789: 2790: pick_all(_T,'$end_of_table',_How) -> 2791: []; 2792: pick_all(T,Last,How) -> 2793: ?line This = case How of 2794: next -> 2795: ?line ets:next(T,Last); 2796: prev -> 2797: ?line ets:prev(T,Last) 2798: end, 2799: ?line [LastObj] = ets:lookup(T,Last), 2800: ?line [LastObj | pick_all(T,This,How)]. 2801: 2802: pick_all_forward(T) -> 2803: ?line pick_all(T,ets:first(T),next). 2804: pick_all_backwards(T) -> 2805: ?line pick_all(T,ets:last(T),prev). 2806: 2807: 2808: 2809: setbag(doc) -> ["Small test case for both set and bag type ets tables."]; 2810: setbag(suite) -> []; 2811: setbag(Config) when is_list(Config) -> 2812: ?line EtsMem = etsmem(), 2813: ?line Set = ets_new(set,[set]), 2814: ?line Bag = ets_new(bag,[bag]), 2815: ?line Key = {foo,bar}, 2816: 2817: %% insert some value 2818: ?line ets:insert(Set,{Key,val1}), 2819: ?line ets:insert(Bag,{Key,val1}), 2820: 2821: %% insert new value for same key again 2822: ?line ets:insert(Set,{Key,val2}), 2823: ?line ets:insert(Bag,{Key,val2}), 2824: 2825: %% check 2826: ?line [{Key,val2}] = ets:lookup(Set,Key), 2827: ?line [{Key,val1},{Key,val2}] = ets:lookup(Bag,Key), 2828: 2829: true = ets:delete(Set), 2830: true = ets:delete(Bag), 2831: ?line verify_etsmem(EtsMem). 2832: 2833: badnew(doc) -> 2834: ["Test case to check proper return values for illegal ets_new() calls."]; 2835: badnew(suite) -> []; 2836: badnew(Config) when is_list(Config) -> 2837: ?line EtsMem = etsmem(), 2838: ?line {'EXIT',{badarg,_}} = (catch ets_new(12,[])), 2839: ?line {'EXIT',{badarg,_}} = (catch ets_new({a,b},[])), 2840: ?line {'EXIT',{badarg,_}} = (catch ets_new(name,[foo])), 2841: ?line {'EXIT',{badarg,_}} = (catch ets_new(name,{bag})), 2842: ?line {'EXIT',{badarg,_}} = (catch ets_new(name,bag)), 2843: ?line verify_etsmem(EtsMem). 2844: 2845: verybadnew(doc) -> 2846: ["Test case to check that a not well formed list does not crash the " 2847: "emulator. OTP-2314 "]; 2848: verybadnew(suite) -> []; 2849: verybadnew(Config) when is_list(Config) -> 2850: ?line EtsMem = etsmem(), 2851: ?line {'EXIT',{badarg,_}} = (catch ets_new(verybad,[set|protected])), 2852: ?line verify_etsmem(EtsMem). 2853: 2854: named(doc) -> ["Small check to see if named tables work."]; 2855: named(suite) -> []; 2856: named(Config) when is_list(Config) -> 2857: ?line EtsMem = etsmem(), 2858: ?line Tab = make_table(foo, 2859: [named_table], 2860: [{key,val}]), 2861: ?line [{key,val}] = ets:lookup(foo,key), 2862: ?line true = ets:delete(Tab), 2863: ?line verify_etsmem(EtsMem). 2864: 2865: keypos2(doc) -> ["Test case to check if specified keypos works."]; 2866: keypos2(suite) -> []; 2867: keypos2(Config) when is_list(Config) -> 2868: ?line EtsMem = etsmem(), 2869: ?line Tab = make_table(foo, 2870: [set,{keypos,2}], 2871: [{val,key}, {val2,key}]), 2872: ?line [{val2,key}] = ets:lookup(Tab,key), 2873: ?line true = ets:delete(Tab), 2874: ?line verify_etsmem(EtsMem). 2875: 2876: privacy(doc) -> 2877: ["Privacy check. Check that a named(public/private/protected) table " 2878: "cannot be read by", 2879: "the wrong process(es)."]; 2880: privacy(suite) -> []; 2881: privacy(Config) when is_list(Config) -> 2882: repeat_for_opts(privacy_do). 2883: 2884: privacy_do(Opts) -> 2885: ?line EtsMem = etsmem(), 2886: ?line process_flag(trap_exit,true), 2887: ?line Owner = my_spawn_link(?MODULE,privacy_owner,[self(),Opts]), 2888: receive 2889: {'EXIT',Owner,Reason} -> 2890: ?line exit({privacy_test,Reason}); 2891: ok -> 2892: ok 2893: end, 2894: 2895: privacy_check(pub,prot,priv), 2896: 2897: Owner ! {shift,1,{pub,prot,priv}}, 2898: receive {Pub1,Prot1,Priv1} -> ok end, 2899: privacy_check(Pub1,Prot1,Priv1), 2900: 2901: Owner ! {shift,2,{Pub1,Prot1,Priv1}}, 2902: receive {Pub2,Prot2,Priv2} -> ok end, 2903: privacy_check(Pub2,Prot2,Priv2), 2904: 2905: Owner ! {shift,0,{Pub2,Prot2,Priv2}}, 2906: receive {Pub2,Prot2,Priv2} -> ok end, 2907: privacy_check(Pub2,Prot2,Priv2), 2908: 2909: Owner ! die, 2910: receive {'EXIT',Owner,_} -> ok end, 2911: ?line verify_etsmem(EtsMem). 2912: 2913: privacy_check(Pub,Prot,Priv) -> 2914: %% check read rights 2915: ?line [] = ets:lookup(Pub, foo), 2916: ?line [] = ets:lookup(Prot,foo), 2917: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(Priv,foo)), 2918: 2919: %% check write rights 2920: ?line true = ets:insert(Pub, {1,foo}), 2921: ?line {'EXIT',{badarg,_}} = (catch ets:insert(Prot,{2,foo})), 2922: ?line {'EXIT',{badarg,_}} = (catch ets:insert(Priv,{3,foo})), 2923: 2924: %% check that it really wasn't written, either 2925: ?line [] = ets:lookup(Prot,foo). 2926: 2927: privacy_owner(Boss, Opts) -> 2928: ets_new(pub, [public,named_table | Opts]), 2929: ets_new(prot,[protected,named_table | Opts]), 2930: ets_new(priv,[private,named_table | Opts]), 2931: Boss ! ok, 2932: privacy_owner_loop(Boss). 2933: 2934: privacy_owner_loop(Boss) -> 2935: receive 2936: {shift,N,Pub_Prot_Priv} -> 2937: {Pub,Prot,Priv} = rotate_tuple(Pub_Prot_Priv, N), 2938: 2939: ets:setopts(Pub,{protection,public}), 2940: ets:setopts(Prot,{protection,protected}), 2941: ets:setopts(Priv,{protection,private}), 2942: Boss ! {Pub,Prot,Priv}, 2943: privacy_owner_loop(Boss); 2944: 2945: die -> ok 2946: end. 2947: 2948: rotate_tuple(Tuple, 0) -> 2949: Tuple; 2950: rotate_tuple(Tuple, N) -> 2951: [H|T] = tuple_to_list(Tuple), 2952: rotate_tuple(list_to_tuple(T ++ [H]), N-1). 2953: 2954: 2955: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2956: 2957: 2958: empty(doc) -> 2959: ["Check lookup in an empty table and lookup of a non-existing key"]; 2960: empty(suite) -> []; 2961: empty(Config) when is_list(Config) -> 2962: repeat_for_opts(empty_do). 2963: 2964: empty_do(Opts) -> 2965: ?line EtsMem = etsmem(), 2966: ?line Tab = ets_new(foo,Opts), 2967: ?line [] = ets:lookup(Tab,key), 2968: ?line true = ets:insert(Tab,{key2,val}), 2969: ?line [] = ets:lookup(Tab,key), 2970: ?line true = ets:delete(Tab), 2971: ?line verify_etsmem(EtsMem). 2972: 2973: badinsert(doc) -> 2974: ["Check proper return values for illegal insert operations."]; 2975: badinsert(suite) -> []; 2976: badinsert(Config) when is_list(Config) -> 2977: repeat_for_opts(badinsert_do). 2978: 2979: badinsert_do(Opts) -> 2980: ?line EtsMem = etsmem(), 2981: ?line {'EXIT',{badarg,_}} = (catch ets:insert(foo,{key,val})), 2982: 2983: ?line Tab = ets_new(foo,Opts), 2984: ?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,{})), 2985: 2986: ?line Tab3 = ets_new(foo,[{keypos,3}| Opts]), 2987: ?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab3,{a,b})), 2988: 2989: ?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,[key,val2])), 2990: ?line true = ets:delete(Tab), 2991: ?line true = ets:delete(Tab3), 2992: ?line verify_etsmem(EtsMem). 2993: 2994: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2995: 2996: 2997: time_lookup(doc) -> ["Lookup timing."]; 2998: time_lookup(suite) -> []; 2999: time_lookup(Config) when is_list(Config) -> 3000: %% just for timing, really 3001: ?line EtsMem = etsmem(), 3002: Values = repeat_for_opts(time_lookup_do), 3003: ?line verify_etsmem(EtsMem), 3004: ?line {comment,lists:flatten(io_lib:format( 3005: "~p ets lookups/s",[Values]))}. 3006: 3007: time_lookup_do(Opts) -> 3008: ?line Tab = ets_new(foo,Opts), 3009: ?line fill_tab(Tab,foo), 3010: ?line ets:insert(Tab,{{a,key},foo}), 3011: ?line {Time,_} = ?t:timecall(test_server,do_times, 3012: [10000,ets,lookup,[Tab,{a,key}]]), 3013: ?line true = ets:delete(Tab), 3014: round(10000 / Time). % lookups/s 3015: 3016: badlookup(doc) -> 3017: ["Check proper return values from bad lookups in existing/non existing " 3018: " ets tables"]; 3019: badlookup(suite) -> []; 3020: badlookup(Config) when is_list(Config) -> 3021: ?line EtsMem = etsmem(), 3022: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(foo,key)), 3023: ?line Tab = ets_new(foo,[]), 3024: ?line ets:delete(Tab), 3025: ?line {'EXIT',{badarg,_}} = (catch ets:lookup(Tab,key)), 3026: ?line verify_etsmem(EtsMem). 3027: 3028: lookup_order(doc) -> ["Test that lookup returns objects in order of insertion for bag and dbag."]; 3029: lookup_order(suite) -> []; 3030: lookup_order(Config) when is_list(Config) -> 3031: EtsMem = etsmem(), 3032: repeat_for_opts(lookup_order_do, [write_concurrency,[bag,duplicate_bag]]), 3033: ?line verify_etsmem(EtsMem), 3034: ok. 3035: 3036: lookup_order_do(Opts) -> 3037: lookup_order_2(Opts, false), 3038: lookup_order_2(Opts, true). 3039: 3040: lookup_order_2(Opts, Fixed) -> 3041: io:format("Opts=~p Fixed=~p\n",[Opts,Fixed]), 3042: 3043: A = 1, B = 2, C = 3, 3044: ABC = [A,B,C], 3045: Pair = [{A,B},{B,A},{A,C},{C,A},{B,C},{C,B}], 3046: Combos = [{D1,D2,D3} || D1<-ABC, D2<-Pair, D3<-Pair], 3047: lists:foreach(fun({D1,{D2a,D2b},{D3a,D3b}}) -> 3048: T = ets_new(foo,Opts), 3049: case Fixed of 3050: true -> ets:safe_fixtable(T,true); 3051: false -> ok 3052: end, 3053: S10 = {T,[],key}, 3054: S20 = check_insert(S10,A), 3055: S30 = check_insert(S20,B), 3056: S40 = check_insert(S30,C), 3057: S50 = check_delete(S40,D1), 3058: S55 = check_insert(S50,D1), 3059: S60 = check_insert(S55,D1), 3060: S70 = check_delete(S60,D2a), 3061: S80 = check_delete(S70,D2b), 3062: S90 = check_insert(S80,D2a), 3063: SA0 = check_delete(S90,D3a), 3064: SB0 = check_delete(SA0,D3b), 3065: check_insert_new(SB0,D3b), 3066: 3067: true = ets:delete(T) 3068: end, 3069: Combos). 3070: 3071: 3072: check_insert({T,List0,Key},Val) -> 3073: %%io:format("insert ~p into ~p\n",[Val,List0]), 3074: ets:insert(T,{Key,Val}), 3075: List1 = case (ets:info(T,type) =:= bag andalso 3076: lists:member({Key,Val},List0)) of 3077: true -> List0; 3078: false -> [{Key,Val} | List0] 3079: end, 3080: check_check({T,List1,Key}). 3081: 3082: check_insert_new({T,List0,Key},Val) -> 3083: %%io:format("insert_new ~p into ~p\n",[Val,List0]), 3084: Ret = ets:insert_new(T,{Key,Val}), 3085: ?line Ret = (List0 =:= []), 3086: List1 = case Ret of 3087: true -> [{Key,Val}]; 3088: false -> List0 3089: end, 3090: check_check({T,List1,Key}). 3091: 3092: 3093: check_delete({T,List0,Key},Val) -> 3094: %%io:format("delete ~p from ~p\n",[Val,List0]), 3095: ets:delete_object(T,{Key,Val}), 3096: List1 = lists:filter(fun(Obj) -> Obj =/= {Key,Val} end, 3097: List0), 3098: check_check({T,List1,Key}). 3099: 3100: check_check(S={T,List,Key}) -> 3101: case lists:reverse(ets:lookup(T,Key)) of 3102: List -> ok; 3103: ETS -> io:format("check failed:\nETS: ~p\nCHK: ~p\n", [ETS,List]), 3104: ?t:fail("Invalid return value from ets:lookup") 3105: end, 3106: ?line Items = ets:info(T,size), 3107: ?line Items = length(List), 3108: S. 3109: 3110: 3111: 3112: fill_tab(Tab,Val) -> 3113: ?line ets:insert(Tab,{key,Val}), 3114: ?line ets:insert(Tab,{{a,144},Val}), 3115: ?line ets:insert(Tab,{{a,key2},Val}), 3116: ?line ets:insert(Tab,{14,Val}), 3117: ok. 3118: 3119: 3120: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3121: 3122: 3123: lookup_element_mult(doc) -> ["Multiple return elements (OTP-2386)"]; 3124: lookup_element_mult(suite) -> []; 3125: lookup_element_mult(Config) when is_list(Config) -> 3126: repeat_for_opts(lookup_element_mult_do). 3127: 3128: lookup_element_mult_do(Opts) -> 3129: ?line EtsMem = etsmem(), 3130: ?line T = ets_new(service, [bag, {keypos, 2} | Opts]), 3131: ?line D = lists:reverse(lem_data()), 3132: ?line lists:foreach(fun(X) -> ets:insert(T, X) end, D), 3133: ?line ok = lem_crash_3(T), 3134: ?line ets:insert(T, {0, "heap_key"}), 3135: ?line ets:lookup_element(T, "heap_key", 2), 3136: ?line true = ets:delete(T), 3137: ?line verify_etsmem(EtsMem). 3138: 3139: lem_data() -> 3140: [ 3141: {service,'eddie2@boromir',{150,236,14,103},httpd88,self()}, 3142: {service,'eddie2@boromir',{150,236,14,103},httpd80,self()}, 3143: {service,'eddie3@boromir',{150,236,14,107},httpd88,self()}, 3144: {service,'eddie3@boromir',{150,236,14,107},httpd80,self()}, 3145: {service,'eddie4@boromir',{150,236,14,108},httpd88,self()} 3146: ]. 3147: 3148: lem_crash(T) -> 3149: L = ets:lookup_element(T, 'eddie2@boromir', 3), 3150: {erlang:phash(L, 256), L}. 3151: 3152: lem_crash_3(T) -> 3153: lem_crash(T), 3154: io:format("Survived once~n"), 3155: lem_crash(T), 3156: io:format("Survived twice~n"), 3157: lem_crash(T), 3158: io:format("Survived all!~n"), 3159: ok. 3160: 3161: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3162: 3163: 3164: delete_elem(doc) -> 3165: ["Check delete of an element inserted in a `filled' table."]; 3166: delete_elem(suite) -> []; 3167: delete_elem(Config) when is_list(Config) -> 3168: repeat_for_opts(delete_elem_do, [write_concurrency, all_types]). 3169: 3170: delete_elem_do(Opts) -> 3171: ?line EtsMem = etsmem(), 3172: ?line Tab = ets_new(foo,Opts), 3173: ?line fill_tab(Tab,foo), 3174: ?line ets:insert(Tab,{{b,key},foo}), 3175: ?line ets:insert(Tab,{{c,key},foo}), 3176: ?line true = ets:delete(Tab,{b,key}), 3177: ?line [] = ets:lookup(Tab,{b,key}), 3178: ?line [{{c,key},foo}] = ets:lookup(Tab,{c,key}), 3179: ?line true = ets:delete(Tab), 3180: ?line verify_etsmem(EtsMem). 3181: 3182: delete_tab(doc) -> 3183: ["Check that ets:delete() works and releases the name of the deleted " 3184: "table."]; 3185: delete_tab(suite) -> []; 3186: delete_tab(Config) when is_list(Config) -> 3187: repeat_for_opts(delete_tab_do,[write_concurrency,all_types]). 3188: 3189: delete_tab_do(Opts) -> 3190: Name = foo, 3191: ?line EtsMem = etsmem(), 3192: ?line Name = ets_new(Name, [named_table | Opts]), 3193: ?line true = ets:delete(foo), 3194: %% The name should be available again. 3195: ?line Name = ets_new(Name, [named_table | Opts]), 3196: ?line true = ets:delete(Name), 3197: ?line verify_etsmem(EtsMem). 3198: 3199: delete_large_tab(doc) -> 3200: "Check that ets:delete/1 works and that other processes can run."; 3201: delete_large_tab(Config) when is_list(Config) -> 3202: ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)], 3203: ?line EtsMem = etsmem(), 3204: repeat_for_opts(fun(Opts) -> delete_large_tab_do(Opts,Data) end), 3205: ?line verify_etsmem(EtsMem). 3206: 3207: delete_large_tab_do(Opts,Data) -> 3208: ?line delete_large_tab_1(foo_hash, Opts, Data, false), 3209: ?line delete_large_tab_1(foo_tree, [ordered_set | Opts], Data, false), 3210: ?line delete_large_tab_1(foo_hash, Opts, Data, true). 3211: 3212: 3213: delete_large_tab_1(Name, Flags, Data, Fix) -> 3214: ?line Tab = ets_new(Name, Flags), 3215: ?line ets:insert(Tab, Data), 3216: 3217: case Fix of 3218: false -> ok; 3219: true -> 3220: ?line true = ets:safe_fixtable(Tab, true), 3221: ?line lists:foreach(fun({K,_}) -> ets:delete(Tab, K) end, Data) 3222: end, 3223: 3224: {priority, Prio} = process_info(self(), priority), 3225: ?line Deleter = self(), 3226: ?line [SchedTracer] 3227: = start_loopers(1, 3228: Prio, 3229: fun (SC) -> 3230: receive 3231: {trace, Deleter, out, _} -> 3232: undefined = ets:info(Tab), 3233: SC+1; 3234: {trace, 3235: Deleter, 3236: register, 3237: delete_large_tab_done_marker}-> 3238: Deleter ! {schedule_count, SC}, 3239: exit(normal); 3240: _ -> 3241: SC 3242: end 3243: end, 3244: 0), 3245: SchedTracerMon = monitor(process, SchedTracer), 3246: ?line Loopers = start_loopers(erlang:system_info(schedulers), 3247: Prio, 3248: fun (_) -> erlang:yield() end, 3249: ok), 3250: ?line erlang:yield(), 3251: ?line 1 = erlang:trace(self(),true,[running,procs,{tracer,SchedTracer}]), 3252: ?line true = ets:delete(Tab), 3253: %% The register stuff is just a trace marker 3254: ?line true = register(delete_large_tab_done_marker, self()), 3255: ?line true = unregister(delete_large_tab_done_marker), 3256: ?line undefined = ets:info(Tab), 3257: ?line ok = stop_loopers(Loopers), 3258: ?line receive 3259: {schedule_count, N} -> 3260: ?line io:format("~s: context switches: ~p", [Name,N]), 3261: if 3262: N >= 5 -> ?line ok; 3263: true -> ?line ?t:fail() 3264: end 3265: end, 3266: receive {'DOWN',SchedTracerMon,process,SchedTracer,_} -> ok end, 3267: ok. 3268: 3269: delete_large_named_table(doc) -> 3270: "Delete a large name table and try to create a new table with the same name in another process."; 3271: delete_large_named_table(Config) when is_list(Config) -> 3272: ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)], 3273: ?line EtsMem = etsmem(), 3274: repeat_for_opts(fun(Opts) -> delete_large_named_table_do(Opts,Data) end), 3275: ?line verify_etsmem(EtsMem), 3276: ok. 3277: 3278: delete_large_named_table_do(Opts,Data) -> 3279: ?line delete_large_named_table_1(foo_hash, [named_table | Opts], Data, false), 3280: ?line delete_large_named_table_1(foo_tree, [ordered_set,named_table | Opts], Data, false), 3281: ?line delete_large_named_table_1(foo_hash, [named_table | Opts], Data, true). 3282: 3283: delete_large_named_table_1(Name, Flags, Data, Fix) -> 3284: ?line Tab = ets_new(Name, Flags), 3285: ?line ets:insert(Tab, Data), 3286: 3287: case Fix of 3288: false -> ok; 3289: true -> 3290: ?line true = ets:safe_fixtable(Tab, true), 3291: ?line lists:foreach(fun({K,_}) -> ets:delete(Tab, K) end, Data) 3292: end, 3293: Parent = self(), 3294: {Pid, MRef} = my_spawn_opt(fun() -> 3295: receive 3296: ets_new -> 3297: ets_new(Name, [named_table]) 3298: end 3299: end, 3300: [link, monitor]), 3301: true = ets:delete(Tab), 3302: Pid ! ets_new, 3303: receive {'DOWN',MRef,process,Pid,_} -> ok end, 3304: ok. 3305: 3306: evil_delete(doc) -> 3307: "Delete a large table, and kill the process during the delete."; 3308: evil_delete(Config) when is_list(Config) -> 3309: ?line Data = [{I,I*I} || I <- lists:seq(1, 100000)], 3310: repeat_for_opts(fun(Opts) -> evil_delete_do(Opts,Data) end). 3311: 3312: evil_delete_do(Opts,Data) -> 3313: ?line EtsMem = etsmem(), 3314: ?line evil_delete_owner(foo_hash, Opts, Data, false), 3315: ?line verify_etsmem(EtsMem), 3316: ?line evil_delete_owner(foo_hash, Opts, Data, true), 3317: ?line verify_etsmem(EtsMem), 3318: ?line evil_delete_owner(foo_tree, [ordered_set | Opts], Data, false), 3319: ?line verify_etsmem(EtsMem), 3320: ?line TabA = evil_delete_not_owner(foo_hash, Opts, Data, false), 3321: ?line verify_etsmem(EtsMem), 3322: ?line TabB = evil_delete_not_owner(foo_hash, Opts, Data, true), 3323: ?line verify_etsmem(EtsMem), 3324: ?line TabC = evil_delete_not_owner(foo_tree, [ordered_set | Opts], Data, false), 3325: ?line verify_etsmem(EtsMem), 3326: ?line lists:foreach(fun(T) -> undefined = ets:info(T) end, 3327: [TabA,TabB,TabC]). 3328: 3329: evil_delete_not_owner(Name, Flags, Data, Fix) -> 3330: io:format("Not owner: ~p, fix = ~p", [Name,Fix]), 3331: ?line Tab = ets_new(Name, [public|Flags]), 3332: ?line ets:insert(Tab, Data), 3333: case Fix of 3334: false -> ok; 3335: true -> 3336: ?line true = ets:safe_fixtable(Tab, true), 3337: ?line lists:foreach(fun({K,_}) -> ets:delete(Tab, K) end, Data) 3338: end, 3339: ?line Pid = my_spawn(fun() -> 3340: P = my_spawn_link( 3341: fun() -> 3342: receive kill -> ok end, 3343: erlang:yield(), 3344: exit(kill_linked_processes_now) 3345: end), 3346: erlang:yield(), 3347: P ! kill, 3348: true = ets:delete(Tab) 3349: end), 3350: ?line Ref = erlang:monitor(process, Pid), 3351: ?line receive {'DOWN',Ref,_,_,_} -> ok end, 3352: Tab. 3353: 3354: evil_delete_owner(Name, Flags, Data, Fix) -> 3355: ?line Fun = fun() -> 3356: ?line Tab = ets_new(Name, [public|Flags]), 3357: ?line ets:insert(Tab, Data), 3358: case Fix of 3359: false -> ok; 3360: true -> 3361: ?line true = ets:safe_fixtable(Tab, true), 3362: ?line lists:foreach(fun({K,_}) -> 3363: ets:delete(Tab, K) 3364: end, Data) 3365: end, 3366: erlang:yield(), 3367: my_spawn_link(fun() -> 3368: erlang:yield(), 3369: exit(kill_linked_processes_now) 3370: end), 3371: true = ets:delete(Tab) 3372: end, 3373: ?line Pid = my_spawn(Fun), 3374: ?line Ref = erlang:monitor(process, Pid), 3375: ?line receive {'DOWN',Ref,_,_,_} -> ok end. 3376: 3377: 3378: exit_large_table_owner(doc) -> 3379: []; 3380: exit_large_table_owner(suite) -> 3381: []; 3382: exit_large_table_owner(Config) when is_list(Config) -> 3383: %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], 3384: ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok}; 3385: (I) -> Do({erlang:phash2(I, 16#ffffff),I}), 3386: {true, I+1} 3387: end, 1) 3388: end, 3389: ?line EtsMem = etsmem(), 3390: repeat_for_opts({exit_large_table_owner_do,{FEData,Config}}), 3391: ?line verify_etsmem(EtsMem). 3392: 3393: exit_large_table_owner_do(Opts,{FEData,Config}) -> 3394: ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 1, 1), 3395: ?line verify_rescheduling_exit(Config, FEData, Opts, false, 1, 1). 3396: 3397: exit_many_large_table_owner(doc) -> []; 3398: exit_many_large_table_owner(suite) -> []; 3399: exit_many_large_table_owner(Config) when is_list(Config) -> 3400: %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], 3401: ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok}; 3402: (I) -> Do({erlang:phash2(I, 16#ffffff),I}), 3403: {true, I+1} 3404: end, 1) 3405: end, 3406: ?line EtsMem = etsmem(), 3407: repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,FEData,Config) end), 3408: ?line verify_etsmem(EtsMem). 3409: 3410: exit_many_large_table_owner_do(Opts,FEData,Config) -> 3411: ?line verify_rescheduling_exit(Config, FEData, Opts, true, 1, 4), 3412: ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 1, 4). 3413: 3414: exit_many_tables_owner(doc) -> []; 3415: exit_many_tables_owner(suite) -> []; 3416: exit_many_tables_owner(Config) when is_list(Config) -> 3417: NoData = fun(_Do) -> ok end, 3418: ?line EtsMem = etsmem(), 3419: ?line verify_rescheduling_exit(Config, NoData, [named_table], false, 1000, 1), 3420: ?line verify_rescheduling_exit(Config, NoData, [named_table,{write_concurrency,true}], false, 1000, 1), 3421: ?line verify_etsmem(EtsMem). 3422: 3423: exit_many_many_tables_owner(doc) -> []; 3424: exit_many_many_tables_owner(suite) -> []; 3425: exit_many_many_tables_owner(Config) when is_list(Config) -> 3426: ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 50)], 3427: ?line FEData = fun(Do) -> lists:foreach(Do, Data) end, 3428: repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,FEData,Config) end). 3429: 3430: exit_many_many_tables_owner_do(Opts,FEData,Config) -> 3431: ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 200, 5), 3432: ?line verify_rescheduling_exit(Config, FEData, Opts, false, 200, 5), 3433: ?line wait_for_test_procs(), 3434: ?line EtsMem = etsmem(), 3435: ?line verify_rescheduling_exit(Config, FEData, Opts, true, 200, 5), 3436: ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 200, 5), 3437: ?line verify_etsmem(EtsMem). 3438: 3439: 3440: count_exit_sched(TP) -> 3441: receive 3442: {trace, TP, in_exiting, 0} -> 3443: count_exit_sched_out(TP, 1); 3444: {trace, TP, out_exiting, 0} -> 3445: count_exit_sched_in(TP, 1); 3446: {trace, TP, out_exited, 0} -> 3447: 0 3448: end. 3449: 3450: count_exit_sched_in(TP, N) -> 3451: receive 3452: {trace, TP, in_exiting, 0} -> 3453: count_exit_sched_out(TP, N); 3454: {trace, TP, _, _} = Msg -> 3455: exit({unexpected_trace_msg, Msg}) 3456: end. 3457: 3458: count_exit_sched_out(TP, N) -> 3459: receive 3460: {trace, TP, out_exiting, 0} -> 3461: count_exit_sched_in(TP, N+1); 3462: {trace, TP, out_exited, 0} -> 3463: N; 3464: {trace, TP, _, _} = Msg -> 3465: exit({unexpected_trace_msg, Msg}) 3466: end. 3467: 3468: vre_fix_tables(Tab) -> 3469: Parent = self(), 3470: Go = make_ref(), 3471: my_spawn_link(fun () -> 3472: true = ets:safe_fixtable(Tab, true), 3473: Parent ! Go, 3474: receive infinity -> ok end 3475: end), 3476: receive Go -> ok end, 3477: ok. 3478: 3479: verify_rescheduling_exit(Config, ForEachData, Flags, Fix, NOTabs, NOProcs) -> 3480: ?line NoFix = 5, 3481: ?line TestCase = atom_to_list(?config(test_case, Config)), 3482: ?line Parent = self(), 3483: ?line KillMe = make_ref(), 3484: ?line PFun = 3485: fun () -> 3486: repeat( 3487: fun () -> 3488: {A, B, C} = now(), 3489: ?line Name = list_to_atom( 3490: TestCase 3491: ++ "-" ++ integer_to_list(A) 3492: ++ "-" ++ integer_to_list(B) 3493: ++ "-" ++ integer_to_list(C)), 3494: Tab = ets_new(Name, Flags), 3495: ForEachData(fun(Data) -> ets:insert(Tab, Data) end), 3496: case Fix of 3497: false -> ok; 3498: true -> 3499: lists:foreach(fun (_) -> 3500: vre_fix_tables(Tab) 3501: end, 3502: lists:seq(1,NoFix)), 3503: KeyPos = ets:info(Tab,keypos), 3504: ForEachData(fun(Data) -> 3505: ets:delete(Tab, element(KeyPos,Data)) 3506: end) 3507: end 3508: end, 3509: NOTabs), 3510: Parent ! {KillMe, self()}, 3511: receive after infinity -> ok end 3512: end, 3513: ?line TPs = lists:map(fun (_) -> 3514: ?line TP = my_spawn_link(PFun), 3515: ?line 1 = erlang:trace(TP, true, [exiting]), 3516: TP 3517: end, 3518: lists:seq(1, NOProcs)), 3519: ?line lists:foreach(fun (TP) -> 3520: receive {KillMe, TP} -> ok end 3521: end, 3522: TPs), 3523: ?line LPs = start_loopers(erlang:system_info(schedulers), 3524: normal, 3525: fun (_) -> 3526: erlang:yield() 3527: end, 3528: ok), 3529: ?line lists:foreach(fun (TP) -> 3530: ?line unlink(TP), 3531: ?line exit(TP, bang) 3532: end, 3533: TPs), 3534: ?line lists:foreach(fun (TP) -> 3535: ?line XScheds = count_exit_sched(TP), 3536: ?line ?t:format("~p XScheds=~p~n", 3537: [TP, XScheds]), 3538: ?line true = XScheds >= 5 3539: end, 3540: TPs), 3541: ?line stop_loopers(LPs), 3542: ?line ok. 3543: 3544: 3545: 3546: table_leak(doc) -> 3547: "Make sure that slots for ets tables are cleared properly."; 3548: table_leak(Config) when is_list(Config) -> 3549: repeat_for_opts(fun(Opts) -> table_leak_1(Opts,20000) end). 3550: 3551: table_leak_1(_,0) -> ok; 3552: table_leak_1(Opts,N) -> 3553: ?line T = ets_new(fooflarf, Opts), 3554: ?line true = ets:delete(T), 3555: table_leak_1(Opts,N-1). 3556: 3557: baddelete(doc) -> 3558: ["Check proper return values for illegal delete operations."]; 3559: baddelete(suite) -> []; 3560: baddelete(Config) when is_list(Config) -> 3561: ?line EtsMem = etsmem(), 3562: ?line {'EXIT',{badarg,_}} = (catch ets:delete(foo)), 3563: ?line Tab = ets_new(foo,[]), 3564: ?line true = ets:delete(Tab), 3565: ?line {'EXIT',{badarg,_}} = (catch ets:delete(Tab)), 3566: ?line verify_etsmem(EtsMem). 3567: 3568: match_delete(doc) -> 3569: ["Check that match_delete works. Also tests tab2list function."]; 3570: match_delete(suite) -> []; 3571: match_delete(Config) when is_list(Config) -> 3572: ?line EtsMem = etsmem(), 3573: repeat_for_opts(match_delete_do,[write_concurrency,all_types]), 3574: ?line verify_etsmem(EtsMem). 3575: 3576: match_delete_do(Opts) -> 3577: ?line EtsMem = etsmem(), 3578: ?line Tab = ets_new(kad,Opts), 3579: ?line fill_tab(Tab,foo), 3580: ?line ets:insert(Tab,{{c,key},bar}), 3581: ?line _ = ets:match_delete(Tab,{'_',foo}), 3582: ?line [{{c,key},bar}] = ets:tab2list(Tab), 3583: ?line _ = ets:match_delete(Tab,'_'), 3584: ?line [] = ets:tab2list(Tab), 3585: ?line true = ets:delete(Tab), 3586: ?line verify_etsmem(EtsMem). 3587: 3588: match_delete3(doc) -> 3589: ["OTP-3005: check match_delete with constant argument."]; 3590: match_delete3(suite) -> []; 3591: match_delete3(Config) when is_list(Config) -> 3592: repeat_for_opts(match_delete3_do). 3593: 3594: match_delete3_do(Opts) -> 3595: ?line EtsMem = etsmem(), 3596: T = make_table(test, 3597: [duplicate_bag | Opts], 3598: [{aa,17}, 3599: {cA,1000}, 3600: {cA,17}, 3601: {cA,1000}, 3602: {aa,17}]), 3603: %% 'aa' and 'cA' have the same hash value in the current 3604: %% implementation. This causes the aa's to precede the cA's, to make 3605: %% the test more interesting. 3606: [{cA,1000},{cA,1000}] = ets:match_object(T, {'_', 1000}), 3607: ets:match_delete(T, {cA,1000}), 3608: [] = ets:match_object(T, {'_', 1000}), 3609: ets:delete(T), 3610: ?line verify_etsmem(EtsMem). 3611: 3612: 3613: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3614: 3615: firstnext(doc) -> ["Tests ets:first/1 & ets:next/2."]; 3616: firstnext(suite) -> []; 3617: firstnext(Config) when is_list(Config) -> 3618: repeat_for_opts(firstnext_do). 3619: 3620: firstnext_do(Opts) -> 3621: ?line EtsMem = etsmem(), 3622: ?line Tab = ets_new(foo,Opts), 3623: ?line [] = firstnext_collect(Tab,ets:first(Tab),[]), 3624: ?line fill_tab(Tab,foo), 3625: ?line Len = length(ets:tab2list(Tab)), 3626: ?line Len = length(firstnext_collect(Tab,ets:first(Tab),[])), 3627: ?line true = ets:delete(Tab), 3628: ?line verify_etsmem(EtsMem). 3629: 3630: firstnext_collect(_Tab,'$end_of_table',List) -> 3631: ?line List; 3632: firstnext_collect(Tab,Key,List) -> 3633: ?line firstnext_collect(Tab,ets:next(Tab,Key),[Key|List]). 3634: 3635: 3636: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3637: 3638: firstnext_concurrent(doc) -> "Tests ets:first/1 & ets:next/2."; 3639: firstnext_concurrent(Config) when is_list(Config) -> 3640: register(master, self()), 3641: ets_init(?MODULE, 20), 3642: [dynamic_go() || _ <- lists:seq(1, 2)], 3643: receive 3644: after 5000 -> ok 3645: end. 3646: 3647: ets_init(Tab, N) -> 3648: ets_new(Tab, [named_table,public,ordered_set]), 3649: cycle(Tab, lists:seq(1,N+1)). 3650: 3651: cycle(_Tab, [H|T]) when H > length(T)-> ok; 3652: cycle(Tab, L) -> 3653: ets:insert(Tab,list_to_tuple(L)), 3654: cycle(Tab, tl(L)++[hd(L)]). 3655: 3656: dynamic_go() -> my_spawn_link(fun dynamic_init/0). 3657: 3658: dynamic_init() -> [dyn_lookup(?MODULE) || _ <- lists:seq(1, 10)]. 3659: 3660: dyn_lookup(T) -> dyn_lookup(T, ets:first(T)). 3661: 3662: dyn_lookup(_T, '$end_of_table') -> []; 3663: dyn_lookup(T, K) -> 3664: NextKey=ets:next(T,K), 3665: case ets:next(T,K) of 3666: NextKey -> 3667: dyn_lookup(T, NextKey); 3668: NK -> 3669: io:fwrite("hmmm... ~p =/= ~p~n", [NextKey,NK]), 3670: exit(failed) 3671: end. 3672: 3673: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3674: 3675: slot(suite) -> []; 3676: slot(Config) when is_list(Config) -> 3677: repeat_for_opts(slot_do). 3678: 3679: slot_do(Opts) -> 3680: ?line EtsMem = etsmem(), 3681: ?line Tab = ets_new(foo,Opts), 3682: ?line fill_tab(Tab,foo), 3683: ?line Elts = ets:info(Tab,size), 3684: ?line Elts = slot_loop(Tab,0,0), 3685: ?line true = ets:delete(Tab), 3686: ?line verify_etsmem(EtsMem). 3687: 3688: slot_loop(Tab,SlotNo,EltsSoFar) -> 3689: ?line case ets:slot(Tab,SlotNo) of 3690: '$end_of_table' -> 3691: ?line {'EXIT',{badarg,_}} = 3692: (catch ets:slot(Tab,SlotNo+1)), 3693: ?line EltsSoFar; 3694: Elts -> 3695: ?line slot_loop(Tab,SlotNo+1,EltsSoFar+length(Elts)) 3696: end. 3697: 3698: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3699: 3700: 3701: match1(suite) -> []; 3702: match1(Config) when is_list(Config) -> 3703: repeat_for_opts(match1_do). 3704: 3705: match1_do(Opts) -> 3706: ?line EtsMem = etsmem(), 3707: ?line Tab = ets_new(foo,Opts), 3708: ?line fill_tab(Tab,foo), 3709: ?line [] = ets:match(Tab,{}), 3710: ?line ets:insert(Tab,{{one,4},4}), 3711: ?line ets:insert(Tab,{{one,5},5}), 3712: ?line ets:insert(Tab,{{two,4},4}), 3713: ?line ets:insert(Tab,{{two,5},6}), 3714: ?line case ets:match(Tab,{{one,'_'},'$0'}) of 3715: [[4],[5]] -> ok; 3716: [[5],[4]] -> ok 3717: end, 3718: ?line case ets:match(Tab,{{two,'$1'},'$0'}) of 3719: [[4,4],[6,5]] -> ok; 3720: [[6,5],[4,4]] -> ok 3721: end, 3722: ?line case ets:match(Tab,{{two,'$9'},'$4'}) of 3723: [[4,4],[6,5]] -> ok; 3724: [[6,5],[4,4]] -> ok 3725: end, 3726: ?line case ets:match(Tab,{{two,'$9'},'$22'}) of 3727: [[4,4],[5,6]] -> ok; 3728: [[5,6],[4,4]] -> ok 3729: end, 3730: ?line [[4]] = ets:match(Tab,{{two,'$0'},'$0'}), 3731: ?line Len = length(ets:match(Tab,'$0')), 3732: ?line Len = length(ets:match(Tab,'_')), 3733: ?line if Len > 4 -> ok end, 3734: ?line true = ets:delete(Tab), 3735: ?line verify_etsmem(EtsMem). 3736: 3737: match2(doc) -> ["Tests match with specified keypos bag table."]; 3738: match2(suite) -> []; 3739: match2(Config) when is_list(Config) -> 3740: repeat_for_opts(match2_do). 3741: 3742: match2_do(Opts) -> 3743: ?line EtsMem = etsmem(), 3744: ?line Tab = make_table(foobar, 3745: [bag, named_table, {keypos, 2} | Opts], 3746: [{value1, key1}, 3747: {value2_1, key2}, 3748: {value2_2, key2}, 3749: {value3_1, key3}, 3750: {value3_2, key3}, 3751: {value2_1, key2_wannabe}]), 3752: ?line case length(ets:match(Tab, '$1')) of 3753: 6 -> ok; 3754: _ -> ?t:fail("Length of matched list is wrong.") 3755: end, 3756: ?line [[value3_1],[value3_2]] = ets:match(Tab, {'$1', key3}), 3757: ?line [[key1]] = ets:match(Tab, {value1, '$1'}), 3758: ?line [[key2_wannabe],[key2]] = ets:match(Tab, {value2_1, '$2'}), 3759: ?line [] = ets:match(Tab,{'$1',nosuchkey}), 3760: ?line [] = ets:match(Tab,{'$1',kgY2}), % same hash as key2 3761: ?line [] = ets:match(Tab,{nosuchvalue,'$1'}), 3762: ?line true = ets:delete(Tab), 3763: ?line verify_etsmem(EtsMem). 3764: 3765: match_object(doc) -> ["Some ets:match_object test."]; 3766: match_object(suite) -> []; 3767: match_object(Config) when is_list(Config) -> 3768: repeat_for_opts(match_object_do). 3769: 3770: match_object_do(Opts) -> 3771: ?line EtsMem = etsmem(), 3772: ?line Tab = ets_new(foobar, Opts), 3773: ?line fill_tab(Tab, foo), 3774: ?line ets:insert(Tab, {{one, 4}, 4}), 3775: ?line ets:insert(Tab,{{one,5},5}), 3776: ?line ets:insert(Tab,{{two,4},4}), 3777: ?line ets:insert(Tab,{{two,5},6}), 3778: ?line case ets:match_object(Tab, {{one, '_'}, '$0'}) of 3779: [{{one,5},5},{{one,4},4}] -> ok; 3780: [{{one,4},4},{{one,5},5}] -> ok; 3781: _ -> ?t:fail("ets:match_object() returned something funny.") 3782: end, 3783: ?line case ets:match_object(Tab, {{two, '$1'}, '$0'}) of 3784: [{{two,5},6},{{two,4},4}] -> ok; 3785: [{{two,4},4},{{two,5},6}] -> ok; 3786: _ -> ?t:fail("ets:match_object() returned something funny.") 3787: end, 3788: ?line case ets:match_object(Tab, {{two, '$9'}, '$4'}) of 3789: [{{two,5},6},{{two,4},4}] -> ok; 3790: [{{two,4},4},{{two,5},6}] -> ok; 3791: _ -> ?t:fail("ets:match_object() returned something funny.") 3792: end, 3793: ?line case ets:match_object(Tab, {{two, '$9'}, '$22'}) of 3794: [{{two,5},6},{{two,4},4}] -> ok; 3795: [{{two,4},4},{{two,5},6}] -> ok; 3796: _ -> ?t:fail("ets:match_object() returned something funny.") 3797: end, 3798: % Check that unsucessful match returns an empty list. 3799: ?line [] = ets:match_object(Tab, {{three,'$0'}, '$92'}), 3800: % Check that '$0' equals '_'. 3801: Len = length(ets:match_object(Tab, '$0')), 3802: Len = length(ets:match_object(Tab, '_')), 3803: ?line if Len > 4 -> ok end, 3804: ?line true = ets:delete(Tab), 3805: ?line verify_etsmem(EtsMem). 3806: 3807: match_object2(suite) -> []; 3808: match_object2(doc) -> ["Tests that db_match_object does not generate " 3809: "a `badarg' when resuming a search with no " 3810: "previous matches."]; 3811: match_object2(Config) when is_list(Config) -> 3812: repeat_for_opts(match_object2_do). 3813: 3814: match_object2_do(Opts) -> 3815: ?line EtsMem = etsmem(), 3816: ?line Tab = ets_new(foo, [bag, {keypos, 2} | Opts]), 3817: ?line fill_tab2(Tab, 0, 13005), % match_db_object does 1000 3818: % elements per pass, might 3819: % change in the future. 3820: ?line case catch ets:match_object(Tab, {hej, '$1'}) of 3821: {'EXIT', _} -> 3822: ets:delete(Tab), 3823: ?t:fail("match_object EXIT:ed"); 3824: [] -> 3825: io:format("Nothing matched."); 3826: List -> 3827: io:format("Matched:~p~n",[List]) 3828: end, 3829: ets:delete(Tab), 3830: ?line verify_etsmem(EtsMem). 3831: 3832: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3833: 3834: 3835: tab2list(doc) -> ["Tests tab2list (OTP-3319)"]; 3836: tab2list(suite) -> []; 3837: tab2list(Config) when is_list(Config) -> 3838: ?line EtsMem = etsmem(), 3839: ?line Tab = make_table(foo, 3840: [ordered_set], 3841: [{a,b}, {c,b}, {b,b}, {a,c}]), 3842: ?line [{a,c},{b,b},{c,b}] = ets:tab2list(Tab), 3843: ?line true = ets:delete(Tab), 3844: ?line verify_etsmem(EtsMem). 3845: 3846: misc1(doc) -> ["Simple general small test. ", 3847: "If this fails, ets is in really bad shape."]; 3848: misc1(suite) -> []; 3849: misc1(Config) when is_list(Config) -> 3850: repeat_for_opts(misc1_do). 3851: 3852: misc1_do(Opts) -> 3853: ?line EtsMem = etsmem(), 3854: ?line Tab = ets_new(foo,Opts), 3855: ?line true = lists:member(Tab,ets:all()), 3856: ?line ets:delete(Tab), 3857: ?line false = lists:member(Tab,ets:all()), 3858: ?line case catch ets:delete(Tab) of 3859: {'EXIT',_Reason} -> 3860: ?line verify_etsmem(EtsMem); 3861: true -> 3862: ?t:fail("Delete of nonexisting table returned `true'.") 3863: end, 3864: ok. 3865: 3866: safe_fixtable(doc) -> ["Check the safe_fixtable function."]; 3867: safe_fixtable(suite) -> []; 3868: safe_fixtable(Config) when is_list(Config) -> 3869: repeat_for_opts(safe_fixtable_do). 3870: 3871: safe_fixtable_do(Opts) -> 3872: ?line EtsMem = etsmem(), 3873: ?line Tab = ets_new(foo, Opts), 3874: ?line fill_tab(Tab, foobar), 3875: ?line true = ets:safe_fixtable(Tab, true), 3876: ?line receive after 1 -> ok end, 3877: ?line true = ets:safe_fixtable(Tab, false), 3878: ?line false = ets:info(Tab,safe_fixed), 3879: ?line true = ets:safe_fixtable(Tab, true), 3880: Self = self(), 3881: ?line {{_,_,_},[{Self,1}]} = ets:info(Tab,safe_fixed), 3882: %% Test that an unjustified 'unfix' is a no-op. 3883: {Pid,MRef} = my_spawn_monitor(fun() -> true = ets:safe_fixtable(Tab,false) end), 3884: {'DOWN', MRef, process, Pid, normal} = receive M -> M end, 3885: ?line true = ets:info(Tab,fixed), 3886: ?line {{_,_,_},[{Self,1}]} = ets:info(Tab,safe_fixed), 3887: %% badarg's 3888: ?line {'EXIT', {badarg, _}} = (catch ets:safe_fixtable(Tab, foobar)), 3889: ?line true = ets:info(Tab,fixed), 3890: ?line true = ets:safe_fixtable(Tab, false), 3891: ?line false = ets:info(Tab,fixed), 3892: ?line {'EXIT', {badarg, _}} = (catch ets:safe_fixtable(Tab, foobar)), 3893: ?line false = ets:info(Tab,fixed), 3894: ?line ets:delete(Tab), 3895: ?line case catch ets:safe_fixtable(Tab, true) of 3896: {'EXIT', _Reason} -> 3897: ?line verify_etsmem(EtsMem); 3898: _ -> 3899: ?t:fail("Fixtable on nonexisting table returned `true'") 3900: end, 3901: ok. 3902: 3903: info(doc) -> ["Tests ets:info result for required tuples."]; 3904: info(suite) -> []; 3905: info(Config) when is_list(Config) -> 3906: repeat_for_opts(info_do). 3907: 3908: info_do(Opts) -> 3909: ?line EtsMem = etsmem(), 3910: ?line MeMyselfI=self(), 3911: ?line ThisNode=node(), 3912: ?line Tab = ets_new(foobar, [{keypos, 2} | Opts]), 3913: 3914: %% Note: ets:info/1 used to return a tuple, but from R11B onwards it 3915: %% returns a list. 3916: ?line Res = ets:info(Tab), 3917: ?line {value, {memory, _Mem}} = lists:keysearch(memory, 1, Res), 3918: ?line {value, {owner, MeMyselfI}} = lists:keysearch(owner, 1, Res), 3919: ?line {value, {name, foobar}} = lists:keysearch(name, 1, Res), 3920: ?line {value, {size, 0}} = lists:keysearch(size, 1, Res), 3921: ?line {value, {node, ThisNode}} = lists:keysearch(node, 1, Res), 3922: ?line {value, {named_table, false}} = lists:keysearch(named_table, 1, Res), 3923: ?line {value, {type, set}} = lists:keysearch(type, 1, Res), 3924: ?line {value, {keypos, 2}} = lists:keysearch(keypos, 1, Res), 3925: ?line {value, {protection, protected}} = 3926: lists:keysearch(protection, 1, Res), 3927: ?line true = ets:delete(Tab), 3928: ?line undefined = ets:info(non_existing_table_xxyy), 3929: ?line undefined = ets:info(non_existing_table_xxyy,type), 3930: ?line undefined = ets:info(non_existing_table_xxyy,node), 3931: ?line undefined = ets:info(non_existing_table_xxyy,named_table), 3932: ?line undefined = ets:info(non_existing_table_xxyy,safe_fixed), 3933: ?line verify_etsmem(EtsMem). 3934: 3935: dups(doc) -> ["Test various duplicate_bags stuff"]; 3936: dups(suite) -> []; 3937: dups(Config) when is_list(Config) -> 3938: repeat_for_opts(dups_do). 3939: 3940: dups_do(Opts) -> 3941: ?line EtsMem = etsmem(), 3942: ?line T = make_table(funky, 3943: [duplicate_bag | Opts], 3944: [{1, 2}, {1, 2}]), 3945: ?line 2 = length(ets:tab2list(T)), 3946: ?line ets:delete(T, 1), 3947: ?line [] = ets:lookup(T, 1), 3948: 3949: ?line ets:insert(T, {1, 2, 2}), 3950: ?line ets:insert(T, {1, 2, 4}), 3951: ?line ets:insert(T, {1, 2, 2}), 3952: ?line ets:insert(T, {1, 2, 2}), 3953: ?line ets:insert(T, {1, 2, 4}), 3954: 3955: ?line 5 = length(ets:tab2list(T)), 3956: 3957: ?line 5 = length(ets:match(T, {'$1', 2, '$2'})), 3958: ?line 3 = length(ets:match(T, {'_', '$1', '$1'})), 3959: ?line ets:match_delete(T, {'_', '$1', '$1'}), 3960: ?line 0 = length(ets:match(T, {'_', '$1', '$1'})), 3961: ?line ets:delete(T), 3962: ?line verify_etsmem(EtsMem). 3963: 3964: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3965: 3966: tab2file(doc) -> ["Check the ets:tab2file function on an empty " 3967: "ets table."]; 3968: tab2file(suite) -> []; 3969: tab2file(Config) when is_list(Config) -> 3970: %% Write an empty ets table to a file, read back and check properties. 3971: ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, set, private, 3972: {keypos, 2}]), 3973: ?line FName = filename:join([?config(priv_dir, Config),"tab2file_case"]), 3974: ?line ok = ets:tab2file(Tab, FName), 3975: ?line true = ets:delete(Tab), 3976: % 3977: ?line EtsMem = etsmem(), 3978: ?line {ok, Tab2} = ets:file2tab(FName), 3979: ?line private = ets:info(Tab2, protection), 3980: ?line true = ets:info(Tab2, named_table), 3981: ?line 2 = ets:info(Tab2, keypos), 3982: ?line set = ets:info(Tab2, type), 3983: ?line true = ets:delete(Tab2), 3984: ?line verify_etsmem(EtsMem). 3985: 3986: tab2file2(doc) -> ["Check the ets:tab2file function on a ", 3987: "filled set/bag type ets table."]; 3988: tab2file2(suite) -> []; 3989: tab2file2(Config) when is_list(Config) -> 3990: repeat_for_opts({tab2file2_do,Config}, [[set,bag],compressed]). 3991: 3992: tab2file2_do(Opts, Config) -> 3993: ?line EtsMem = etsmem(), 3994: ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, private, 3995: {keypos, 2} | Opts]), 3996: ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]), 3997: ?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!) 3998: ?line Len = length(ets:tab2list(Tab)), 3999: ?line Mem = ets:info(Tab, memory), 4000: ?line Type = ets:info(Tab, type), 4001: %%io:format("org tab: ~p\n",[ets:info(Tab)]), 4002: ?line ok = ets:tab2file(Tab, FName), 4003: ?line true = ets:delete(Tab), 4004: 4005: ?line EtsMem4 = etsmem(), 4006: 4007: ?line {ok, Tab2} = ets:file2tab(FName), 4008: %%io:format("loaded tab: ~p\n",[ets:info(Tab2)]), 4009: ?line private = ets:info(Tab2, protection), 4010: ?line true = ets:info(Tab2, named_table), 4011: ?line 2 = ets:info(Tab2, keypos), 4012: ?line Type = ets:info(Tab2, type), 4013: ?line Len = length(ets:tab2list(Tab2)), 4014: ?line Mem = ets:info(Tab2, memory), 4015: ?line true = ets:delete(Tab2), 4016: io:format("Between = ~p\n", [EtsMem4]), 4017: ?line verify_etsmem(EtsMem). 4018: 4019: -define(test_list, [8,5,4,1,58,125,255, 250, 245, 240, 235, 4020: 230, Num rem 255, 255, 125, 130, 135, 140, 145, 4021: 150, 134, 12, 54, Val rem 255, 12, 3, 6, 9, 126]). 4022: -define(big_test_list, [Num rem 256|lists:seq(1, 66)]). 4023: -define(test_integer, 2846287468+Num). 4024: -define(test_float, 187263.18236-Val). 4025: -define(test_atom, some_crazy_atom). 4026: -define(test_tuple, {just, 'Some', 'Tuple', 1, [list, item], Val+Num}). 4027: 4028: %% Insert different datatypes into a ets table. 4029: fill_tab2(_Tab, _Val, 0) -> 4030: ok; 4031: fill_tab2(Tab, Val, Num) -> 4032: ?line Item = 4033: case Num rem 10 of 4034: 0 -> "String"; 4035: 1 -> ?line ?test_atom; 4036: 2 -> ?line ?test_tuple; 4037: 3 -> ?line ?test_integer; 4038: 4 -> ?line ?test_float; 4039: 5 -> ?line list_to_binary(?test_list); %Heap binary 4040: 6 -> ?line list_to_binary(?big_test_list); %Refc binary 4041: 7 -> ?line make_sub_binary(?test_list, Num); %Sub binary 4042: 8 -> ?line ?test_list; 4043: 9 -> ?line fun(X) -> {Tab,Val,X*Num} end 4044: end, 4045: ?line true=ets:insert(Tab, {Item, Val}), 4046: ?line fill_tab2(Tab, Val+1, Num-1), 4047: ok. 4048: 4049: tabfile_ext1(suite) -> 4050: []; 4051: tabfile_ext1(doc) -> 4052: ["Tests verification of tables with object count extended_info"]; 4053: tabfile_ext1(Config) when is_list(Config) -> 4054: repeat_for_opts(fun(Opts) -> tabfile_ext1_do(Opts, Config) end). 4055: 4056: tabfile_ext1_do(Opts,Config) -> 4057: ?line FName = filename:join([?config(priv_dir, Config),"nisse.dat"]), 4058: ?line FName2 = filename:join([?config(priv_dir, Config),"countflip.dat"]), 4059: L = lists:seq(1,10), 4060: T = ets_new(x,Opts), 4061: Name = make_ref(), 4062: [ets:insert(T,{X,integer_to_list(X)}) || X <- L], 4063: ok = ets:tab2file(T,FName,[{extended_info,[object_count]}]), 4064: true = lists:sort(ets:tab2list(T)) =:= 4065: lists:sort(ets:tab2list(element(2,ets:file2tab(FName)))), 4066: true = lists:sort(ets:tab2list(T)) =:= 4067: lists:sort(ets:tab2list( 4068: element(2,ets:file2tab(FName,[{verify,true}])))), 4069: {ok,Name} = disk_log:open([{name,Name},{file,FName}]), 4070: {_,[H0|T0]} = disk_log:chunk(Name,start), 4071: disk_log:close(Name), 4072: LH0=tuple_to_list(H0), 4073: {value,{size,N}}=lists:keysearch(size,1,LH0), 4074: NewLH0 = lists:keyreplace(size,1,LH0,{size,N-1}), 4075: NewH0 = list_to_tuple(NewLH0), 4076: NewT0=lists:keydelete(8,1,T0), 4077: file:delete(FName2), 4078: disk_log:open([{name,Name},{file,FName2},{mode,read_write}]), 4079: disk_log:log_terms(Name,[NewH0|NewT0]), 4080: disk_log:close(Name), 4081: 9 = length(ets:tab2list(element(2,ets:file2tab(FName2)))), 4082: {error,invalid_object_count} = ets:file2tab(FName2,[{verify,true}]), 4083: {ok, _} = ets:tabfile_info(FName2), 4084: {ok, _} = ets:tabfile_info(FName), 4085: file:delete(FName), 4086: file:delete(FName2), 4087: ok. 4088: 4089: tabfile_ext2(suite) -> 4090: []; 4091: tabfile_ext2(doc) -> 4092: ["Tests verification of tables with md5sum extended_info"]; 4093: tabfile_ext2(Config) when is_list(Config) -> 4094: repeat_for_opts(fun(Opts) -> tabfile_ext2_do(Opts,Config) end). 4095: 4096: tabfile_ext2_do(Opts,Config) -> 4097: ?line FName = filename:join([?config(priv_dir, Config),"olle.dat"]), 4098: ?line FName2 = filename:join([?config(priv_dir, Config),"bitflip.dat"]), 4099: L = lists:seq(1,10), 4100: T = ets_new(x,Opts), 4101: Name = make_ref(), 4102: [ets:insert(T,{X,integer_to_list(X)}) || X <- L], 4103: ok = ets:tab2file(T,FName,[{extended_info,[md5sum]}]), 4104: true = lists:sort(ets:tab2list(T)) =:= 4105: lists:sort(ets:tab2list(element(2,ets:file2tab(FName)))), 4106: true = lists:sort(ets:tab2list(T)) =:= 4107: lists:sort(ets:tab2list( 4108: element(2,ets:file2tab(FName,[{verify,true}])))), 4109: {ok, Name} = disk_log:open([{name,Name},{file,FName}]), 4110: {_,[H1|T1]} = disk_log:chunk(Name,start), 4111: disk_log:close(Name), 4112: NewT1=lists:keyreplace(8,1,T1,{8,"9"}), 4113: file:delete(FName2), 4114: disk_log:open([{name,Name},{file,FName2},{mode,read_write}]), 4115: disk_log:log_terms(Name,[H1|NewT1]), 4116: disk_log:close(Name), 4117: {value,{8,"9"}} = lists:keysearch(8,1, 4118: ets:tab2list( 4119: element(2,ets:file2tab(FName2)))), 4120: {error,checksum_error} = ets:file2tab(FName2,[{verify,true}]), 4121: {value,{extended_info,[md5sum]}} = 4122: lists:keysearch(extended_info,1,element(2,ets:tabfile_info(FName2))), 4123: {value,{extended_info,[md5sum]}} = 4124: lists:keysearch(extended_info,1,element(2,ets:tabfile_info(FName))), 4125: file:delete(FName), 4126: file:delete(FName2), 4127: ok. 4128: 4129: tabfile_ext3(suite) -> 4130: []; 4131: tabfile_ext3(doc) -> 4132: ["Tests verification of (named) tables without extended info"]; 4133: tabfile_ext3(Config) when is_list(Config) -> 4134: ?line FName = filename:join([?config(priv_dir, Config),"namn.dat"]), 4135: ?line FName2 = filename:join([?config(priv_dir, Config),"ncountflip.dat"]), 4136: L = lists:seq(1,10), 4137: Name = make_ref(), 4138: ?MODULE = ets_new(?MODULE,[named_table]), 4139: [ets:insert(?MODULE,{X,integer_to_list(X)}) || X <- L], 4140: ets:tab2file(?MODULE,FName), 4141: {error,cannot_create_table} = ets:file2tab(FName), 4142: true = ets:delete(?MODULE), 4143: {ok,?MODULE} = ets:file2tab(FName), 4144: true = ets:delete(?MODULE), 4145: disk_log:open([{name,Name},{file,FName}]), 4146: {_,[H2|T2]} = disk_log:chunk(Name,start), 4147: disk_log:close(Name), 4148: NewT2=lists:keydelete(8,1,T2), 4149: file:delete(FName2), 4150: disk_log:open([{name,Name},{file,FName2},{mode,read_write}]), 4151: disk_log:log_terms(Name,[H2|NewT2]), 4152: disk_log:close(Name), 4153: 9 = length(ets:tab2list(element(2,ets:file2tab(FName2)))), 4154: true = ets:delete(?MODULE), 4155: {error,invalid_object_count} = ets:file2tab(FName2,[{verify,true}]), 4156: {'EXIT',_} = (catch ets:delete(?MODULE)), 4157: {ok,_} = ets:tabfile_info(FName2), 4158: {ok,_} = ets:tabfile_info(FName), 4159: file:delete(FName), 4160: file:delete(FName2), 4161: ok. 4162: 4163: tabfile_ext4(suite) -> 4164: []; 4165: tabfile_ext4(doc) -> 4166: ["Tests verification of large table with md5 sum"]; 4167: tabfile_ext4(Config) when is_list(Config) -> 4168: ?line FName = filename:join([?config(priv_dir, Config),"bauta.dat"]), 4169: LL = lists:seq(1,10000), 4170: TL = ets_new(x,[]), 4171: Name2 = make_ref(), 4172: [ets:insert(TL,{X,integer_to_list(X)}) || X <- LL], 4173: ok = ets:tab2file(TL,FName,[{extended_info,[md5sum]}]), 4174: {ok, Name2} = disk_log:open([{name, Name2}, {file, FName}, 4175: {mode, read_only}]), 4176: {C,[_|_]} = disk_log:chunk(Name2,start), 4177: {_,[_|_]} = disk_log:chunk(Name2,C), 4178: disk_log:close(Name2), 4179: true = lists:sort(ets:tab2list(TL)) =:= 4180: lists:sort(ets:tab2list(element(2,ets:file2tab(FName)))), 4181: Res = [ 4182: begin 4183: {ok,FD} = file:open(FName,[binary,read,write]), 4184: {ok, Bin} = file:pread(FD,0,1000), 4185: <<B1:N/binary,Ch:8,B2/binary>> = Bin, 4186: Ch2 = (Ch + 1) rem 255, 4187: Bin2 = <<B1/binary,Ch2:8,B2/binary>>, 4188: ok = file:pwrite(FD,0,Bin2), 4189: ok = file:close(FD), 4190: X = case ets:file2tab(FName) of 4191: {ok,TL2} -> 4192: true = lists:sort(ets:tab2list(TL)) =/= 4193: lists:sort(ets:tab2list(TL2)); 4194: _ -> 4195: totally_broken 4196: end, 4197: {error,Y} = ets:file2tab(FName,[{verify,true}]), 4198: ets:tab2file(TL,FName,[{extended_info,[md5sum]}]), 4199: {X,Y} 4200: end || N <- lists:seq(400,500) ], 4201: io:format("~p~n",[Res]), 4202: file:delete(FName), 4203: ok. 4204: 4205: 4206: 4207: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4208: 4209: make_sub_binary(List, Num) when is_list(List) -> 4210: N = Num rem 23, 4211: Bin = list_to_binary([lists:seq(0, N)|List]), 4212: {_,B} = split_binary(Bin, N+1), 4213: B. 4214: 4215: 4216: %% Lookup stuff like crazy... 4217: heavy_lookup(doc) -> ["Performs multiple lookups for every key ", 4218: "in a large table."]; 4219: heavy_lookup(suite) -> []; 4220: heavy_lookup(Config) when is_list(Config) -> 4221: repeat_for_opts(heavy_lookup_do). 4222: 4223: heavy_lookup_do(Opts) -> 4224: ?line EtsMem = etsmem(), 4225: ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]), 4226: ?line ok = fill_tab2(Tab, 0, 7000), 4227: ?line ?t:do_times(50, ?MODULE, do_lookup, [Tab, 6999]), 4228: ?line true = ets:delete(Tab), 4229: ?line verify_etsmem(EtsMem). 4230: 4231: do_lookup(_Tab, 0) -> ok; 4232: do_lookup(Tab, N) -> 4233: case ets:lookup(Tab, N) of 4234: [] -> ?t:format("Set #~p was reported as empty. Not valid.", 4235: [N]), 4236: exit('Invalid lookup'); 4237: _ -> do_lookup(Tab, N-1) 4238: end. 4239: 4240: heavy_lookup_element(doc) -> ["Performs multiple lookups for ", 4241: "every element in a large table."]; 4242: heavy_lookup_element(suite) -> []; 4243: heavy_lookup_element(Config) when is_list(Config) -> 4244: repeat_for_opts(heavy_lookup_element_do). 4245: 4246: heavy_lookup_element_do(Opts) -> 4247: EtsMem = etsmem(), 4248: Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]), 4249: ok = fill_tab2(Tab, 0, 7000), 4250: % lookup ALL elements 50 times 4251: ?t:do_times(50, ?MODULE, do_lookup_element, [Tab, 6999, 1]), 4252: true = ets:delete(Tab), 4253: verify_etsmem(EtsMem). 4254: 4255: do_lookup_element(_Tab, 0, _) -> ok; 4256: do_lookup_element(Tab, N, M) -> 4257: ?line case catch ets:lookup_element(Tab, N, M) of 4258: {'EXIT', {badarg, _}} -> 4259: case M of 4260: 1 -> ?t:fail("Set #~p reported as empty. Not valid.", 4261: [N]), 4262: exit('Invalid lookup_element'); 4263: _ -> ?line do_lookup_element(Tab, N-1, 1) 4264: end; 4265: _ -> ?line do_lookup_element(Tab, N, M+1) 4266: end. 4267: 4268: 4269: heavy_concurrent(Config) when is_list(Config) -> 4270: repeat_for_opts(do_heavy_concurrent). 4271: 4272: do_heavy_concurrent(Opts) -> 4273: ?line Size = 10000, 4274: ?line EtsMem = etsmem(), 4275: ?line Tab = ets_new(blupp, [set, public, {keypos, 2} | Opts]), 4276: ?line ok = fill_tab2(Tab, 0, Size), 4277: ?line Procs = lists:map( 4278: fun (N) -> 4279: my_spawn_link( 4280: fun () -> 4281: do_heavy_concurrent_proc(Tab, Size, N) 4282: end) 4283: end, 4284: lists:seq(1, 500)), 4285: ?line lists:foreach(fun (P) -> 4286: M = erlang:monitor(process, P), 4287: receive 4288: {'DOWN', M, process, P, _} -> 4289: ok 4290: end 4291: end, 4292: Procs), 4293: ?line true = ets:delete(Tab), 4294: ?line verify_etsmem(EtsMem). 4295: 4296: do_heavy_concurrent_proc(_Tab, 0, _Offs) -> 4297: done; 4298: do_heavy_concurrent_proc(Tab, N, Offs) when (N+Offs) rem 100 == 0 -> 4299: Data = {"here", are, "S O M E ", data, "toooooooooooooooooo", insert, 4300: make_ref(), make_ref(), make_ref()}, 4301: true=ets:insert(Tab, {{self(),Data}, N}), 4302: do_heavy_concurrent_proc(Tab, N-1, Offs); 4303: do_heavy_concurrent_proc(Tab, N, Offs) -> 4304: _ = ets:lookup(Tab, N), 4305: do_heavy_concurrent_proc(Tab, N-1, Offs). 4306: 4307: 4308: fold_empty(doc) -> 4309: []; 4310: fold_empty(suite) -> []; 4311: fold_empty(Config) when is_list(Config) -> 4312: ?line EtsMem = etsmem(), 4313: ?line Tab = make_table(a, [], []), 4314: ?line [] = ets:foldl(fun(_X) -> exit(hej) end, [], Tab), 4315: ?line [] = ets:foldr(fun(_X) -> exit(hej) end, [], Tab), 4316: ?line true = ets:delete(Tab), 4317: ?line verify_etsmem(EtsMem). 4318: 4319: foldl(doc) -> 4320: []; 4321: foldl(suite) -> []; 4322: foldl(Config) when is_list(Config) -> 4323: ?line EtsMem = etsmem(), 4324: ?line L = [{a,1}, {c,3}, {b,2}], 4325: ?line LS = lists:sort(L), 4326: ?line Tab = make_table(a, [bag], L), 4327: ?line LS = lists:sort(ets:foldl(fun(E,A) -> [E|A] end, [], Tab)), 4328: ?line true = ets:delete(Tab), 4329: ?line verify_etsmem(EtsMem). 4330: 4331: foldr(doc) -> 4332: []; 4333: foldr(suite) -> []; 4334: foldr(Config) when is_list(Config) -> 4335: ?line EtsMem = etsmem(), 4336: ?line L = [{a,1}, {c,3}, {b,2}], 4337: ?line LS = lists:sort(L), 4338: ?line Tab = make_table(a, [bag], L), 4339: ?line LS = lists:sort(ets:foldr(fun(E,A) -> [E|A] end, [], Tab)), 4340: ?line true = ets:delete(Tab), 4341: ?line verify_etsmem(EtsMem). 4342: 4343: foldl_ordered(doc) -> 4344: []; 4345: foldl_ordered(suite) -> []; 4346: foldl_ordered(Config) when is_list(Config) -> 4347: ?line EtsMem = etsmem(), 4348: ?line L = [{a,1}, {c,3}, {b,2}], 4349: ?line LS = lists:sort(L), 4350: ?line Tab = make_table(a, [ordered_set], L), 4351: ?line LS = lists:reverse(ets:foldl(fun(E,A) -> [E|A] end, [], Tab)), 4352: ?line true = ets:delete(Tab), 4353: ?line verify_etsmem(EtsMem). 4354: 4355: foldr_ordered(doc) -> 4356: []; 4357: foldr_ordered(suite) -> []; 4358: foldr_ordered(Config) when is_list(Config) -> 4359: ?line EtsMem = etsmem(), 4360: ?line L = [{a,1}, {c,3}, {b,2}], 4361: ?line LS = lists:sort(L), 4362: ?line Tab = make_table(a, [ordered_set], L), 4363: ?line LS = ets:foldr(fun(E,A) -> [E|A] end, [], Tab), 4364: ?line true = ets:delete(Tab), 4365: ?line verify_etsmem(EtsMem). 4366: 4367: member(suite) -> 4368: []; 4369: member(doc) -> 4370: ["Tests ets:member BIF"]; 4371: member(Config) when is_list(Config) -> 4372: repeat_for_opts(member_do, [write_concurrency, all_types]). 4373: 4374: member_do(Opts) -> 4375: ?line EtsMem = etsmem(), 4376: ?line T = ets_new(xxx, Opts), 4377: ?line false = ets:member(T,hej), 4378: ?line E = fun(0,_F)->ok; 4379: (N,F) -> 4380: ?line ets:insert(T,{N,N rem 10}), 4381: F(N-1,F) 4382: end, 4383: ?line E(10000,E), 4384: ?line false = ets:member(T,hej), 4385: ?line true = ets:member(T,1), 4386: ?line false = ets:member(T,20000), 4387: ?line ets:delete(T,5), 4388: ?line false = ets:member(T,5), 4389: ?line ets:safe_fixtable(T,true), 4390: ?line ets:delete(T,6), 4391: ?line false = ets:member(T,6), 4392: ?line ets:safe_fixtable(T,false), 4393: ?line false = ets:member(T,6), 4394: ?line ets:delete(T), 4395: ?line {'EXIT',{badarg,_}} = (catch ets:member(finnsinte, 23)), 4396: ?line {'EXIT',{badarg,_}} = (catch ets:member(T, 23)), 4397: ?line verify_etsmem(EtsMem). 4398: 4399: 4400: build_table(L1,L2,Num) -> 4401: T = ets_new(xxx, [ordered_set] 4402: ), 4403: lists:foreach( 4404: fun(X1) -> 4405: lists:foreach( 4406: fun(X2) -> 4407: F = fun(FF,N) -> 4408: ets:insert(T,{{X1,X2,N}, 4409: X1, X2, N}), 4410: case N of 4411: 0 -> 4412: ok; 4413: _ -> 4414: FF(FF,N-1) 4415: end 4416: end, 4417: F(F,Num) 4418: end, L2) 4419: end, L1), 4420: T. 4421: 4422: build_table2(L1,L2,Num) -> 4423: T = ets_new(xxx, [ordered_set] 4424: ), 4425: lists:foreach( 4426: fun(X1) -> 4427: lists:foreach( 4428: fun(X2) -> 4429: F = fun(FF,N) -> 4430: ets:insert(T,{{N,X1,X2}, 4431: N, X1, X2}), 4432: case N of 4433: 0 -> 4434: ok; 4435: _ -> 4436: FF(FF,N-1) 4437: end 4438: end, 4439: F(F,Num) 4440: end, L2) 4441: end, L1), 4442: T. 4443: 4444: time_match_object(Tab,Match, Res) -> 4445: T1 = erlang:now(), 4446: Res = ets:match_object(Tab,Match), 4447: T2 = erlang:now(), 4448: nowdiff(T1,T2). 4449: 4450: time_match(Tab,Match) -> 4451: T1 = erlang:now(), 4452: ets:match(Tab,Match), 4453: T2 = erlang:now(), 4454: nowdiff(T1,T2). 4455: 4456: seventyfive_percent_success(_,S,Fa,0) -> 4457: true = (S > ((S + Fa) * 0.75)); 4458: 4459: seventyfive_percent_success({M,F,A},S,Fa,N) -> 4460: case (catch apply(M,F,A)) of 4461: {'EXIT', _} -> 4462: seventyfive_percent_success({M,F,A},S,Fa+1,N-1); 4463: _ -> 4464: seventyfive_percent_success({M,F,A},S+1,Fa,N-1) 4465: end. 4466: 4467: fifty_percent_success(_,S,Fa,0) -> 4468: true = (S > ((S + Fa) * 0.5)); 4469: 4470: fifty_percent_success({M,F,A},S,Fa,N) -> 4471: case (catch apply(M,F,A)) of 4472: {'EXIT', _} -> 4473: fifty_percent_success({M,F,A},S,Fa+1,N-1); 4474: _ -> 4475: fifty_percent_success({M,F,A},S+1,Fa,N-1) 4476: end. 4477: 4478: 4479: nowtonumber({Mega, Secs, Milli}) -> 4480: Milli + Secs * 1000000 + Mega * 1000000000000. 4481: nowdiff(T1,T2) -> 4482: nowtonumber(T2) - nowtonumber(T1). 4483: 4484: create_random_string(0) -> 4485: []; 4486: 4487: create_random_string(OfLength) -> 4488: C = case random:uniform(2) of 4489: 1 -> 4490: (random:uniform($Z - $A + 1) - 1) + $A; 4491: _ -> 4492: (random:uniform($z - $a + 1) - 1) + $a 4493: end, 4494: [C | create_random_string(OfLength - 1)]. 4495: 4496: 4497: create_random_tuple(OfLength) -> 4498: list_to_tuple(lists:map(fun(X) -> 4499: list_to_atom([X]) 4500: end,create_random_string(OfLength))). 4501: 4502: create_partly_bound_tuple(OfLength) -> 4503: case random:uniform(2) of 4504: 1 -> 4505: create_partly_bound_tuple1(OfLength); 4506: _ -> 4507: create_partly_bound_tuple3(OfLength) 4508: end. 4509: 4510: create_partly_bound_tuple1(OfLength) -> 4511: T0 = create_random_tuple(OfLength), 4512: I = random:uniform(OfLength), 4513: setelement(I,T0,'$1'). 4514: 4515: 4516: set_n_random_elements(T0,0,_,_) -> 4517: T0; 4518: set_n_random_elements(T0,N,OfLength,GenFun) -> 4519: I = random:uniform(OfLength), 4520: What = GenFun(I), 4521: case element(I,T0) of 4522: What -> 4523: set_n_random_elements(T0,N,OfLength,GenFun); 4524: _Else -> 4525: set_n_random_elements(setelement(I,T0,What), 4526: N-1,OfLength,GenFun) 4527: end. 4528: 4529: make_dollar_atom(I) -> 4530: list_to_atom([$$] ++ integer_to_list(I)). 4531: create_partly_bound_tuple2(OfLength) -> 4532: T0 = create_random_tuple(OfLength), 4533: I = random:uniform(OfLength - 1), 4534: set_n_random_elements(T0,I,OfLength,fun make_dollar_atom/1). 4535: 4536: create_partly_bound_tuple3(OfLength) -> 4537: T0 = create_random_tuple(OfLength), 4538: I = random:uniform(OfLength - 1), 4539: set_n_random_elements(T0,I,OfLength,fun(_) -> '_' end). 4540: 4541: do_n_times(_,0) -> 4542: ok; 4543: do_n_times(Fun,N) -> 4544: Fun(), 4545: case N rem 1000 of 4546: 0 -> 4547: io:format("."); 4548: _ -> 4549: ok 4550: end, 4551: do_n_times(Fun,N-1). 4552: 4553: make_table(Name, Options, Elements) -> 4554: T = ets_new(Name, Options), 4555: lists:foreach(fun(E) -> ets:insert(T, E) end, Elements), 4556: T. 4557: filltabint(Tab,0) -> 4558: Tab; 4559: filltabint(Tab,N) -> 4560: ets:insert(Tab,{N,integer_to_list(N)}), 4561: filltabint(Tab,N-1). 4562: filltabint2(Tab,0) -> 4563: Tab; 4564: filltabint2(Tab,N) -> 4565: ets:insert(Tab,{N + N rem 2,integer_to_list(N)}), 4566: filltabint2(Tab,N-1). 4567: filltabint3(Tab,0) -> 4568: Tab; 4569: filltabint3(Tab,N) -> 4570: ets:insert(Tab,{N + N rem 2,integer_to_list(N + N rem 2)}), 4571: filltabint3(Tab,N-1). 4572: xfilltabint(Tab,N) -> 4573: case ets:info(Tab,type) of 4574: bag -> 4575: filltabint2(Tab,N); 4576: duplicate_bag -> 4577: ets:select_delete(Tab,[{'_',[],[true]}]), 4578: filltabint3(Tab,N); 4579: _ -> 4580: filltabint(Tab,N) 4581: end. 4582: 4583: 4584: filltabstr(Tab,N) -> 4585: filltabstr(Tab,0,N). 4586: filltabstr(Tab,N,N) -> 4587: Tab; 4588: filltabstr(Tab,Floor,N) when N > Floor -> 4589: ets:insert(Tab,{integer_to_list(N),N}), 4590: filltabstr(Tab,Floor,N-1). 4591: 4592: filltabstr2(Tab,0) -> 4593: Tab; 4594: filltabstr2(Tab,N) -> 4595: ets:insert(Tab,{integer_to_list(N),N}), 4596: ets:insert(Tab,{integer_to_list(N),N+1}), 4597: filltabstr2(Tab,N-1). 4598: filltabstr3(Tab,0) -> 4599: Tab; 4600: filltabstr3(Tab,N) -> 4601: ets:insert(Tab,{integer_to_list(N),N}), 4602: ets:insert(Tab,{integer_to_list(N),N}), 4603: filltabstr3(Tab,N-1). 4604: xfilltabstr(Tab,N) -> 4605: case ets:info(Tab,type) of 4606: bag -> 4607: filltabstr2(Tab,N); 4608: duplicate_bag -> 4609: ets:select_delete(Tab,[{'_',[],[true]}]), 4610: filltabstr3(Tab,N); 4611: _ -> 4612: filltabstr(Tab,N) 4613: end. 4614: 4615: fill_sets_int(N) -> 4616: fill_sets_int(N,[]). 4617: fill_sets_int(N,Opts) -> 4618: Tab1 = ets_new(xxx, [ordered_set|Opts]), 4619: filltabint(Tab1,N), 4620: Tab2 = ets_new(xxx, [set|Opts]), 4621: filltabint(Tab2,N), 4622: Tab3 = ets_new(xxx, [bag|Opts]), 4623: filltabint2(Tab3,N), 4624: Tab4 = ets_new(xxx, [duplicate_bag|Opts]), 4625: filltabint3(Tab4,N), 4626: [Tab1,Tab2,Tab3,Tab4]. 4627: 4628: check_fun(_Tab,_Fun,'$end_of_table') -> 4629: ok; 4630: check_fun(Tab,Fun,Item) -> 4631: lists:foreach(fun(Obj) -> 4632: true = Fun(Obj) 4633: end, 4634: ets:lookup(Tab,Item)), 4635: check_fun(Tab,Fun,ets:next(Tab,Item)). 4636: 4637: check(Tab,Fun,N) -> 4638: N = ets:info(Tab, size), 4639: check_fun(Tab,Fun,ets:first(Tab)). 4640: 4641: 4642: 4643: del_one_by_one_set(T,N,N) -> 4644: 0 = ets:info(T,size), 4645: ok; 4646: del_one_by_one_set(T,From,To) -> 4647: N = ets:info(T,size), 4648: ets:delete_object(T,{From, integer_to_list(From)}), 4649: N = (ets:info(T,size) + 1), 4650: Next = if 4651: From < To -> 4652: From + 1; 4653: true -> 4654: From - 1 4655: end, 4656: del_one_by_one_set(T,Next,To). 4657: 4658: del_one_by_one_bag(T,N,N) -> 4659: 0 = ets:info(T,size), 4660: ok; 4661: del_one_by_one_bag(T,From,To) -> 4662: N = ets:info(T,size), 4663: ets:delete_object(T,{From + From rem 2, integer_to_list(From)}), 4664: N = (ets:info(T,size) + 1), 4665: Next = if 4666: From < To -> 4667: From + 1; 4668: true -> 4669: From - 1 4670: end, 4671: del_one_by_one_bag(T,Next,To). 4672: 4673: 4674: del_one_by_one_dbag_1(T,N,N) -> 4675: 0 = ets:info(T,size), 4676: ok; 4677: del_one_by_one_dbag_1(T,From,To) -> 4678: N = ets:info(T,size), 4679: ets:delete_object(T,{From, integer_to_list(From)}), 4680: case From rem 2 of 4681: 0 -> 4682: N = (ets:info(T,size) + 2); 4683: 1 -> 4684: N = ets:info(T,size) 4685: end, 4686: Next = if 4687: From < To -> 4688: From + 1; 4689: true -> 4690: From - 1 4691: end, 4692: del_one_by_one_dbag_1(T,Next,To). 4693: 4694: del_one_by_one_dbag_2(T,N,N) -> 4695: 0 = ets:info(T,size), 4696: ok; 4697: del_one_by_one_dbag_2(T,From,To) -> 4698: N = ets:info(T,size), 4699: ets:delete_object(T,{From, integer_to_list(From)}), 4700: case From rem 2 of 4701: 0 -> 4702: N = (ets:info(T,size) + 3); 4703: 1 -> 4704: N = (ets:info(T,size) + 1) 4705: end, 4706: Next = if 4707: From < To -> 4708: From + 1; 4709: true -> 4710: From - 1 4711: end, 4712: del_one_by_one_dbag_2(T,Next,To). 4713: 4714: del_one_by_one_dbag_3(T,N,N) -> 4715: 0 = ets:info(T,size), 4716: ok; 4717: del_one_by_one_dbag_3(T,From,To) -> 4718: N = ets:info(T,size), 4719: Obj = {From + From rem 2, integer_to_list(From)}, 4720: ets:delete_object(T,Obj), 4721: case From rem 2 of 4722: 0 -> 4723: N = (ets:info(T,size) + 2); 4724: 1 -> 4725: N = (ets:info(T,size) + 1), 4726: Obj2 = {From, integer_to_list(From)}, 4727: ets:delete_object(T,Obj2), 4728: N = (ets:info(T,size) + 2) 4729: end, 4730: Next = if 4731: From < To -> 4732: From + 1; 4733: true -> 4734: From - 1 4735: end, 4736: del_one_by_one_dbag_3(T,Next,To). 4737: 4738: 4739: successive_delete(Table,From,To,Type) -> 4740: successive_delete(Table,From,To,Type,ets:info(Table,type)). 4741: 4742: successive_delete(_Table,N,N,_,_) -> 4743: ok; 4744: successive_delete(Table,From,To,Type,TType) -> 4745: MS = case Type of 4746: bound -> 4747: [{{From,'_'},[],[true]}]; 4748: unbound -> 4749: [{{'$1','_'},[],[{'==', '$1', From}]}] 4750: end, 4751: case TType of 4752: X when X == bag; X == duplicate_bag -> 4753: %erlang:display(From), 4754: case From rem 2 of 4755: 0 -> 4756: 2 = ets:select_delete(Table,MS); 4757: _ -> 4758: 0 = ets:select_delete(Table,MS) 4759: end; 4760: _ -> 4761: 1 = ets:select_delete(Table,MS) 4762: end, 4763: Next = if 4764: From < To -> 4765: From + 1; 4766: true -> 4767: From - 1 4768: end, 4769: successive_delete(Table, Next, To, Type,TType). 4770: 4771: gen_dets_filename(Config,N) -> 4772: filename:join(?config(priv_dir,Config), 4773: "testdets_" ++ integer_to_list(N) ++ ".dets"). 4774: 4775: otp_6842_select_1000(Config) when is_list(Config) -> 4776: ?line Tab = ets_new(xxx,[ordered_set]), 4777: ?line [ets:insert(Tab,{X,X}) || X <- lists:seq(1,10000)], 4778: ?line AllTrue = lists:duplicate(10,true), 4779: ?line AllTrue = 4780: [ length( 4781: element(1, 4782: ets:select(Tab,[{'_',[],['$_']}],X*1000))) =:= 4783: X*1000 || X <- lists:seq(1,10) ], 4784: ?line Sequences = [[1000,1000,1000,1000,1000,1000,1000,1000,1000,1000], 4785: [2000,2000,2000,2000,2000], 4786: [3000,3000,3000,1000], 4787: [4000,4000,2000], 4788: [5000,5000], 4789: [6000,4000], 4790: [7000,3000], 4791: [8000,2000], 4792: [9000,1000], 4793: [10000]], 4794: ?line AllTrue = [ check_seq(Tab, ets:select(Tab,[{'_',[],['$_']}],hd(L)),L) || 4795: L <- Sequences ], 4796: ?line ets:delete(Tab), 4797: ok. 4798: 4799: check_seq(_,'$end_of_table',[]) -> 4800: true; 4801: check_seq(Tab,{L,C},[H|T]) when length(L) =:= H -> 4802: check_seq(Tab, ets:select(C),T); 4803: check_seq(A,B,C) -> 4804: erlang:display({A,B,C}), 4805: false. 4806: 4807: otp_6338(Config) when is_list(Config) -> 4808: L = binary_to_term(<<131,108,0,0,0,2,104,2,108,0,0,0,2,103,100,0,19,112,112,98,49,95,98,115,49,50,64,98,108,97,100,101,95,48,95,53,0,0,33,50,0,0,0,4,1,98,0,0,23,226,106,100,0,4,101,120,105,116,104,2,108,0,0,0,2,104,2,100,0,3,115,98,109,100,0,19,112,112,98,50,95,98,115,49,50,64,98,108,97,100,101,95,48,95,56,98,0,0,18,231,106,100,0,4,114,101,99,118,106>>), 4809: T = ets_new(xxx,[ordered_set]), 4810: lists:foreach(fun(X) -> ets:insert(T,X) end,L), 4811: [[4839,recv]] = ets:match(T,{[{sbm,ppb2_bs12@blade_0_8},'$1'],'$2'}), 4812: ets:delete(T). 4813: 4814: %% Elements could come in the wrong order in a bag if a rehash occurred. 4815: otp_5340(Config) when is_list(Config) -> 4816: repeat_for_opts(otp_5340_do). 4817: 4818: otp_5340_do(Opts) -> 4819: N = 3000, 4820: T = ets_new(otp_5340, [bag,public | Opts]), 4821: Ids = [1,2,3,4,5], 4822: [w(T, N, Id) || Id <- Ids], 4823: verify(T, Ids), 4824: ets:delete(T). 4825: 4826: w(_,0, _) -> ok; 4827: w(T,N, Id) -> 4828: ets:insert(T, {N, Id}), 4829: w(T,N-1,Id). 4830: 4831: verify(T, Ids) -> 4832: List = my_tab_to_list(T), 4833: Errors = lists:filter(fun(Bucket) -> 4834: verify2(Bucket, Ids) 4835: end, List), 4836: case Errors of 4837: [] -> 4838: ok; 4839: _ -> 4840: io:format("Failed:\n~p\n", [Errors]), 4841: ?t:fail() 4842: end. 4843: 4844: verify2([{_N,Id}|RL], [Id|R]) -> 4845: verify2(RL,R); 4846: verify2([],[]) -> false; 4847: verify2(_Err, _) -> 4848: true. 4849: 4850: otp_7665(doc) -> ["delete_object followed by delete on fixed bag failed to delete objects."]; 4851: otp_7665(suite) -> []; 4852: otp_7665(Config) when is_list(Config) -> 4853: repeat_for_opts(otp_7665_do). 4854: 4855: otp_7665_do(Opts) -> 4856: Tab = ets_new(otp_7665,[bag | Opts]), 4857: Min = 0, 4858: Max = 10, 4859: lists:foreach(fun(N)-> otp_7665_act(Tab,Min,Max,N) end, 4860: lists:seq(Min,Max)), 4861: ?line true = ets:delete(Tab). 4862: 4863: otp_7665_act(Tab,Min,Max,DelNr) -> 4864: List1 = [{key,N} || N <- lists:seq(Min,Max)], 4865: ?line true = ets:insert(Tab, List1), 4866: ?line true = ets:safe_fixtable(Tab, true), 4867: ?line true = ets:delete_object(Tab, {key,DelNr}), 4868: List2 = lists:delete({key,DelNr}, List1), 4869: 4870: %% Now verify that we find all remaining objects 4871: ?line List2 = ets:lookup(Tab,key), 4872: ?line EList2 = lists:map(fun({key,N})-> N end, 4873: List2), 4874: ?line EList2 = ets:lookup_element(Tab,key,2), 4875: ?line true = ets:delete(Tab, key), 4876: ?line [] = ets:lookup(Tab, key), 4877: ?line true = ets:safe_fixtable(Tab, false), 4878: ok. 4879: 4880: %% Whitebox testing of meta name table hashing. 4881: meta_wb(Config) when is_list(Config) -> 4882: ?line EtsMem = etsmem(), 4883: repeat_for_opts(meta_wb_do), 4884: ?line verify_etsmem(EtsMem). 4885: 4886: 4887: meta_wb_do(Opts) -> 4888: %% Do random new/delete/rename of colliding named tables 4889: Names0 = [pioneer | colliding_names(pioneer)], 4890: 4891: %% Remove any names that happen to exist as tables already 4892: Names = lists:filter(fun(Name) -> ets:info(Name) == undefined end, 4893: Names0), 4894: Len = length(Names), 4895: OpFuns = {fun meta_wb_new/4, fun meta_wb_delete/4, fun meta_wb_rename/4}, 4896: 4897: ?line true = (Len >= 3), 4898: 4899: io:format("Colliding names = ~p\n",[Names]), 4900: F = fun(0,_,_) -> ok; 4901: (N,Tabs,Me) -> Name1 = lists:nth(random:uniform(Len),Names), 4902: Name2 = lists:nth(random:uniform(Len),Names), 4903: Op = element(random:uniform(3),OpFuns), 4904: NTabs = Op(Name1, Name2, Tabs, Opts), 4905: Me(N-1,NTabs,Me) 4906: end, 4907: F(Len*100, [], F), 4908: 4909: % cleanup 4910: lists:foreach(fun(Name)->catch ets:delete(Name) end, 4911: Names). 4912: 4913: meta_wb_new(Name, _, Tabs, Opts) -> 4914: case (catch ets_new(Name,[named_table|Opts])) of 4915: Name -> 4916: ?line false = lists:member(Name, Tabs), 4917: [Name | Tabs]; 4918: {'EXIT',{badarg,_}} -> 4919: ?line true = lists:member(Name, Tabs), 4920: Tabs 4921: end. 4922: meta_wb_delete(Name, _, Tabs, _) -> 4923: case (catch ets:delete(Name)) of 4924: true -> 4925: ?line true = lists:member(Name, Tabs), 4926: lists:delete(Name, Tabs); 4927: {'EXIT',{badarg,_}} -> 4928: ?line false = lists:member(Name, Tabs), 4929: Tabs 4930: end. 4931: meta_wb_rename(Old, New, Tabs, _) -> 4932: case (catch ets:rename(Old,New)) of 4933: New -> 4934: ?line true = lists:member(Old, Tabs) 4935: andalso not lists:member(New, Tabs), 4936: [New | lists:delete(Old, Tabs)]; 4937: {'EXIT',{badarg,_}} -> 4938: ?line true = not lists:member(Old, Tabs) 4939: orelse lists:member(New,Tabs), 4940: Tabs 4941: end. 4942: 4943: 4944: colliding_names(Name) -> 4945: erts_debug:set_internal_state(colliding_names, {Name,5}). 4946: 4947: 4948: %% OTP_6913: Grow and shrink. 4949: 4950: grow_shrink(Config) when is_list(Config) -> 4951: ?line EtsMem = etsmem(), 4952: ?line grow_shrink_0(lists:seq(3071, 5000), EtsMem), 4953: ?line verify_etsmem(EtsMem). 4954: 4955: grow_shrink_0([N|Ns], EtsMem) -> 4956: ?line grow_shrink_1(N, [set]), 4957: ?line grow_shrink_1(N, [ordered_set]), 4958: %% Verifying ets-memory here takes too long time, since 4959: %% lock-free allocators were introduced... 4960: %% ?line verify_etsmem(EtsMem), 4961: grow_shrink_0(Ns, EtsMem); 4962: grow_shrink_0([], _) -> ok. 4963: 4964: grow_shrink_1(N, Flags) -> 4965: ?line T = ets_new(a, Flags), 4966: ?line grow_shrink_2(N, N, T), 4967: ?line ets:delete(T). 4968: 4969: grow_shrink_2(0, Orig, T) -> 4970: List = [{I,a} || I <- lists:seq(1, Orig)], 4971: List = lists:sort(ets:tab2list(T)), 4972: grow_shrink_3(Orig, T); 4973: grow_shrink_2(N, Orig, T) -> 4974: true = ets:insert(T, {N,a}), 4975: grow_shrink_2(N-1, Orig, T). 4976: 4977: grow_shrink_3(0, T) -> 4978: [] = ets:tab2list(T); 4979: grow_shrink_3(N, T) -> 4980: true = ets:delete(T, N), 4981: grow_shrink_3(N-1, T). 4982: 4983: grow_pseudo_deleted(doc) -> ["Grow a table that still contains pseudo-deleted objects"]; 4984: grow_pseudo_deleted(suite) -> []; 4985: grow_pseudo_deleted(Config) when is_list(Config) -> 4986: only_if_smp(fun() -> grow_pseudo_deleted_do() end). 4987: 4988: grow_pseudo_deleted_do() -> 4989: lists:foreach(fun(Type) -> grow_pseudo_deleted_do(Type) end, 4990: [set,bag,duplicate_bag]). 4991: 4992: grow_pseudo_deleted_do(Type) -> 4993: process_flag(scheduler,1), 4994: Self = self(), 4995: ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]), 4996: Mod = 7, Mult = 10000, 4997: filltabint(T,Mod*Mult), 4998: ?line true = ets:safe_fixtable(T,true), 4999: ?line Mult = ets:select_delete(T, 5000: [{{'$1', '_'}, 5001: [{'=:=', {'rem', '$1', Mod}, 0}], 5002: [true]}]), 5003: Left = Mult*(Mod-1), 5004: ?line Left = ets:info(T,size), 5005: ?line Mult = get_kept_objects(T), 5006: filltabstr(T,Mult), 5007: my_spawn_opt(fun()-> ?line true = ets:info(T,fixed), 5008: Self ! start, 5009: io:format("Starting to filltabstr... ~p\n",[now()]), 5010: filltabstr(T,Mult,Mult+10000), 5011: io:format("Done with filltabstr. ~p\n",[now()]), 5012: Self ! done 5013: end, [link, {scheduler,2}]), 5014: ?line start = receive_any(), 5015: io:format("Unfixing table...~p nitems=~p\n",[now(),ets:info(T,size)]), 5016: ?line true = ets:safe_fixtable(T,false), 5017: io:format("Unfix table done. ~p nitems=~p\n",[now(),ets:info(T,size)]), 5018: ?line false = ets:info(T,fixed), 5019: ?line 0 = get_kept_objects(T), 5020: ?line done = receive_any(), 5021: %%verify_table_load(T), % may fail if concurrency is poor (genny) 5022: ets:delete(T), 5023: process_flag(scheduler,0). 5024: 5025: shrink_pseudo_deleted(doc) -> ["Shrink a table that still contains pseudo-deleted objects"]; 5026: shrink_pseudo_deleted(suite) -> []; 5027: shrink_pseudo_deleted(Config) when is_list(Config) -> 5028: only_if_smp(fun()->shrink_pseudo_deleted_do() end). 5029: 5030: shrink_pseudo_deleted_do() -> 5031: lists:foreach(fun(Type) -> shrink_pseudo_deleted_do(Type) end, 5032: [set,bag,duplicate_bag]). 5033: 5034: shrink_pseudo_deleted_do(Type) -> 5035: process_flag(scheduler,1), 5036: Self = self(), 5037: ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]), 5038: Half = 10000, 5039: filltabint(T,Half*2), 5040: ?line true = ets:safe_fixtable(T,true), 5041: ?line Half = ets:select_delete(T, 5042: [{{'$1', '_'}, 5043: [{'>', '$1', Half}], 5044: [true]}]), 5045: ?line Half = ets:info(T,size), 5046: ?line Half = get_kept_objects(T), 5047: my_spawn_opt(fun()-> ?line true = ets:info(T,fixed), 5048: Self ! start, 5049: io:format("Starting to delete... ~p\n",[now()]), 5050: del_one_by_one_set(T,1,Half+1), 5051: io:format("Done with delete. ~p\n",[now()]), 5052: Self ! done 5053: end, [link, {scheduler,2}]), 5054: ?line start = receive_any(), 5055: io:format("Unfixing table...~p nitems=~p\n",[now(),ets:info(T,size)]), 5056: ?line true = ets:safe_fixtable(T,false), 5057: io:format("Unfix table done. ~p nitems=~p\n",[now(),ets:info(T,size)]), 5058: ?line false = ets:info(T,fixed), 5059: ?line 0 = get_kept_objects(T), 5060: ?line done = receive_any(), 5061: %%verify_table_load(T), % may fail if concurrency is poor (genny) 5062: ets:delete(T), 5063: process_flag(scheduler,0). 5064: 5065: 5066: 5067: meta_lookup_unnamed_read(suite) -> []; 5068: meta_lookup_unnamed_read(Config) when is_list(Config) -> 5069: InitF = fun(_) -> Tab = ets_new(unnamed,[]), 5070: true = ets:insert(Tab,{key,data}), 5071: Tab 5072: end, 5073: ExecF = fun(Tab) -> [{key,data}] = ets:lookup(Tab,key), 5074: Tab 5075: end, 5076: FiniF = fun(Tab) -> true = ets:delete(Tab) 5077: end, 5078: run_workers(InitF,ExecF,FiniF,10000). 5079: 5080: meta_lookup_unnamed_write(suite) -> []; 5081: meta_lookup_unnamed_write(Config) when is_list(Config) -> 5082: InitF = fun(_) -> Tab = ets_new(unnamed,[]), 5083: {Tab,0} 5084: end, 5085: ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}), 5086: {Tab,N+1} 5087: end, 5088: FiniF = fun({Tab,_}) -> true = ets:delete(Tab) 5089: end, 5090: run_workers(InitF,ExecF,FiniF,10000). 5091: 5092: meta_lookup_named_read(suite) -> []; 5093: meta_lookup_named_read(Config) when is_list(Config) -> 5094: InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)), 5095: Tab = ets_new(Name,[named_table]), 5096: true = ets:insert(Tab,{key,data}), 5097: Tab 5098: end, 5099: ExecF = fun(Tab) -> [{key,data}] = ets:lookup(Tab,key), 5100: Tab 5101: end, 5102: FiniF = fun(Tab) -> true = ets:delete(Tab) 5103: end, 5104: run_workers(InitF,ExecF,FiniF,10000). 5105: 5106: meta_lookup_named_write(suite) -> []; 5107: meta_lookup_named_write(Config) when is_list(Config) -> 5108: InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)), 5109: Tab = ets_new(Name,[named_table]), 5110: {Tab,0} 5111: end, 5112: ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}), 5113: {Tab,N+1} 5114: end, 5115: FiniF = fun({Tab,_}) -> true = ets:delete(Tab) 5116: end, 5117: run_workers(InitF,ExecF,FiniF,10000). 5118: 5119: meta_newdel_unnamed(suite) -> []; 5120: meta_newdel_unnamed(Config) when is_list(Config) -> 5121: InitF = fun(_) -> ok end, 5122: ExecF = fun(_) -> Tab = ets_new(unnamed,[]), 5123: true = ets:delete(Tab) 5124: end, 5125: FiniF = fun(_) -> ok end, 5126: run_workers(InitF,ExecF,FiniF,10000). 5127: 5128: meta_newdel_named(suite) -> []; 5129: meta_newdel_named(Config) when is_list(Config) -> 5130: InitF = fun([ProcN|_]) -> list_to_atom(integer_to_list(ProcN)) 5131: end, 5132: ExecF = fun(Name) -> Name = ets_new(Name,[named_table]), 5133: true = ets:delete(Name), 5134: Name 5135: end, 5136: FiniF = fun(_) -> ok end, 5137: run_workers(InitF,ExecF,FiniF,10000). 5138: 5139: smp_insert(doc) -> ["Concurrent insert's on same table"]; 5140: smp_insert(suite) -> []; 5141: smp_insert(Config) when is_list(Config) -> 5142: ets_new(smp_insert,[named_table,public,{write_concurrency,true}]), 5143: InitF = fun(_) -> ok end, 5144: ExecF = fun(_) -> true = ets:insert(smp_insert,{random:uniform(10000)}) 5145: end, 5146: FiniF = fun(_) -> ok end, 5147: run_workers(InitF,ExecF,FiniF,100000), 5148: verify_table_load(smp_insert), 5149: ets:delete(smp_insert). 5150: 5151: smp_fixed_delete(doc) -> ["Concurrent delete's on same fixated table"]; 5152: smp_fixed_delete(suite) -> []; 5153: smp_fixed_delete(Config) when is_list(Config) -> 5154: only_if_smp(fun()->smp_fixed_delete_do() end). 5155: 5156: smp_fixed_delete_do() -> 5157: T = ets_new(foo,[public,{write_concurrency,true}]), 5158: %%Mem = ets:info(T,memory), 5159: NumOfObjs = 100000, 5160: filltabint(T,NumOfObjs), 5161: ets:safe_fixtable(T,true), 5162: Buckets = num_of_buckets(T), 5163: InitF = fun([ProcN,NumOfProcs|_]) -> {ProcN,NumOfProcs} end, 5164: ExecF = fun({Key,_}) when Key > NumOfObjs -> 5165: [end_of_work]; 5166: ({Key,Increment}) -> 5167: true = ets:delete(T,Key), 5168: {Key+Increment,Increment} 5169: end, 5170: FiniF = fun(_) -> ok end, 5171: run_workers_do(InitF,ExecF,FiniF,NumOfObjs), 5172: ?line 0 = ets:info(T,size), 5173: ?line true = ets:info(T,fixed), 5174: ?line Buckets = num_of_buckets(T), 5175: ?line NumOfObjs = get_kept_objects(T), 5176: ets:safe_fixtable(T,false), 5177: %% Will fail as unfix does not shrink the table: 5178: %%?line Mem = ets:info(T,memory), 5179: %%verify_table_load(T), 5180: ets:delete(T). 5181: 5182: num_of_buckets(T) -> 5183: ?line element(1,ets:info(T,stats)). 5184: 5185: smp_unfix_fix(doc) -> ["Fixate hash table while other process is busy doing unfix"]; 5186: smp_unfix_fix(suite) -> []; 5187: smp_unfix_fix(Config) when is_list(Config) -> 5188: only_if_smp(fun()-> smp_unfix_fix_do() end). 5189: 5190: smp_unfix_fix_do() -> 5191: process_flag(scheduler,1), 5192: Parent = self(), 5193: T = ets_new(foo,[public,{write_concurrency,true}]), 5194: %%Mem = ets:info(T,memory), 5195: NumOfObjs = 100000, 5196: Deleted = 50000, 5197: filltabint(T,NumOfObjs), 5198: ets:safe_fixtable(T,true), 5199: Buckets = num_of_buckets(T), 5200: ?line Deleted = ets:select_delete(T,[{{'$1', '_'}, 5201: [{'=<','$1', Deleted}], 5202: [true]}]), 5203: ?line Buckets = num_of_buckets(T), 5204: Left = NumOfObjs - Deleted, 5205: ?line Left = ets:info(T,size), 5206: ?line true = ets:info(T,fixed), 5207: ?line Deleted = get_kept_objects(T), 5208: 5209: {Child, Mref} = 5210: my_spawn_opt(fun()-> ?line true = ets:info(T,fixed), 5211: Parent ! start, 5212: io:format("Child waiting for table to be unfixed... now=~p mem=~p\n", 5213: [now(),ets:info(T,memory)]), 5214: repeat_while(fun()-> ets:info(T,fixed) end), 5215: io:format("Table unfixed. Child Fixating! now=~p mem=~p\n", 5216: [now(),ets:info(T,memory)]), 5217: ?line true = ets:safe_fixtable(T,true), 5218: repeat_while(fun(Key) when Key =< NumOfObjs -> 5219: ets:delete(T,Key), {true,Key+1}; 5220: (Key) -> {false,Key} 5221: end, 5222: Deleted), 5223: ?line 0 = ets:info(T,size), 5224: ?line true = get_kept_objects(T) >= Left, 5225: ?line done = receive_any() 5226: end, 5227: [link, monitor, {scheduler,2}]), 5228: 5229: ?line start = receive_any(), 5230: ?line true = ets:info(T,fixed), 5231: io:format("Parent starting to unfix... ~p\n",[now()]), 5232: ets:safe_fixtable(T,false), 5233: io:format("Parent done with unfix. ~p\n",[now()]), 5234: Child ! done, 5235: {'DOWN', Mref, process, Child, normal} = receive_any(), 5236: ?line false = ets:info(T,fixed), 5237: ?line 0 = get_kept_objects(T), 5238: %%verify_table_load(T), 5239: ets:delete(T), 5240: process_flag(scheduler,0). 5241: 5242: otp_8166(doc) -> ["Unsafe unfix was done by trapping select/match"]; 5243: otp_8166(suite) -> []; 5244: otp_8166(Config) when is_list(Config) -> 5245: only_if_smp(3, fun()-> otp_8166_do(false), 5246: otp_8166_do(true) 5247: end). 5248: 5249: otp_8166_do(WC) -> 5250: %% Bug scenario: One process segv while reading the table because another 5251: %% process is doing unfix without write-lock at the end of a trapping match_object. 5252: process_flag(scheduler,1), 5253: T = ets_new(foo,[public, {write_concurrency,WC}]), 5254: NumOfObjs = 3000, %% Need more than 1000 live objects for match_object to trap one time 5255: Deleted = NumOfObjs div 2, 5256: filltabint(T,NumOfObjs), 5257: {ReaderPid, ReaderMref} = 5258: my_spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end, 5259: [link, monitor, {scheduler,2}]), 5260: {ZombieCrPid, ZombieCrMref} = 5261: my_spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end, 5262: [link, monitor, {scheduler,3}]), 5263: 5264: repeat(fun() -> ZombieCrPid ! {loop, self()}, 5265: zombies_created = receive_any(), 5266: otp_8166_trapper(T, 10, ZombieCrPid) 5267: end, 5268: 100), 5269: 5270: ReaderPid ! quit, 5271: {'DOWN', ReaderMref, process, ReaderPid, normal} = receive_any(), 5272: ZombieCrPid ! quit, 5273: {'DOWN', ZombieCrMref, process, ZombieCrPid, normal} = receive_any(), 5274: ?line false = ets:info(T,fixed), 5275: ?line 0 = get_kept_objects(T), 5276: %%verify_table_load(T), 5277: ets:delete(T), 5278: process_flag(scheduler,0). 5279: 5280: %% Keep reading the table 5281: otp_8166_reader(T, NumOfObjs) -> 5282: repeat_while(fun(0) -> 5283: receive quit -> {false,done} 5284: after 0 -> {true,NumOfObjs} 5285: end; 5286: (Key) -> 5287: ets:lookup(T,Key), 5288: {true, Key-1} 5289: end, 5290: NumOfObjs). 5291: 5292: %% Do a match_object that will trap and thereby fixate and then unfixate the table 5293: otp_8166_trapper(T, Try, ZombieCrPid) -> 5294: [] = ets:match_object(T,{'_',"Pink Unicorn"}), 5295: case {ets:info(T,fixed),Try} of 5296: {true,1} -> 5297: io:format("failed to provoke unsafe unfix, give up...\n",[]), 5298: ZombieCrPid ! unfix; 5299: {true,_} -> 5300: io:format("trapper too fast, trying again...\n",[]), 5301: otp_8166_trapper(T, Try-1, ZombieCrPid); 5302: {false,_} -> done 5303: end. 5304: 5305: 5306: %% Fixate table and create some pseudo-deleted objects (zombies) 5307: %% Then wait for trapper to fixate before unfixing, as we want the trappers' 5308: %% unfix to be the one that purges the zombies. 5309: otp_8166_zombie_creator(T,Deleted) -> 5310: case receive_any() of 5311: quit -> done; 5312: 5313: {loop,Pid} -> 5314: filltabint(T,Deleted), 5315: ets:safe_fixtable(T,true), 5316: ?line Deleted = ets:select_delete(T,[{{'$1', '_'}, 5317: [{'=<','$1', Deleted}], 5318: [true]}]), 5319: Pid ! zombies_created, 5320: repeat_while(fun() -> case ets:info(T,safe_fixed) of 5321: {_,[_P1,_P2]} -> 5322: false; 5323: _ -> 5324: receive unfix -> false 5325: after 0 -> true 5326: end 5327: end 5328: end), 5329: ets:safe_fixtable(T,false), 5330: otp_8166_zombie_creator(T,Deleted); 5331: 5332: unfix -> 5333: io:format("ignore unfix in outer loop?\n",[]), 5334: otp_8166_zombie_creator(T,Deleted) 5335: end. 5336: 5337: 5338: 5339: 5340: verify_table_load(T) -> 5341: ?line Stats = ets:info(T,stats), 5342: ?line {Buckets,AvgLen,StdDev,ExpSD,_MinLen,_MaxLen,_} = Stats, 5343: ?line ok = if 5344: AvgLen > 7 -> 5345: io:format("Table overloaded: Stats=~p\n~p\n", 5346: [Stats, ets:info(T)]), 5347: false; 5348: 5349: Buckets>256, AvgLen < 6 -> 5350: io:format("Table underloaded: Stats=~p\n~p\n", 5351: [Stats, ets:info(T)]), 5352: false; 5353: 5354: StdDev > ExpSD*2 -> 5355: io:format("Too large standard deviation (poor hashing?)," 5356: " stats=~p\n~p\n",[Stats, ets:info(T)]), 5357: false; 5358: 5359: true -> 5360: io:format("Stats = ~p\n",[Stats]), 5361: ok 5362: end. 5363: 5364: 5365: otp_8732(doc) -> ["ets:select on a tree with NIL key object"]; 5366: otp_8732(Config) when is_list(Config) -> 5367: Tab = ets_new(noname,[ordered_set]), 5368: filltabstr(Tab,999), 5369: ets:insert(Tab,{[],"nasty NIL object"}), 5370: ?line [] = ets:match(Tab,{'_',nomatch}), %% Will hang if bug not fixed 5371: ok. 5372: 5373: 5374: smp_select_delete(suite) -> []; 5375: smp_select_delete(doc) -> 5376: ["Run concurrent select_delete (and inserts) on same table."]; 5377: smp_select_delete(Config) when is_list(Config) -> 5378: T = ets_new(smp_select_delete,[named_table,public,{write_concurrency,true}]), 5379: Mod = 17, 5380: Zeros = erlang:make_tuple(Mod,0), 5381: InitF = fun(_) -> Zeros end, 5382: ExecF = fun(Diffs0) -> 5383: case random:uniform(20) of 5384: 1 -> 5385: Mod = 17, 5386: Eq = random:uniform(Mod) - 1, 5387: Deleted = ets:select_delete(T, 5388: [{{'_', '$1'}, 5389: [{'=:=', {'rem', '$1', Mod}, Eq}], 5390: [true]}]), 5391: Diffs1 = setelement(Eq+1, Diffs0, 5392: element(Eq+1,Diffs0) - Deleted), 5393: Diffs1; 5394: _ -> 5395: Key = random:uniform(10000), 5396: Eq = Key rem Mod, 5397: ?line case ets:insert_new(T,{Key,Key}) of 5398: true -> 5399: Diffs1 = setelement(Eq+1, Diffs0, 5400: element(Eq+1,Diffs0)+1), 5401: Diffs1; 5402: false -> Diffs0 5403: end 5404: end 5405: end, 5406: FiniF = fun(Result) -> Result end, 5407: Results = run_workers_do(InitF,ExecF,FiniF,20000), 5408: ?line TotCnts = lists:foldl(fun(Diffs, Sum) -> add_lists(Sum,tuple_to_list(Diffs)) end, 5409: lists:duplicate(Mod, 0), Results), 5410: io:format("TotCnts = ~p\n",[TotCnts]), 5411: ?line LeftInTab = lists:foldl(fun(N,Sum) -> Sum+N end, 5412: 0, TotCnts), 5413: io:format("LeftInTab = ~p\n",[LeftInTab]), 5414: ?line LeftInTab = ets:info(T,size), 5415: lists:foldl(fun(Cnt,Eq) -> 5416: WasCnt = ets:select_count(T, 5417: [{{'_', '$1'}, 5418: [{'=:=', {'rem', '$1', Mod}, Eq}], 5419: [true]}]), 5420: io:format("~p: ~p =?= ~p\n",[Eq,Cnt,WasCnt]), 5421: ?line Cnt = WasCnt, 5422: Eq+1 5423: end, 5424: 0, TotCnts), 5425: verify_table_load(T), 5426: ?line LeftInTab = ets:select_delete(T, [{{'$1','$1'}, [], [true]}]), 5427: ?line 0 = ets:info(T,size), 5428: ?line false = ets:info(T,fixed), 5429: ets:delete(T). 5430: 5431: types(doc) -> ["Test different types"]; 5432: types(Config) when is_list(Config) -> 5433: init_externals(), 5434: repeat_for_opts(types_do,[[set,ordered_set],compressed]). 5435: 5436: types_do(Opts) -> 5437: EtsMem = etsmem(), 5438: ?line T = ets_new(xxx,Opts), 5439: Fun = fun(Term) -> 5440: ets:insert(T,{Term}), 5441: ?line [{Term}] = ets:lookup(T,Term), 5442: ets:insert(T,{Term,xxx}), 5443: ?line [{Term,xxx}] = ets:lookup(T,Term), 5444: ets:insert(T,{Term,"xxx"}), 5445: ?line [{Term,"xxx"}] = ets:lookup(T,Term), 5446: ets:insert(T,{xxx,Term}), 5447: ?line [{xxx,Term}] = ets:lookup(T,xxx), 5448: ets:insert(T,{"xxx",Term}), 5449: ?line [{"xxx",Term}] = ets:lookup(T,"xxx"), 5450: ets:delete_all_objects(T), 5451: ?line 0 = ets:info(T,size) 5452: end, 5453: test_terms(Fun, strict), 5454: ets:delete(T), 5455: ?line verify_etsmem(EtsMem). 5456: 5457: 5458: %% OTP-9932: Memory overwrite when inserting large integers in compressed bag. 5459: %% Will crash with segv on 64-bit opt if not fixed. 5460: otp_9932(Config) when is_list(Config) -> 5461: T = ets:new(xxx, [bag, compressed]), 5462: Fun = fun(N) -> 5463: Key = {1316110174588445 bsl N,1316110174588583 bsl N}, 5464: S = {Key, Key}, 5465: true = ets:insert(T, S), 5466: [S] = ets:lookup(T, Key), 5467: true = ets:insert(T, S), 5468: [S] = ets:lookup(T, Key) 5469: end, 5470: lists:foreach(Fun, lists:seq(0, 16)), 5471: ets:delete(T). 5472: 5473: 5474: otp_9423(doc) -> ["vm-deadlock caused by race between ets:delete and others on write_concurrency table"]; 5475: otp_9423(Config) when is_list(Config) -> 5476: InitF = fun(_) -> {0,0} end, 5477: ExecF = fun({S,F}) -> 5478: receive 5479: stop -> 5480: io:format("~p got stop\n", [self()]), 5481: [end_of_work | {"Succeded=",S,"Failed=",F}] 5482: after 0 -> 5483: %%io:format("~p (~p) doing lookup\n", [self(), {S,F}]), 5484: try ets:lookup(otp_9423, key) of 5485: [] -> {S+1,F} 5486: catch 5487: error:badarg -> {S,F+1} 5488: end 5489: end 5490: end, 5491: FiniF = fun(R) -> R end, 5492: case run_workers(InitF, ExecF, FiniF, infinite, 1) of 5493: Pids when is_list(Pids) -> 5494: %%[P ! start || P <- Pids], 5495: repeat(fun() -> ets:new(otp_9423, [named_table, public, {write_concurrency,true}]), 5496: ets:delete(otp_9423) 5497: end, 10000), 5498: [P ! stop || P <- Pids], 5499: wait_pids(Pids), 5500: ok; 5501: 5502: Skipped -> Skipped 5503: end. 5504: 5505: 5506: %% Corrupted binary in compressed table 5507: otp_10182(Config) when is_list(Config) -> 5508: Bin = <<"aHR0cDovL2hvb3RzdWl0ZS5jb20vYy9wcm8tYWRyb2xsLWFi">>, 5509: Key = {test, Bin}, 5510: Value = base64:decode(Bin), 5511: In = {Key,Value}, 5512: Db = ets:new(undefined, [set, protected, {read_concurrency, true}, compressed]), 5513: ets:insert(Db, In), 5514: [Out] = ets:lookup(Db, Key), 5515: io:format("In : ~p\nOut: ~p\n", [In,Out]), 5516: ets:delete(Db), 5517: In = Out. 5518: 5519: 5520: 5521: 5522: % 5523: % Utility functions: 5524: % 5525: 5526: add_lists(L1,L2) -> 5527: add_lists(L1,L2,[]). 5528: add_lists([],[],Acc) -> 5529: lists:reverse(Acc); 5530: add_lists([E1|T1], [E2|T2], Acc) -> 5531: add_lists(T1, T2, [E1+E2 | Acc]). 5532: 5533: run_workers(InitF,ExecF,FiniF,Laps) -> 5534: run_workers(InitF,ExecF,FiniF,Laps, 0). 5535: run_workers(InitF,ExecF,FiniF,Laps, Exclude) -> 5536: case erlang:system_info(smp_support) of 5537: true -> 5538: run_workers_do(InitF,ExecF,FiniF,Laps, Exclude); 5539: false -> 5540: {skipped,"No smp support"} 5541: end. 5542: 5543: run_workers_do(InitF,ExecF,FiniF,Laps) -> 5544: run_workers_do(InitF,ExecF,FiniF,Laps, 0). 5545: run_workers_do(InitF,ExecF,FiniF,Laps, Exclude) -> 5546: ?line NumOfProcs = case erlang:system_info(schedulers) of 5547: N when (N > Exclude) -> N - Exclude 5548: end, 5549: io:format("smp starting ~p workers\n",[NumOfProcs]), 5550: Seeds = [{ProcN,random:uniform(9999)} || ProcN <- lists:seq(1,NumOfProcs)], 5551: Parent = self(), 5552: Pids = [my_spawn_link(fun()-> worker(Seed,InitF,ExecF,FiniF,Laps,Parent,NumOfProcs) end) 5553: || Seed <- Seeds], 5554: case Laps of 5555: infinite -> Pids; 5556: _ -> wait_pids(Pids) 5557: end. 5558: 5559: worker({ProcN,Seed}, InitF, ExecF, FiniF, Laps, Parent, NumOfProcs) -> 5560: io:format("smp worker ~p, seed=~p~n",[self(),Seed]), 5561: random:seed(Seed,Seed,Seed), 5562: State1 = InitF([ProcN, NumOfProcs]), 5563: State2 = worker_loop(Laps, ExecF, State1), 5564: Result = FiniF(State2), 5565: io:format("worker ~p done\n",[self()]), 5566: Parent ! {self(), Result}. 5567: 5568: worker_loop(0, _, State) -> 5569: State; 5570: worker_loop(_, _, [end_of_work|State]) -> 5571: State; 5572: worker_loop(infinite, ExecF, State) -> 5573: worker_loop(infinite,ExecF,ExecF(State)); 5574: worker_loop(N, ExecF, State) -> 5575: worker_loop(N-1,ExecF,ExecF(State)). 5576: 5577: wait_pids(Pids) -> 5578: wait_pids(Pids,[]). 5579: wait_pids([],Acc) -> 5580: Acc; 5581: wait_pids(Pids, Acc) -> 5582: receive 5583: {Pid,Result} -> 5584: ?line true = lists:member(Pid,Pids), 5585: Others = lists:delete(Pid,Pids), 5586: io:format("wait_pid got ~p from ~p, still waiting for ~p\n",[Result,Pid,Others]), 5587: wait_pids(Others,[Result | Acc]) 5588: end. 5589: 5590: 5591: 5592: 5593: my_tab_to_list(Ts) -> 5594: Key = ets:first(Ts), 5595: my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)]). 5596: 5597: my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc); 5598: my_tab_to_list(Ts,Key, Acc) -> 5599: my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]). 5600: 5601: 5602: wait_for_memory_deallocations() -> 5603: try 5604: erts_debug:set_internal_state(wait, deallocations) 5605: catch 5606: error:undef -> 5607: erts_debug:set_internal_state(available_internal_state, true), 5608: wait_for_memory_deallocations() 5609: end. 5610: 5611: 5612: etsmem() -> 5613: wait_for_memory_deallocations(), 5614: 5615: AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size), 5616: ets:info(T,memory),ets:info(T,type)} 5617: end, ets:all()), 5618: 5619: EtsAllocInfo = erlang:system_info({allocator,ets_alloc}), 5620: ErlangMemoryEts = try erlang:memory(ets) catch error:notsup -> notsup end, 5621: 5622: Mem = 5623: {ErlangMemoryEts, 5624: case EtsAllocInfo of 5625: false -> undefined; 5626: MemInfo -> 5627: CS = lists:foldl( 5628: fun ({instance, _, L}, Acc) -> 5629: {value,{mbcs,MBCS}} = lists:keysearch(mbcs, 1, L), 5630: {value,{sbcs,SBCS}} = lists:keysearch(sbcs, 1, L), 5631: NewAcc = [MBCS, SBCS | Acc], 5632: case lists:keysearch(mbcs_pool, 1, L) of 5633: {value,{mbcs_pool, MBCS_POOL}} -> 5634: [MBCS_POOL|NewAcc]; 5635: _ -> NewAcc 5636: end 5637: end, 5638: [], 5639: MemInfo), 5640: lists:foldl( 5641: fun(L, {Bl0,BlSz0}) -> 5642: {value,BlTup} = lists:keysearch(blocks, 1, L), 5643: blocks = element(1, BlTup), 5644: Bl = element(2, BlTup), 5645: {value,BlSzTup} = lists:keysearch(blocks_size, 1, L), 5646: blocks_size = element(1, BlSzTup), 5647: BlSz = element(2, BlSzTup), 5648: {Bl0+Bl,BlSz0+BlSz} 5649: end, {0,0}, CS) 5650: end}, 5651: {Mem,AllTabs}. 5652: 5653: verify_etsmem({MemInfo,AllTabs}) -> 5654: wait_for_test_procs(), 5655: case etsmem() of 5656: {MemInfo,_} -> 5657: io:format("Ets mem info: ~p", [MemInfo]), 5658: case MemInfo of 5659: {ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined -> 5660: %% Use 'erl +Mea max' to do more complete memory leak testing. 5661: {comment,"Incomplete or no mem leak testing"}; 5662: _ -> 5663: ok 5664: end; 5665: {MemInfo2, AllTabs2} -> 5666: io:format("Expected: ~p", [MemInfo]), 5667: io:format("Actual: ~p", [MemInfo2]), 5668: io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]), 5669: io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]), 5670: ets_test_spawn_logger ! failed_memcheck, 5671: {comment, "Failed memory check"} 5672: end. 5673: 5674: 5675: start_loopers(N, Prio, Fun, State) -> 5676: lists:map(fun (_) -> 5677: my_spawn_opt(fun () -> looper(Fun, State) end, 5678: [{priority, Prio}, link]) 5679: end, 5680: lists:seq(1, N)). 5681: 5682: stop_loopers(Loopers) -> 5683: lists:foreach(fun (P) -> 5684: unlink(P), 5685: exit(P, bang) 5686: end, 5687: Loopers), 5688: ok. 5689: 5690: looper(Fun, State) -> 5691: looper(Fun, Fun(State)). 5692: 5693: spawn_logger(Procs, FailedMemchecks) -> 5694: receive 5695: {new_test_proc, Proc} -> 5696: spawn_logger([Proc|Procs], FailedMemchecks); 5697: {sync_test_procs, Kill, From} -> 5698: lists:foreach(fun (Proc) when From == Proc -> 5699: ok; 5700: (Proc) -> 5701: Mon = erlang:monitor(process, Proc), 5702: receive 5703: {'DOWN', Mon, _, _, _} -> 5704: ok 5705: after 0 -> 5706: case Kill of 5707: true -> exit(Proc, kill); 5708: _ -> ok 5709: end, 5710: erlang:display({"Waiting for 'DOWN' from", Proc, 5711: process_info(Proc), pid_status(Proc)}), 5712: receive 5713: {'DOWN', Mon, _, _, _} -> 5714: ok 5715: end 5716: end 5717: end, Procs), 5718: From ! test_procs_synced, 5719: spawn_logger([From], FailedMemchecks); 5720: 5721: failed_memcheck -> 5722: spawn_logger(Procs, FailedMemchecks+1); 5723: 5724: {Pid, get_failed_memchecks} -> 5725: Pid ! {get_failed_memchecks, FailedMemchecks}, 5726: spawn_logger(Procs, FailedMemchecks) 5727: end. 5728: 5729: pid_status(Pid) -> 5730: try 5731: erts_debug:get_internal_state({process_status, Pid}) 5732: catch 5733: error:undef -> 5734: erts_debug:set_internal_state(available_internal_state, true), 5735: pid_status(Pid) 5736: end. 5737: 5738: start_spawn_logger() -> 5739: case whereis(ets_test_spawn_logger) of 5740: Pid when is_pid(Pid) -> true; 5741: _ -> register(ets_test_spawn_logger, 5742: spawn_opt(fun () -> spawn_logger([], 0) end, 5743: [{priority, max}])) 5744: end. 5745: 5746: %% restart_spawn_logger() -> 5747: %% stop_spawn_logger(), 5748: %% start_spawn_logger(). 5749: 5750: stop_spawn_logger() -> 5751: Mon = erlang:monitor(process, ets_test_spawn_logger), 5752: (catch exit(whereis(ets_test_spawn_logger), kill)), 5753: receive {'DOWN', Mon, _, _, _} -> ok end. 5754: 5755: wait_for_test_procs() -> 5756: wait_for_test_procs(false). 5757: 5758: wait_for_test_procs(Kill) -> 5759: ets_test_spawn_logger ! {sync_test_procs, Kill, self()}, 5760: receive test_procs_synced -> ok end. 5761: 5762: log_test_proc(Proc) when is_pid(Proc) -> 5763: ets_test_spawn_logger ! {new_test_proc, Proc}, 5764: Proc. 5765: 5766: my_spawn(Fun) -> log_test_proc(spawn(Fun)). 5767: %%my_spawn(M,F,A) -> log_test_proc(spawn(M,F,A)). 5768: %%my_spawn(N,M,F,A) -> log_test_proc(spawn(N,M,F,A)). 5769: 5770: my_spawn_link(Fun) -> log_test_proc(spawn_link(Fun)). 5771: my_spawn_link(M,F,A) -> log_test_proc(spawn_link(M,F,A)). 5772: %%my_spawn_link(N,M,F,A) -> log_test_proc(spawn_link(N,M,F,A)). 5773: 5774: my_spawn_opt(Fun,Opts) -> 5775: case spawn_opt(Fun,Opts) of 5776: Pid when is_pid(Pid) -> log_test_proc(Pid); 5777: {Pid, _} = Res when is_pid(Pid) -> log_test_proc(Pid), Res 5778: end. 5779: 5780: my_spawn_monitor(Fun) -> 5781: Res = spawn_monitor(Fun), 5782: {Pid, _} = Res, 5783: log_test_proc(Pid), 5784: Res. 5785: 5786: repeat(_Fun, 0) -> 5787: ok; 5788: repeat(Fun, N) -> 5789: Fun(), 5790: repeat(Fun, N-1). 5791: 5792: repeat_while(Fun) -> 5793: case Fun() of 5794: true -> repeat_while(Fun); 5795: false -> false 5796: end. 5797: 5798: repeat_while(Fun, Arg0) -> 5799: case Fun(Arg0) of 5800: {true,Arg1} -> repeat_while(Fun,Arg1); 5801: {false,Ret} -> Ret 5802: end. 5803: 5804: %% Some (but not all) permutations of List 5805: repeat_for_permutations(Fun, List) -> 5806: repeat_for_permutations(Fun, List, length(List)-1). 5807: repeat_for_permutations(Fun, List, 0) -> 5808: Fun(List); 5809: repeat_for_permutations(Fun, List, N) -> 5810: {A,B} = lists:split(N, List), 5811: L1 = B++A, 5812: L2 = lists:reverse(L1), 5813: L3 = B++lists:reverse(A), 5814: L4 = lists:reverse(B)++A, 5815: Fun(L1), Fun(L2), Fun(L3), Fun(L4), 5816: repeat_for_permutations(Fun, List, N-1). 5817: 5818: receive_any() -> 5819: receive M -> 5820: io:format("Process ~p got msg ~p\n", [self(),M]), 5821: M 5822: end. 5823: 5824: receive_any_spinning() -> 5825: receive_any_spinning(1000000). 5826: receive_any_spinning(Loops) -> 5827: receive_any_spinning(Loops,Loops,1). 5828: receive_any_spinning(Loops,0,Tries) -> 5829: receive M -> 5830: io:format("Spinning process ~p got msg ~p after ~p tries\n", [self(),M,Tries]), 5831: M 5832: after 0 -> 5833: receive_any_spinning(Loops, Loops, Tries+1) 5834: end; 5835: receive_any_spinning(Loops, N, Tries) when N>0 -> 5836: receive_any_spinning(Loops, N-1, Tries). 5837: 5838: 5839: 5840: spawn_monitor_with_pid(Pid, Fun) when is_pid(Pid) -> 5841: spawn_monitor_with_pid(Pid, Fun, 10). 5842: 5843: spawn_monitor_with_pid(_, _, 0) -> 5844: failed; 5845: spawn_monitor_with_pid(Pid, Fun, N) -> 5846: case my_spawn(fun()-> case self() of 5847: Pid -> Fun(); 5848: _ -> die 5849: end 5850: end) of 5851: Pid -> 5852: {Pid, erlang:monitor(process, Pid)}; 5853: _Other -> 5854: spawn_monitor_with_pid(Pid,Fun,N-1) 5855: end. 5856: 5857: 5858: only_if_smp(Func) -> 5859: only_if_smp(2, Func). 5860: only_if_smp(Schedulers, Func) -> 5861: case {erlang:system_info(smp_support), 5862: erlang:system_info(schedulers_online)} of 5863: {false,_} -> {skip,"No smp support"}; 5864: {true,N} when N < Schedulers -> {skip,"Too few schedulers online"}; 5865: {true,_} -> Func() 5866: end. 5867: 5868: %% Copy-paste from emulator/test/binary_SUITE.erl 5869: -define(heap_binary_size, 64). 5870: test_terms(Test_Func, Mode) -> 5871: garbage_collect(), 5872: ?line Pib0 = process_info(self(),binary), 5873: 5874: ?line Test_Func(atom), 5875: ?line Test_Func(''), 5876: ?line Test_Func('a'), 5877: ?line Test_Func('ab'), 5878: ?line Test_Func('abc'), 5879: ?line Test_Func('abcd'), 5880: ?line Test_Func('abcde'), 5881: ?line Test_Func('abcdef'), 5882: ?line Test_Func('abcdefg'), 5883: ?line Test_Func('abcdefgh'), 5884: 5885: ?line Test_Func(fun() -> ok end), 5886: X = id([a,{b,c},c]), 5887: Y = id({x,y,z}), 5888: Z = id(1 bsl 8*257), 5889: ?line Test_Func(fun() -> X end), 5890: ?line Test_Func(fun() -> {X,Y} end), 5891: ?line Test_Func([fun() -> {X,Y,Z} end, 5892: fun() -> {Z,X,Y} end, 5893: fun() -> {Y,Z,X} end]), 5894: 5895: ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}), 5896: ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}}, 5897: {1,2,3}}), 5898: 5899: ?line Test_Func(1), 5900: ?line Test_Func(42), 5901: ?line Test_Func(-23), 5902: ?line Test_Func(256), 5903: ?line Test_Func(25555), 5904: ?line Test_Func(-3333), 5905: 5906: ?line Test_Func(1.0), 5907: 5908: ?line Test_Func(183749783987483978498378478393874), 5909: ?line Test_Func(-37894183749783987483978498378478393874), 5910: Very_Big = very_big_num(), 5911: ?line Test_Func(Very_Big), 5912: ?line Test_Func(-Very_Big+1), 5913: 5914: ?line Test_Func([]), 5915: ?line Test_Func("abcdef"), 5916: ?line Test_Func([a, b, 1, 2]), 5917: ?line Test_Func([a|b]), 5918: 5919: ?line Test_Func({}), 5920: ?line Test_Func({1}), 5921: ?line Test_Func({a, b}), 5922: ?line Test_Func({a, b, c}), 5923: ?line Test_Func(list_to_tuple(lists:seq(0, 255))), 5924: ?line Test_Func(list_to_tuple(lists:seq(0, 256))), 5925: 5926: ?line Test_Func(make_ref()), 5927: ?line Test_Func([make_ref(), make_ref()]), 5928: 5929: ?line Test_Func(make_port()), 5930: 5931: ?line Test_Func(make_pid()), 5932: ?line Test_Func(make_ext_pid()), 5933: ?line Test_Func(make_ext_port()), 5934: ?line Test_Func(make_ext_ref()), 5935: 5936: Bin0 = list_to_binary(lists:seq(0, 14)), 5937: ?line Test_Func(Bin0), 5938: Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size)), 5939: ?line Test_Func(Bin1), 5940: Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1)), 5941: ?line Test_Func(Bin2), 5942: Bin3 = list_to_binary(lists:seq(0, 255)), 5943: garbage_collect(), 5944: Pib = process_info(self(),binary), 5945: ?line Test_Func(Bin3), 5946: garbage_collect(), 5947: case Mode of 5948: strict -> ?line Pib = process_info(self(),binary); 5949: skip_refc_check -> ok 5950: end, 5951: 5952: ?line Test_Func(make_unaligned_sub_binary(Bin0)), 5953: ?line Test_Func(make_unaligned_sub_binary(Bin1)), 5954: ?line Test_Func(make_unaligned_sub_binary(Bin2)), 5955: ?line Test_Func(make_unaligned_sub_binary(Bin3)), 5956: 5957: ?line Test_Func(make_sub_binary(lists:seq(42, 43))), 5958: ?line Test_Func(make_sub_binary([42,43,44])), 5959: ?line Test_Func(make_sub_binary([42,43,44,45])), 5960: ?line Test_Func(make_sub_binary([42,43,44,45,46])), 5961: ?line Test_Func(make_sub_binary([42,43,44,45,46,47])), 5962: ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])), 5963: ?line Test_Func(make_sub_binary(lists:seq(42, 49))), 5964: ?line Test_Func(make_sub_binary(lists:seq(0, 14))), 5965: ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))), 5966: ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))), 5967: ?line Test_Func(make_sub_binary(lists:seq(0, 255))), 5968: 5969: ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))), 5970: ?line Test_Func(make_unaligned_sub_binary([42,43,44])), 5971: ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])), 5972: ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])), 5973: ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])), 5974: ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])), 5975: ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))), 5976: ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))), 5977: ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))), 5978: ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))), 5979: ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))), 5980: 5981: %% Bit level binaries. 5982: ?line Test_Func(<<1:1>>), 5983: ?line Test_Func(<<2:2>>), 5984: ?line Test_Func(<<42:10>>), 5985: ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), 5986: 5987: ?line Test_Func(F = fun(A) -> 42*A end), 5988: ?line Test_Func(lists:duplicate(32, F)), 5989: 5990: ?line Test_Func(FF = fun binary_SUITE:all/1), 5991: ?line Test_Func(lists:duplicate(32, FF)), 5992: 5993: garbage_collect(), 5994: case Mode of 5995: strict -> ?line Pib0 = process_info(self(),binary); 5996: skip_refc_check -> ok 5997: end, 5998: ok. 5999: 6000: 6001: id(I) -> I. 6002: 6003: very_big_num() -> 6004: very_big_num(33, 1). 6005: 6006: very_big_num(Left, Result) when Left > 0 -> 6007: ?line very_big_num(Left-1, Result*256); 6008: very_big_num(0, Result) -> 6009: ?line Result. 6010: 6011: make_port() -> 6012: ?line open_port({spawn, "efile"}, [eof]). 6013: 6014: make_pid() -> 6015: ?line spawn_link(?MODULE, sleeper, []). 6016: 6017: sleeper() -> 6018: ?line receive after infinity -> ok end. 6019: 6020: make_ext_pid() -> 6021: {Pid, _, _} = get(externals), 6022: Pid. 6023: 6024: make_ext_port() -> 6025: {_, Port, _} = get(externals), 6026: Port. 6027: make_ext_ref() -> 6028: {_, _, Ref} = get(externals), 6029: Ref. 6030: 6031: init_externals() -> 6032: case get(externals) of 6033: undefined -> 6034: OtherNode = {gurka@sallad, 1}, 6035: Res = {mk_pid(OtherNode, 7645, 8123), 6036: mk_port(OtherNode, 187489773), 6037: mk_ref(OtherNode, [262143, 1293964255, 3291964278])}, 6038: put(externals, Res); 6039: 6040: {_,_,_} -> ok 6041: end. 6042: 6043: %% 6044: %% Node container constructor functions 6045: %% 6046: 6047: -define(VERSION_MAGIC, 131). 6048: -define(PORT_EXT, 102). 6049: -define(PID_EXT, 103). 6050: -define(NEW_REFERENCE_EXT, 114). 6051: 6052: uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 -> 6053: [(Uint bsr 24) band 16#ff, 6054: (Uint bsr 16) band 16#ff, 6055: (Uint bsr 8) band 16#ff, 6056: Uint band 16#ff]; 6057: uint32_be(Uint) -> 6058: exit({badarg, uint32_be, [Uint]}). 6059: 6060: uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 -> 6061: [(Uint bsr 8) band 16#ff, 6062: Uint band 16#ff]; 6063: uint16_be(Uint) -> 6064: exit({badarg, uint16_be, [Uint]}). 6065: 6066: uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> 6067: Uint band 16#ff; 6068: uint8(Uint) -> 6069: exit({badarg, uint8, [Uint]}). 6070: 6071: mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> 6072: <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), 6073: mk_pid({NodeNameExt, Creation}, Number, Serial); 6074: mk_pid({NodeNameExt, Creation}, Number, Serial) -> 6075: case catch binary_to_term(list_to_binary([?VERSION_MAGIC, 6076: ?PID_EXT, 6077: NodeNameExt, 6078: uint32_be(Number), 6079: uint32_be(Serial), 6080: uint8(Creation)])) of 6081: Pid when is_pid(Pid) -> 6082: Pid; 6083: {'EXIT', {badarg, _}} -> 6084: exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]}); 6085: Other -> 6086: exit({unexpected_binary_to_term_result, Other}) 6087: end. 6088: 6089: mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> 6090: <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), 6091: mk_port({NodeNameExt, Creation}, Number); 6092: mk_port({NodeNameExt, Creation}, Number) -> 6093: case catch binary_to_term(list_to_binary([?VERSION_MAGIC, 6094: ?PORT_EXT, 6095: NodeNameExt, 6096: uint32_be(Number), 6097: uint8(Creation)])) of 6098: Port when is_port(Port) -> 6099: Port; 6100: {'EXIT', {badarg, _}} -> 6101: exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]}); 6102: Other -> 6103: exit({unexpected_binary_to_term_result, Other}) 6104: end. 6105: 6106: mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), 6107: is_integer(Creation), 6108: is_list(Numbers) -> 6109: <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), 6110: mk_ref({NodeNameExt, Creation}, Numbers); 6111: mk_ref({NodeNameExt, Creation}, Numbers) when is_binary(NodeNameExt), 6112: is_integer(Creation), 6113: is_list(Numbers) -> 6114: case catch binary_to_term(list_to_binary([?VERSION_MAGIC, 6115: ?NEW_REFERENCE_EXT, 6116: uint16_be(length(Numbers)), 6117: NodeNameExt, 6118: uint8(Creation), 6119: lists:map(fun (N) -> 6120: uint32_be(N) 6121: end, 6122: Numbers)])) of 6123: Ref when is_reference(Ref) -> 6124: Ref; 6125: {'EXIT', {badarg, _}} -> 6126: exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]}); 6127: Other -> 6128: exit({unexpected_binary_to_term_result, Other}) 6129: end. 6130: 6131: 6132: make_sub_binary(Bin) when is_binary(Bin) -> 6133: {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), 6134: B; 6135: make_sub_binary(List) -> 6136: make_sub_binary(list_to_binary(List)). 6137: 6138: make_unaligned_sub_binary(Bin0) when is_binary(Bin0) -> 6139: Bin1 = <<0:3,Bin0/binary,31:5>>, 6140: Sz = size(Bin0), 6141: <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), 6142: Bin; 6143: make_unaligned_sub_binary(List) -> 6144: make_unaligned_sub_binary(list_to_binary(List)). 6145: 6146: %% Repeat test function with different combination of table options 6147: %% 6148: repeat_for_opts(F) -> 6149: repeat_for_opts(F, [write_concurrency, read_concurrency, compressed]). 6150: 6151: repeat_for_opts(F, OptGenList) when is_atom(F) -> 6152: repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts) end, OptGenList); 6153: repeat_for_opts({F,Args}, OptGenList) when is_atom(F) -> 6154: repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts,Args) end, OptGenList); 6155: repeat_for_opts(F, OptGenList) -> 6156: repeat_for_opts(F, OptGenList, []). 6157: 6158: repeat_for_opts(F, [], Acc) -> 6159: lists:foldl(fun(Opts, RV_Acc) -> 6160: OptList = lists:filter(fun(E) -> E =/= void end, Opts), 6161: io:format("Calling with options ~p\n",[OptList]), 6162: RV = F(OptList), 6163: case RV_Acc of 6164: {comment,_} -> RV_Acc; 6165: _ -> case RV of 6166: {comment,_} -> RV; 6167: _ -> [RV | RV_Acc] 6168: end 6169: end 6170: end, [], Acc); 6171: repeat_for_opts(F, [OptList | Tail], []) when is_list(OptList) -> 6172: repeat_for_opts(F, Tail, [[Opt] || Opt <- OptList]); 6173: repeat_for_opts(F, [OptList | Tail], AccList) when is_list(OptList) -> 6174: repeat_for_opts(F, Tail, [[Opt|Acc] || Opt <- OptList, Acc <- AccList]); 6175: repeat_for_opts(F, [Atom | Tail], AccList) when is_atom(Atom) -> 6176: repeat_for_opts(F, [repeat_for_opts_atom2list(Atom) | Tail ], AccList). 6177: 6178: repeat_for_opts_atom2list(all_types) -> [set,ordered_set,bag,duplicate_bag]; 6179: repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}]; 6180: repeat_for_opts_atom2list(read_concurrency) -> [{read_concurrency,false},{read_concurrency,true}]; 6181: repeat_for_opts_atom2list(compressed) -> [compressed,void]. 6182: 6183: ets_new(Name, Opts) -> 6184: %%ets:new(Name, [compressed | Opts]). 6185: ets:new(Name, Opts).