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