1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1997-2013. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%
   21: -module(mnesia_isolation_test).
   22: -author('hakan@erix.ericsson.se').
   23: 
   24: -compile([export_all]).
   25: -include("mnesia_test_lib.hrl").
   26: 
   27: init_per_testcase(Func, Conf) ->
   28:     mnesia_test_lib:init_per_testcase(Func, Conf).
   29: 
   30: end_per_testcase(Func, Conf) ->
   31:     mnesia_test_lib:end_per_testcase(Func, Conf).
   32: 
   33: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   34: all() -> 
   35:     [{group, locking}, {group, visibility}].
   36: 
   37: groups() -> 
   38:     [{locking, [],
   39:       [no_conflict, simple_queue_conflict,
   40:        advanced_queue_conflict, simple_deadlock_conflict,
   41:        advanced_deadlock_conflict, lock_burst,
   42:        {group, sticky_locks}, {group, unbound_locking},
   43:        {group, admin_conflict}, nasty]},
   44:      {sticky_locks, [], [basic_sticky_functionality]},
   45:      {unbound_locking, [], [unbound1, unbound2]},
   46:      {admin_conflict, [],
   47:       [create_table, delete_table, move_table_copy,
   48:        add_table_index, del_table_index, transform_table,
   49:        snmp_open_table, snmp_close_table,
   50:        change_table_copy_type, change_table_access,
   51:        add_table_copy, del_table_copy, dump_tables,
   52:        {group, extra_admin_tests}]},
   53:      {extra_admin_tests, [],
   54:       [del_table_copy_1, del_table_copy_2, del_table_copy_3,
   55:        add_table_copy_1, add_table_copy_2, add_table_copy_3,
   56:        add_table_copy_4, move_table_copy_1, move_table_copy_2,
   57:        move_table_copy_3, move_table_copy_4]},
   58:      {visibility, [],
   59:       [dirty_updates_visible_direct,
   60:        dirty_reads_regardless_of_trans,
   61:        trans_update_invisibible_outside_trans,
   62:        trans_update_visible_inside_trans, write_shadows,
   63:        delete_shadows, write_delete_shadows_bag,
   64:        write_delete_shadows_bag2, {group, iteration},
   65:        shadow_search, snmp_shadows]},
   66:      {removed_resources, [], [rr_kill_copy]},
   67:      {iteration, [], [foldl, first_next]}].
   68: 
   69: init_per_group(_GroupName, Config) ->
   70:     Config.
   71: 
   72: end_per_group(_GroupName, Config) ->
   73:     Config.
   74: 
   75: 
   76: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   77: 
   78: 
   79: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   80: 
   81: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   82: no_conflict(suite) -> [];
   83: no_conflict(Config) when is_list(Config) ->
   84:     [Node1] = ?acquire_nodes(1, Config), 
   85:     Tab = no_conflict, 
   86:     create_conflict_table(Tab, [Node1]), 
   87:     Fun = fun(OtherOid, Lock1, Lock2) ->
   88: 		  %% Start two transactions
   89: 		  {success, [B, A]} = ?start_activities([Node1, Node1]), 
   90: 		  ?start_transactions([B, A]), 
   91: 		  
   92: 		  A ! fun() -> Lock1(one_oid(Tab)),  ok end, 
   93: 		  ?match_receive({A, ok}), 
   94: 		  B ! fun() -> Lock2(OtherOid),  ok end, 
   95: 		  ?match_receive({B, ok}), 
   96: 		  A ! fun() -> mnesia:abort(ok) end, 
   97: 		  ?match_receive({A, {aborted, ok}}), 
   98: 		  B ! fun() -> mnesia:abort(ok) end, 
   99: 		  ?match_receive({B, {aborted, ok}})
  100: 	  end, 
  101:     NoLocks = lock_funs(no_lock, any_granularity), 
  102:     SharedLocks = lock_funs(shared_lock, any_granularity), 
  103:     AnyLocks = lock_funs(any_lock, any_granularity), 
  104:     OneOneFun = fun(Lock1, Lock2) -> Fun(one_oid(Tab), Lock1, Lock2) end, 
  105:     fun_loop(OneOneFun, NoLocks, AnyLocks), 
  106:     fun_loop(OneOneFun, AnyLocks, NoLocks), 
  107:     fun_loop(OneOneFun, SharedLocks, SharedLocks), 
  108: 
  109:     %% Lock different objects
  110:     OneOtherFun = fun(Lock1, Lock2) -> Fun(other_oid(Tab), Lock1, Lock2) end, 
  111:     OneSharedLocks = lock_funs(shared_lock, one), 
  112:     OneExclusiveLocks = lock_funs(exclusive_lock, one), 
  113:     fun_loop(OneOtherFun, OneSharedLocks, OneExclusiveLocks), 
  114:     fun_loop(OneOtherFun, OneExclusiveLocks, OneSharedLocks), 
  115:     fun_loop(OneOtherFun, OneExclusiveLocks, OneExclusiveLocks), 
  116:     ok.
  117: 
  118: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  119: simple_queue_conflict(suite) -> [];
  120: simple_queue_conflict(Config) when is_list(Config) ->
  121:     [Node1] = ?acquire_nodes(1, Config), 
  122:     Tab = simple_queue_conflict, 
  123:     create_conflict_table(Tab, [Node1]), 
  124:     Fun = fun(OneLock, OtherLock) ->
  125: 		  %% Start two transactions
  126: 		  {success, [B, A]} = ?start_activities([Node1, Node1]), 
  127: 		  ?start_transactions([B, A]), 
  128: 		  
  129: 		  A ! fun() -> OneLock(one_oid(Tab)),  ok end, 
  130: 		  ?match_receive({A, ok}), 
  131: 		  B ! fun() -> OtherLock(one_oid(Tab)),  ok end, 
  132: 		  wait_for_lock(B, [Node1], 20), % Max 10 sec
  133: 		  A ! end_trans, 
  134: 		  ?match_multi_receive([{A, {atomic, end_trans}}, {B, ok}]), 
  135: 		  B ! fun() -> mnesia:abort(ok) end, 
  136: 		  ?match_receive({B, {aborted, ok}})
  137: 	  end, 
  138:     OneSharedLocks = lock_funs(shared_lock, one), 
  139:     AllSharedLocks = lock_funs(shared_lock, all), 
  140:     OneExclusiveLocks = lock_funs(exclusive_lock, one), 
  141:     AllExclusiveLocks = lock_funs(exclusive_lock, all), 
  142:     fun_loop(Fun, OneExclusiveLocks, OneExclusiveLocks), 
  143:     fun_loop(Fun, AllExclusiveLocks, AllExclusiveLocks), 
  144:     fun_loop(Fun, OneExclusiveLocks, AllExclusiveLocks), 
  145:     fun_loop(Fun, AllExclusiveLocks, OneExclusiveLocks), 
  146:     fun_loop(Fun, OneSharedLocks, AllExclusiveLocks), 
  147:     fun_loop(Fun, AllSharedLocks, OneExclusiveLocks), 
  148:     ok.
  149: 
  150: wait_for_lock(Pid, _Nodes, 0) ->
  151:     Queue = mnesia:system_info(lock_queue),
  152:     ?error("Timeout while waiting for lock on Pid ~p in queue ~p~n", [Pid, Queue]);
  153: wait_for_lock(Pid, Nodes, N) ->
  154:     rpc:multicall(Nodes, sys, get_status, [mnesia_locker]), 
  155:     List = [rpc:call(Node, mnesia, system_info, [lock_queue]) || Node <- Nodes],
  156:     Q = lists:append(List),
  157:     check_q(Pid, Q, Nodes, N).
  158: 
  159: check_q(Pid, [{_Oid, _Op, Pid, _Tid, _WFT} | _Tail], _N, _Count) -> ok;
  160: check_q(Pid, [_ | Tail], N, Count) -> check_q(Pid, Tail, N, Count);
  161: check_q(Pid, [], N, Count) ->
  162:     timer:sleep(500),
  163:     wait_for_lock(Pid, N, Count - 1).
  164: 
  165: 
  166: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  167: advanced_queue_conflict(suite) -> [];
  168: advanced_queue_conflict(Config) when is_list(Config) ->
  169:     [Node1] = ?acquire_nodes(1, Config), 
  170:     Tab = advanced_queue_conflict, 
  171:     create_conflict_table(Tab, [Node1]), 
  172:     OneRec = {Tab, 3, 3}, 
  173:     OneOid = {Tab, 3}, 
  174:     OtherRec = {Tab, 4, 4}, 
  175:     OtherOid = {Tab, 4}, 
  176:     
  177:     %% Start four transactions
  178:     {success, [D, C, B, A]} = ?start_activities(lists:duplicate(4, Node1)), 
  179:     ?start_transactions([D, C, B, A]), 
  180:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  181:     ?match([], mnesia:system_info(held_locks)), 
  182:     ?match([], mnesia:system_info(lock_queue)), 
  183: 
  184:     %% Acquire some locks
  185:     A ! fun() -> mnesia:write(OneRec) end, 
  186:     ?match_receive({A, ok}), 
  187:     A ! fun() -> mnesia:read(OneOid) end, 
  188:     ?match_receive({A, [OneRec]}), 
  189:     
  190:     B ! fun() -> mnesia:write(OtherRec) end, 
  191:     ?match_receive({B, ok}), 
  192:     B ! fun() -> mnesia:read(OneOid) end, 
  193:     ?match_receive(timeout), 
  194:     
  195:     C ! fun() -> mnesia:read(OtherOid) end, 
  196:     ?match_receive(timeout), 
  197:     D ! fun() -> mnesia:wread(OtherOid) end, 
  198:     ?match_receive(timeout), 
  199:     
  200:     %% and release them in a certain order
  201:     A ! end_trans, 
  202:     ?match_multi_receive([{A, {atomic, end_trans}}, {B, [OneRec]}]), 
  203:     B ! end_trans, 
  204:     ?match_multi_receive([{B, {atomic, end_trans}}, {C, [OtherRec]}]), 
  205:     C ! end_trans, 
  206:     ?match_multi_receive([{C, {atomic, end_trans}}, {D, [OtherRec]}]), 
  207:     D ! end_trans, 
  208:     ?match_receive({D, {atomic, end_trans}}), 
  209:     
  210:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  211:     ?match([], mnesia:system_info(held_locks)), 
  212:     ?match([], mnesia:system_info(lock_queue)), 
  213:     ok.
  214: 
  215: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  216: simple_deadlock_conflict(suite) -> [];
  217: simple_deadlock_conflict(Config) when is_list(Config) ->
  218:     [Node1] = ?acquire_nodes(1, Config), 
  219:     Tab = simple_deadlock_conflict, 
  220:     create_conflict_table(Tab, [Node1]), 
  221:     Rec = {Tab, 4, 4}, 
  222:     Oid = {Tab, 4}, 
  223:     
  224:     %% Start two transactions
  225:     {success, [B, A]} = ?start_activities(lists:duplicate(2, Node1)), 
  226:     mnesia_test_lib:start_transactions([B, A], 0),  % A is newest
  227:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  228:     ?match([], mnesia:system_info(held_locks)), 
  229:     ?match([], mnesia:system_info(lock_queue)), 
  230: 
  231:     B ! fun() -> mnesia:write(Rec) end, 
  232:     ?match_receive({B, ok}), 
  233:     A ! fun() -> mnesia:read(Oid) end, 
  234:     ?match_receive({A, {aborted, nomore}}), 
  235:     B ! end_trans, 
  236:     ?match_receive({B, {atomic, end_trans}}), 
  237: 
  238:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  239:     ?match([], mnesia:system_info(held_locks)), 
  240:     ?match([], mnesia:system_info(lock_queue)), 
  241:     ok.
  242: 
  243: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  244: advanced_deadlock_conflict(suite) -> [];
  245: advanced_deadlock_conflict(Config) when is_list(Config) ->
  246:     [Node1, Node2] = ?acquire_nodes(2, Config), 
  247:     Tab = advanced_deadlock_conflict, 
  248:     create_conflict_table(Tab, [Node2]), 
  249:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  250:     Rec = {Tab, 4, 4}, 
  251:     Oid = {Tab, 4}, 
  252:     
  253:     %% Start two transactions
  254:     {success, [B, A]} = ?start_activities([Node1, Node2]), 
  255:     mnesia_test_lib:start_sync_transactions([B, A], 0),  % A is newest
  256:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  257:     ?match([], mnesia:system_info(held_locks)), 
  258:     ?match([], mnesia:system_info(lock_queue)), 
  259: 
  260:     B ! fun() -> mnesia:write(Rec) end, 
  261:     ?match_receive({B, ok}), 
  262:     A ! fun() -> mnesia:read(Oid) end, 
  263:     ?match_receive({A, {aborted, nomore}}), 
  264:     B ! end_trans, 
  265:     ?match_receive({B, {atomic, end_trans}}), 
  266: 
  267:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  268:     ?match([], mnesia:system_info(held_locks)), 
  269:     ?match([], mnesia:system_info(lock_queue)), 
  270:     ok.
  271: 
  272: one_oid(Tab) -> {Tab, 1}.
  273: other_oid(Tab) -> {Tab, 2}.
  274:     
  275: create_conflict_table(Tab, Nodes) ->
  276:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
  277: 					     {ram_copies, Nodes}, 
  278: 					     {attributes, [key, val]}, 
  279: 					     {index, [val]}
  280: 					    ])),
  281:     ?match([], mnesia_test_lib:sync_tables(Nodes, [Tab])),
  282:     init_conflict_table(Tab).
  283: 
  284: init_conflict_table(Tab) ->
  285:     Recs = mnesia:dirty_match_object({Tab, '_', '_'}), 
  286:     lists:foreach(fun(R) -> mnesia:dirty_delete_object(R) end, Recs), 
  287:     Keys = [one_oid(Tab), other_oid(Tab)], 
  288:     [mnesia:dirty_write({T, K, K}) || {T, K} <- Keys].
  289: 
  290: %% Apply Fun for each X and Y
  291: fun_loop(Fun, Xs, Ys) ->
  292:     lists:foreach(fun(X) -> lists:foreach(fun(Y) -> do_fun(Fun, X, Y) end, Ys) end, Xs).
  293: 
  294: do_fun(Fun, X, Y) ->
  295:     Pid = spawn_link(?MODULE, do_fun, [self(), Fun, X, Y]), 
  296:     receive
  297: 	{done_fun, Pid} -> done_fun
  298:     end.
  299: 
  300: do_fun(Monitor, Fun, X, Y) ->
  301:     ?log("{do_fun ~p~n", [[Fun, X, Y]]), 
  302:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  303:     ?match([], mnesia:system_info(held_locks)), 
  304:     ?match([], mnesia:system_info(lock_queue)), 
  305:     Fun(X, Y), 
  306:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  307:     ?match([], mnesia:system_info(held_locks)), 
  308:     ?match([], mnesia:system_info(lock_queue)), 
  309:     unlink(Monitor), 
  310:     Monitor ! {done_fun, self()}, 
  311:     exit(done_fun).
  312: 
  313: %% Returns a list of fun's
  314: lock_funs(no_lock, one) ->
  315:     [
  316:      fun(Oid) -> mnesia:dirty_read(Oid) end, 
  317:      fun({Tab, Key}) -> mnesia:dirty_write({Tab, Key, Key}) end, 
  318:      fun({Tab, Key}) -> mnesia:dirty_write({Tab, Key, Key}), 
  319: 		       mnesia:dirty_update_counter({Tab, Key}, 0) end, 
  320:      fun(Oid) -> mnesia:dirty_delete(Oid) end, 
  321:      fun({Tab, Key}) -> mnesia:dirty_delete_object({Tab, Key, Key}) end, 
  322:      fun({Tab, Key}) -> mnesia:dirty_match_object({Tab, Key, Key}) end, 
  323:      fun({Tab, Key}) -> mnesia:dirty_index_match_object({Tab, Key, Key}, val) end, 
  324:      fun({Tab, Key}) -> mnesia:dirty_index_read(Tab, Key, val) end, 
  325:      fun({Tab, Key}) -> mnesia:dirty_index_match_object({Tab, '_', Key}, val) end
  326:     ];
  327: lock_funs(no_lock, all) ->
  328:     [
  329:      fun({Tab, _}) -> mnesia:dirty_match_object({Tab, '_', '_'}) end, 
  330:      fun({Tab, _}) -> slot_iter(Tab) end, 
  331:      fun({Tab, _}) -> key_iter(Tab) end
  332:     ];
  333: lock_funs(shared_lock, one) ->
  334:     
  335:     [
  336:      fun(Oid) -> mnesia:read(Oid) end, 
  337:      fun({Tab, Key}) ->
  338: 	     init_conflict_table(Tab), 
  339: 	     mnesia:dirty_delete(other_oid(Tab)), 
  340: 	     mnesia:match_object({Tab, Key, Key}) end
  341:      ];
  342: lock_funs(shared_lock, all) ->
  343:     [
  344:      fun({Tab, _}) -> mnesia:read_lock_table(Tab) end, 
  345:      fun({Tab, Key}) -> mnesia:match_object({Tab, '_', Key}) end, 
  346:      fun({Tab, _}) -> mnesia:match_object({Tab, '_', '_'}) end, 
  347:      fun({Tab, _}) -> mnesia:all_keys(Tab) end, 
  348:      fun({Tab, Key}) -> mnesia:index_match_object({Tab, '_', Key}, val) end,
  349:      fun({Tab, Key}) -> mnesia:index_read(Tab, Key, val) end
  350:     ];
  351: lock_funs(exclusive_lock, one) ->
  352:     [
  353:      fun(Oid) -> mnesia:wread(Oid) end, 
  354:      fun({Tab, Key}) -> mnesia:write({Tab, Key, Key}) end, 
  355:      fun(Oid) -> mnesia:delete(Oid) end, 
  356:      fun({Tab, Key}) -> mnesia:delete_object({Tab, Key, Key}) end, 
  357:      fun({Tab, Key}) -> mnesia:s_write({Tab, Key, Key}) end, 
  358:      fun(Oid) -> mnesia:s_delete(Oid) end, 
  359:      fun({Tab, Key}) -> mnesia:s_delete_object({Tab, Key, Key}) end
  360:     ];
  361: lock_funs(exclusive_lock, all) ->
  362:     [
  363:      fun({Tab, _}) -> mnesia:write_lock_table(Tab) end
  364:     ];
  365: lock_funs(Compatibility, any_granularity) ->
  366:     lists:append([lock_funs(Compatibility, Granularity) ||
  367: 		     Granularity <- [one, all]]);
  368: lock_funs(any_lock, Granularity) ->
  369:     lists:append([lock_funs(Compatibility, Granularity) ||
  370: 		     Compatibility <- [no_lock, shared_lock, exclusive_lock]]).
  371: 
  372: slot_iter(Tab) ->
  373:     slot_iter(Tab, mnesia:dirty_slot(Tab, 0), 1).
  374: slot_iter(_Tab, '$end_of_table', _) ->
  375:     [];
  376: slot_iter(Tab, Recs, Slot) ->
  377:     Recs ++ slot_iter(Tab, mnesia:dirty_slot(Tab, Slot), Slot+1).
  378: 
  379: key_iter(Tab) ->
  380:     key_iter(Tab, mnesia:dirty_first(Tab)).
  381: key_iter(_Tab, '$end_of_table') ->
  382:     [];
  383: key_iter(Tab, Key) ->
  384:     [Key | key_iter(Tab, mnesia:dirty_next(Tab, Key))].
  385: 
  386: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  387: lock_burst(suite) -> [];
  388: lock_burst(Config) when is_list(Config) ->
  389:     [Node1] = ?acquire_nodes(1, Config), 
  390:     Tab = burst, 
  391:     ?match({atomic, ok},  mnesia:create_table(Tab, 
  392: 					    [{attributes,  [a, b]}, 
  393: 					     {ram_copies, [Node1]}])), 
  394:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  395:     ?match([], mnesia:system_info(held_locks)), 
  396:     ?match([], mnesia:system_info(lock_queue)), 
  397:     ?match(ok, burst_em(Tab, 1000)), 
  398:     ?match([{burst,1,1000}], mnesia:dirty_read(Tab,1)),
  399:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  400:     ?match([], mnesia:system_info(held_locks)), 
  401:     ?match([], mnesia:system_info(lock_queue)), 
  402:     ok.
  403: 
  404: burst_em(Tab, N) ->
  405:     spawn_link(?MODULE, burst_counter, [self(), Tab, N]), 
  406:     receive
  407: 	burst_counter_done -> ok
  408:     end.
  409: 
  410: burst_counter(Monitor, Tab, N) when N > 0 ->
  411:     ?match(ok, burst_gen(Tab, N, self())), 
  412:     Monitor ! burst_receiver(N).
  413: 
  414: burst_receiver(0) ->
  415:     burst_counter_done;
  416: burst_receiver(N) ->
  417:     receive
  418: 	burst_incr_done ->
  419: 	    burst_receiver(N-1)
  420:     end.
  421:     
  422: burst_gen(_, 0, _) ->
  423:     ok;
  424: burst_gen(Tab, N, Father) when is_integer(N), N > 0 ->
  425:     spawn_link(?MODULE,  burst_incr,  [Tab, Father]), 
  426:     burst_gen(Tab, N-1, Father).
  427: 
  428: burst_incr(Tab, Father) ->
  429:     Fun = fun() ->
  430: 		  Val = 
  431: 		      case mnesia:read({Tab, 1}) of
  432: 			  [{Tab, 1, V}] -> V;
  433: 			  [] -> 0
  434: 		      end, 
  435: 		  mnesia:write({Tab, 1, Val+1})
  436: 	  end, 
  437:     ?match({atomic, ok}, mnesia:transaction(Fun)), 
  438:     Father ! burst_incr_done.
  439: 
  440: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  441: 
  442: basic_sticky_functionality(suite) -> [];
  443: basic_sticky_functionality(Config) when is_list(Config) -> 
  444:     [N1, N2] = Nodes = ?acquire_nodes(2, Config),
  445:     Tab = basic_table,
  446:     Storage = mnesia_test_lib:storage_type(disc_copies, Config),
  447:     ?match({atomic, ok}, mnesia:create_table(Tab, [{Storage, Nodes}])),
  448:     ?match({atomic, ok}, mnesia:create_table(sync, [{ram_copies, Nodes}])),
  449:     Trans1 = fun() ->
  450: 		     ?match(ok, mnesia:s_write({Tab, 1, 2})),
  451: 		     ?match([{Tab, 1, 2}], mnesia:read({Tab, 1})),
  452: 		     ?match(timeout, receive M -> M after 500 -> timeout end),
  453: 		     ?match(ok, mnesia:s_write({Tab, 2, 2})),
  454: 		     ?match(ok, mnesia:write({Tab, 42, 4711}))
  455: 	     end,
  456:     Trans2 = fun() ->
  457: 		     ?match([{Tab, 1, 2}],  mnesia:read({Tab, 1})),
  458: 		     ?match(timeout, receive M -> M after 500 -> timeout end),
  459: 		     ?match(ok, mnesia:write({Tab, 1, 4711})),
  460: 		     ?match(ok, mnesia:s_write({Tab, 2, 4})),
  461: 		     ?match(ok, mnesia:delete({Tab, 42}))
  462: 	     end,
  463:     rpc:call(N1, mnesia, transaction, [Trans1]),
  464:     ?match([{Tab,N1}], rpc:call(N1, ?MODULE, get_sticky, [])),
  465:     ?match([{Tab,N1}], rpc:call(N2, ?MODULE, get_sticky, [])),
  466: 
  467:     rpc:call(N2, mnesia, transaction, [Trans2]),
  468:     ?match([], rpc:call(N1, ?MODULE, get_sticky, [])),
  469:     ?match([], rpc:call(N2, ?MODULE, get_sticky, [])),
  470:     
  471:     Slock = fun() -> mnesia:read({sync,sync}),get_sticky() end,
  472:     ?match({atomic, [{Tab,1, 4711}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
  473:     ?match({atomic, [{Tab,2, 4}]},    mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
  474:     ?match({atomic, [{Tab,N1}]}, rpc:call(N1, mnesia, transaction, 
  475: 					  [fun() -> mnesia:s_write({Tab, 1, 3}),Slock() end])),
  476:     ?match([{Tab,N1}], rpc:call(N2, ?MODULE, get_sticky, [])),
  477:     
  478:     ?match({atomic,[]}, rpc:call(N2, mnesia, transaction, 
  479: 				 [fun() -> mnesia:s_write({Tab, 1, 4}),Slock()  end])),
  480: 
  481:     ?match([], rpc:call(N1, ?MODULE, get_sticky, [])),
  482:     ?match([], rpc:call(N2, ?MODULE, get_sticky, [])),
  483:     
  484:     ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction, 
  485: 					 [fun() -> mnesia:s_write({Tab, 1, 4}),Slock() end])),
  486:     
  487:     ?match({atomic,[]}, rpc:call(N1, mnesia, transaction, 
  488: 				 [fun() -> mnesia:s_write({Tab, 1, 5}),Slock()  end])),
  489:     ?match({atomic,[{Tab,N1}]}, rpc:call(N1, mnesia, transaction, 
  490: 					 [fun() -> mnesia:s_write({Tab, 1, 5}),Slock()  end])),
  491:     ?match({atomic,[]}, rpc:call(N2, mnesia, transaction, 
  492: 				 [fun() -> mnesia:s_write({Tab, 1, 6}),Slock()  end])),
  493:     ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction, 
  494: 					 [fun() -> mnesia:s_write({Tab, 1, 7}),Slock()  end])),
  495:     
  496:     ?match([{Tab,N2}], get_sticky()),
  497:     ?match({atomic, [{Tab,1, 7}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
  498:     ?match([{Tab,N2}], get_sticky()),
  499:     ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
  500:     ?match([{Tab,N2}], get_sticky()),
  501:     ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction, 
  502: 					 [fun() -> mnesia:s_write({Tab, 1, 6}),Slock()  end])),
  503:     ?match([{Tab,N2}], get_sticky()),
  504:     ?match({atomic, [{Tab,1, 6}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
  505:     ?match([{Tab,N2}], get_sticky()),
  506:     ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
  507:     ?match([{Tab,N2}], get_sticky()),
  508:     ?verify_mnesia(Nodes, []).
  509: 
  510: get_sticky() ->
  511:     mnesia_locker ! {get_table, self(), mnesia_sticky_locks},
  512:     receive {mnesia_sticky_locks, Locks} -> Locks end.
  513: 
  514: get_held() ->
  515:     mnesia_locker ! {get_table, self(), mnesia_sticky_locks},
  516:     receive {mnesia_sticky_locks, Locks} -> Locks end.
  517: 
  518: 
  519: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  520: 
  521: 
  522: unbound1(suite) -> [];
  523: unbound1(Config) when is_list(Config) ->
  524:     [Node1] = ?acquire_nodes(1, Config),
  525:     
  526:     ?match({atomic, ok}, mnesia:create_table(ul, [])),
  527:     
  528:     Tester = self(),
  529:     Write = fun() ->
  530: 		    mnesia:write({ul, {key, {17,42}}, val}),
  531: 		    ?log("~p Got write lock waiting...~n", [self()]),  
  532: 		    Tester ! continue,
  533: 		    receive 
  534: 			continue ->
  535: 			    ok
  536: 		    end,
  537: 		    ?log("..continuing~n", []),
  538: 		    ok
  539: 	    end,
  540:     
  541:     {success, [A]} = ?start_activities([Node1]),
  542:     ?start_transactions([A]),    
  543:     A ! Write,    
  544:     
  545:     receive continue -> ok end,
  546: 
  547:     Match = fun() ->		    
  548: 		    case catch mnesia:match_object({ul, {key, {'_', '$0'}}, '_'}) of
  549: 			{'EXIT', What} ->  %% Cyclic first time
  550: 			    ?log("Cyclic Restarting~n", []),
  551: 			    A ! continue,
  552: 			    A ! end_trans,
  553: 			    exit(What);
  554: 			Res ->
  555: 			    ?log("Got match log ~p...~n", [Res]),
  556: 			    Res
  557: 		    end
  558: 	    end,
  559:     ?match({atomic, [{ul,{key,{17,42}},val}]}, mnesia:transaction(Match)),
  560: 
  561:     ?match_receive({A, ok}),
  562:     ?match_receive({A, {atomic, end_trans}}),
  563:     ok.
  564: 
  565: unbound2(suite) -> [];
  566: unbound2(Config) when is_list(Config) ->
  567:     [Node1] = ?acquire_nodes(1, Config),
  568:     
  569:     ?match({atomic, ok}, mnesia:create_table(ul, [])),
  570:         
  571:     {success, [B, A]} = ?start_activities([Node1, Node1]),
  572:     
  573:     Me = self(),
  574:     
  575:     Write = fun() ->
  576: 		    mnesia:write({ul, {key, {17,42}}, val}),
  577: 		    ?log("~p Got write lock waiting... Tid ~p ~n", 
  578: 			 [self(), get(mnesia_activity_state)]),  
  579: 		    Me ! ok_lock,
  580: 		    receive 
  581: 			continue ->
  582: 			    ok
  583: 		    end,
  584: 		    ?log("..continuing~n", []),
  585: 		    ok
  586: 	    end,
  587:     
  588:     Match = fun() ->  
  589: 		    receive 
  590: 			continueB -> 
  591: 			    ?log("~p, moving on TID ~p~n", 
  592: 				 [self(), get(mnesia_activity_state)]),
  593: 			    Me ! {self(), continuing}
  594: 		    end,
  595: 		    case catch mnesia:match_object({ul, {key, {'_', '$0'}}, 
  596: 						    '_'}) of
  597: 			{'EXIT', What} ->  %% Cyclic first time
  598: 			    ?log("Cyclic Restarting ~p ~n", [What]),
  599: 			    {should_not_happen,What};
  600: 			Res ->
  601: 			    ?log("Got match log ~p...~n", [Res]),
  602: 			    Res
  603: 		    end
  604: 	    end,
  605: 
  606:     B ! fun() -> mnesia:transaction(Match) end,
  607:     timer:sleep(100),  %% Let B be started first..
  608:     A ! fun() -> mnesia:transaction(Write) end,    
  609:     
  610:     receive ok_lock -> ok end,
  611: 
  612:     B ! continueB,
  613:     ?match_receive({B, continuing}),
  614: 
  615:     %% B should now be in lock queue.
  616:     A ! continue,
  617:     ?match_multi_receive([{A, {atomic, ok}},
  618: 			  {B, {atomic, [{ul,{key,{17,42}},val}]}}]),
  619:     ok.
  620: 
  621: receiver() ->
  622:     receive 
  623: 	{_Pid, begin_trans} ->
  624: 	    receiver();
  625: 	Else ->
  626: 	    Else
  627:     after 
  628: 	10000 ->
  629: 	    timeout
  630:     end.   
  631: 
  632: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  633: 
  634: create_table(suite) -> [];
  635: create_table(Config) when is_list(Config) ->
  636:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
  637:     Tab = c_t_tab,
  638:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  639:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  640:     insert(Tab, 50),
  641:     {success, [A]} = ?start_activities([ThisNode]), 
  642:     mnesia_test_lib:start_sync_transactions([A], 0),
  643: 
  644:     A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
  645:     ?match_receive({A, ok}),   %% A is executed
  646:     
  647:     DiskMaybe = mnesia_test_lib:storage_type(disc_copies, Config),
  648: 
  649:     Pid = spawn_link(?MODULE, op, [self(), mnesia, create_table,
  650: 				   [test_tab1, [{DiskMaybe, [ThisNode]}]]]),
  651:     ?match_multi_receive([{Pid, {atomic, ok}},
  652: 			  {'EXIT', Pid, normal}]), %% No Locks! op should be exec.
  653: 
  654:     Pid2 = spawn_link(?MODULE, op, [self(), mnesia, create_table,
  655: 				   [test_tab2, [{ram_copies, [Node2]}]]]),
  656: 
  657:     ?match_multi_receive([{Pid2, {atomic, ok}},
  658: 			  {'EXIT', Pid2, normal}]), %% No Locks! op should be exec.
  659: 
  660:     A ! end_trans,        
  661:     ?match_receive({A,{atomic,end_trans}}),
  662:     
  663:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  664:     ?match([], mnesia:system_info(held_locks)), 
  665:     ?match([], mnesia:system_info(lock_queue)), 
  666:     ok.
  667: 
  668: delete_table(suite) -> [];
  669: delete_table(Config) when is_list(Config) ->
  670:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
  671:     Tab = d_t_tab,
  672:     Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
  673:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  674:     insert(Tab, 50),
  675:     {success, [A]} = ?start_activities([ThisNode]), 
  676:     mnesia_test_lib:start_sync_transactions([A], 0),
  677: 
  678:     A ! fun() -> mnesia:read({Tab, 1}) end,
  679:     ?match_receive({A, [{Tab, 1, 1, 0}]}),   %% A is executed
  680: 
  681:     Pid = spawn_link(?MODULE, op, [self(), mnesia, delete_table,
  682: 				   [Tab]]),
  683:     
  684:     ?match_receive(timeout),   %% op waits for locks occupied by A
  685: 
  686:     A ! end_trans,             %% Kill A, locks should be released
  687:     ?match_receive({A,{atomic,end_trans}}),     
  688: 
  689:     receive 
  690: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  691:     after
  692: 	timer:seconds(20) -> ?error("Operation timed out", [])
  693:     end,
  694:     
  695:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  696:     ?match([], mnesia:system_info(held_locks)), 
  697:     ?match([], mnesia:system_info(lock_queue)), 
  698:     ok.
  699: 
  700: move_table_copy(suite) -> [];
  701: move_table_copy(Config) when is_list(Config) ->
  702:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
  703:     Tab = m_t_c_tab,
  704:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  705:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  706:     insert(Tab, 50),
  707:     {success, [A]} = ?start_activities([ThisNode]), 
  708:     mnesia_test_lib:start_sync_transactions([A], 0),
  709: 
  710:     A ! fun() -> mnesia:write({Tab, 1, 2, 3}) end,
  711:     ?match_receive({A, ok}),   %% A is executed
  712: 
  713:     Pid = spawn_link(?MODULE, op, [self(), mnesia, move_table_copy,
  714: 				   [Tab, ThisNode, Node2]]),
  715:     
  716:     ?match_receive(timeout),   %% op waits for locks occupied by A
  717: 
  718:     A ! end_trans,             %% Kill A, locks should be released
  719:     ?match_receive({A,{atomic,end_trans}}),     
  720: 
  721:     receive 
  722: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  723:     after
  724: 	timer:seconds(20) -> ?error("Operation timed out", [])
  725:     end,
  726:     
  727:     timer:sleep(500), %% Don't know how to sync this !!!
  728:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  729:     sys:get_status(whereis(mnesia_tm)),    % Explicit sync, release locks is async
  730:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  731:     ?match([], mnesia:system_info(held_locks)), 
  732:     ?match([], mnesia:system_info(lock_queue)), 
  733:     ok.
  734: 
  735: add_table_index(suite) -> [];
  736: add_table_index(Config) when is_list(Config) ->
  737:     [ThisNode, _Node2] = ?acquire_nodes(2, Config ++ [{tc_timeout, 60000}]),
  738:     Tab = a_t_i_tab,
  739:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  740:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  741:     insert(Tab, 50),
  742:     {success, [A]} = ?start_activities([ThisNode]), 
  743:     mnesia_test_lib:start_sync_transactions([A], 0),
  744: 
  745:     A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
  746:     ?match_receive({A, ok}),   %% A is executed
  747: 
  748:     Pid = spawn_link(?MODULE, op, [self(), mnesia, 
  749: 				   add_table_index, [Tab, attr1]]),
  750: 
  751:     ?match_receive(timeout),   %% op waits for locks occupied by A
  752: 
  753:     A ! end_trans,             %% Kill A, locks should be released
  754:     ?match_receive({A,{atomic,end_trans}}),     
  755:     
  756:     receive 
  757: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  758:     after
  759: 	timer:seconds(20) -> ?error("Operation timed out", [])
  760:     end,
  761:     
  762:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  763:     ?match([], mnesia:system_info(held_locks)), 
  764:     ?match([], mnesia:system_info(lock_queue)), 
  765:     ok.
  766: 
  767: del_table_index(suite) -> [];
  768: del_table_index(Config) when is_list(Config) ->
  769:     [ThisNode, _Node2] = ?acquire_nodes(2, Config),
  770:     Tab = d_t_i_tab,
  771:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  772:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  773:     insert(Tab, 50),
  774:     ?match({atomic, ok}, mnesia:add_table_index(Tab, attr1)),    
  775: 
  776:     {success, [A]} = ?start_activities([ThisNode]), 
  777:     mnesia_test_lib:start_sync_transactions([A], 0),
  778: 
  779:     A ! fun() -> mnesia:write({Tab, 51, 51, attr2}) end,
  780:     ?match_receive({A, ok}),   %% A is executed
  781: 
  782:     Pid = spawn_link(?MODULE, op, [self(), mnesia, del_table_index,
  783: 				   [Tab, attr1]]),
  784: 
  785:     ?match_receive(timeout),   %% op waits for locks occupied by A
  786: 
  787:     A ! end_trans,             %% Kill A, locks should be released
  788:     ?match_receive({A,{atomic,end_trans}}),     
  789:     %% Locks released! op should be exec.
  790:     receive 
  791: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  792:     after
  793: 	timer:seconds(20) -> ?error("Operation timed out", [])
  794:     end,
  795:     
  796:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  797:     ?match([], mnesia:system_info(held_locks)), 
  798:     ?match([], mnesia:system_info(lock_queue)), 
  799:     ok.
  800: 
  801: transform_table(suite) -> [];
  802: transform_table(Config) when is_list(Config) -> 
  803:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
  804:     Tab = t_t_tab,
  805:     Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
  806:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  807:     insert(Tab, 50),
  808:     {success, [A]} = ?start_activities([ThisNode]), 
  809:     mnesia_test_lib:start_sync_transactions([A], 0),
  810: 
  811:     A ! fun() -> mnesia:read({Tab, 1}) end,
  812:     ?match_receive({A, [{Tab, 1, 1, 0}]}),   %% A is executed
  813: 
  814:     Transform = fun({Table, Key, Attr1, Attr2}) -> % Need todo a transform
  815: 			{Table, Key, {Attr1, Attr2}} end,    
  816:     Pid = spawn_link(?MODULE, op, [self(), mnesia, transform_table,
  817: 				   [Tab, Transform, [key, attr1]]]),
  818:     ?match_receive(timeout),   %% op waits for locks occupied by A
  819: 
  820:     A ! end_trans,             %% Kill A, locks should be released
  821:     ?match_receive({A,{atomic,end_trans}}),     
  822:     
  823:     receive 
  824: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  825:     after
  826: 	timer:seconds(20) -> ?error("Operation timed out", [])
  827:     end,
  828: 
  829:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  830:     ?match([], mnesia:system_info(held_locks)), 
  831:     ?match([], mnesia:system_info(lock_queue)), 
  832:     ok.
  833: 
  834: snmp_open_table(suite) -> [];
  835: snmp_open_table(Config) when is_list(Config) -> 
  836:     [ThisNode, _Node2] = ?acquire_nodes(2, Config),
  837:     Tab = s_o_t_tab,
  838:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  839:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  840:     insert(Tab, 50),
  841:     {success, [A]} = ?start_activities([ThisNode]), 
  842:     mnesia_test_lib:start_sync_transactions([A], 0),
  843: 
  844:     A ! fun() -> mnesia:write({Tab, 1, 1, 100}) end,
  845:     ?match_receive({A, ok}),   %% A is executed
  846: 
  847:     Pid = spawn_link(?MODULE, op, [self(), mnesia, snmp_open_table,
  848: 				   [Tab, [{key, integer}]]]),
  849: 
  850:     ?match_receive(timeout),   %% op waits for locks occupied by A
  851: 
  852:     A ! end_trans,             %% Kill A, locks should be released
  853:     ?match_receive({A,{atomic,end_trans}}),     
  854:     
  855:     %% Locks released! op should be exec. Can take a while (thats the timeout)
  856:     receive 
  857: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  858:     after
  859: 	timer:seconds(20) -> ?error("Operation timed out", [])
  860:     end,
  861:     
  862:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  863:     ?match([], mnesia:system_info(held_locks)), 
  864:     ?match([], mnesia:system_info(lock_queue)), 
  865:     ok.
  866: 
  867: snmp_close_table(suite) -> [];
  868: snmp_close_table(Config) when is_list(Config) -> 
  869:     [ThisNode, _Node2] = ?acquire_nodes(2, Config),
  870:     Tab = s_c_t_tab,
  871:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  872:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  873:     ?match({atomic, ok}, mnesia:snmp_open_table(Tab, [{key, integer}])),
  874:     insert(Tab, 50),
  875:     {success, [A]} = ?start_activities([ThisNode]), 
  876:     mnesia_test_lib:start_sync_transactions([A], 0),
  877: 
  878:     A ! fun() -> mnesia:write({Tab, 1, 1, 100}) end,
  879:     ?match_receive({A, ok}),   %% A is executed    
  880: 
  881:     Pid = spawn_link(?MODULE, op, [self(), mnesia, snmp_close_table, [Tab]]),   
  882:     ?match_receive(timeout),   %% op waits for locks occupied by A
  883: 
  884:     A ! end_trans,             %% Kill A, locks should be released
  885:     ?match_receive({A,{atomic,end_trans}}),     
  886:     
  887:     %% Locks released! op should be exec. Can take a while (thats the timeout)
  888:     receive 
  889: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  890:     after
  891: 	timer:seconds(20) -> ?error("Operation timed out", [])
  892:     end,
  893:     
  894:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  895:     ?match([], mnesia:system_info(held_locks)), 
  896:     ?match([], mnesia:system_info(lock_queue)), 
  897:     ok.
  898: 
  899: change_table_copy_type(suite) -> [];
  900: change_table_copy_type(Config) when is_list(Config) ->
  901:     [ThisNode, _Node2] = ?acquire_nodes(2, Config),
  902:     Tab = c_t_c_t_tab,
  903:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  904:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  905:     insert(Tab, 50),
  906:     {success, [A]} = ?start_activities([ThisNode]), 
  907:     mnesia_test_lib:start_sync_transactions([A], 0),
  908:     A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
  909:     ?match_receive({A, ok}),   %% A is executed
  910: 
  911:     Pid = spawn_link(?MODULE, op, [self(), mnesia, change_table_copy_type, 
  912: 				   [Tab, ThisNode, disc_copies]]),
  913: 
  914:     ?match_receive(timeout),   %% op waits for locks occupied by A
  915: 
  916:     A ! end_trans,             %% Kill A, locks should be released
  917:     ?match_receive({A,{atomic,end_trans}}),     
  918:     
  919:     receive 
  920: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  921:     after
  922: 	timer:seconds(20) -> ?error("Operation timed out", [])
  923:     end,
  924:     
  925:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  926:     ?match([], mnesia:system_info(held_locks)), 
  927:     ?match([], mnesia:system_info(lock_queue)), 
  928:     ok.
  929: 
  930: change_table_access(suite) -> [];
  931: change_table_access(Config) when is_list(Config) -> 
  932:     [ThisNode, _Node2] = ?acquire_nodes(2, Config),
  933:     Tab = c_t_a_tab,
  934:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  935:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  936:     insert(Tab, 50),
  937:     {success, [A]} = ?start_activities([ThisNode]), 
  938:     mnesia_test_lib:start_sync_transactions([A], 0),
  939: 
  940:     A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
  941:     ?match_receive({A, ok}),   %% A is executed
  942: 
  943:     Pid = spawn_link(?MODULE, op, [self(), mnesia, change_table_access_mode,
  944: 				   [Tab, read_only]]),
  945: 
  946:     
  947:     ?match_receive(timeout),   %% op waits for locks occupied by A
  948: 
  949:     A ! end_trans,             %% Kill A, locks should be released
  950:     ?match_receive({A,{atomic,end_trans}}),     
  951:     
  952:     receive 
  953: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  954:     after
  955: 	timer:seconds(20) -> ?error("Operation timed out", [])
  956:     end,
  957: 
  958:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  959:     ?match([], mnesia:system_info(held_locks)), 
  960:     ?match([], mnesia:system_info(lock_queue)), 
  961:     ok.
  962: 
  963: add_table_copy(suite) -> [];
  964: add_table_copy(Config) when is_list(Config) -> 
  965:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
  966:     Tab = a_t_c_tab,
  967:     Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
  968:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  969:     insert(Tab, 50),
  970:     {success, [A]} = ?start_activities([ThisNode]), 
  971:     mnesia_test_lib:start_sync_transactions([A], 0),
  972: 
  973:     A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
  974:     ?match_receive({A, ok}),   %% A is executed
  975: 
  976:     Pid = spawn_link(?MODULE, op, [self(), mnesia, add_table_copy, 
  977: 				   [Tab, Node2, ram_copies]]),
  978:    
  979:     ?match_receive(timeout),   %% op waits for locks occupied by A
  980: 
  981:     A ! end_trans,             %% Kill A, locks should be released
  982:     ?match_receive({A,{atomic,end_trans}}),     
  983:     
  984:     receive 
  985: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
  986:     after
  987: 	timer:seconds(20) -> ?error("Operation timed out", [])
  988:     end,
  989: 
  990:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
  991:     ?match([], mnesia:system_info(held_locks)), 
  992:     ?match([], mnesia:system_info(lock_queue)), 
  993:     ok.
  994: 
  995: del_table_copy(suite) -> [];
  996: del_table_copy(Config) when is_list(Config) -> 
  997:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
  998:     Tab = d_t_c_tab,
  999:     Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
 1000:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
 1001:     insert(Tab, 50),
 1002:     {success, [A]} = ?start_activities([ThisNode]), 
 1003:     mnesia_test_lib:start_sync_transactions([A], 0),
 1004:     A ! fun() -> mnesia:write({Tab, 1, 2, 5}) end,
 1005:     ?match_receive({A, ok}),   %% A is executed
 1006: 
 1007:     Pid = spawn_link(?MODULE, op, [self(), mnesia, del_table_copy,
 1008: 				   [Tab, ThisNode]]),
 1009: 
 1010:     ?match_receive(timeout),   %% op waits for locks occupied by A
 1011:     A ! end_trans,             %% Kill A, locks should be released
 1012:     ?match_receive({A, {atomic,end_trans}}),     
 1013: 
 1014:     ?match_receive({Pid, {atomic, ok}}),
 1015:     ?match_receive({'EXIT', Pid, normal}),
 1016: 
 1017:     timer:sleep(500), %% Don't know how to sync this !!!
 1018:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
 1019:     sys:get_status(whereis(mnesia_tm)),    % Explicit sync, release locks is async
 1020:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
 1021:     ?match([], mnesia:system_info(held_locks)), 
 1022:     ?match([], mnesia:system_info(lock_queue)), 
 1023:     ok.
 1024: 
 1025: dump_tables(suite) -> [];
 1026: dump_tables(Config) when is_list(Config) -> 
 1027:     [ThisNode, Node2] = ?acquire_nodes(2, Config),
 1028:     Tab = dump_t_tab,
 1029:     Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
 1030:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
 1031:     insert(Tab, 50),
 1032:     {success, [A]} = ?start_activities([ThisNode]), 
 1033:     mnesia_test_lib:start_sync_transactions([A], 0),
 1034:     A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
 1035:     ?match_receive({A, ok}),   %% A is executed
 1036: 
 1037:     Pid = spawn_link(?MODULE, op, [self(), mnesia, dump_tables,
 1038: 				   [[Tab]]]),
 1039: 
 1040:     ?match_receive(timeout),   %% op waits for locks occupied by A
 1041: 
 1042:     A ! end_trans,             %% Kill A, locks should be released
 1043:     ?match_receive({A,{atomic,end_trans}}),     
 1044:     
 1045:     receive 
 1046: 	Msg -> ?match({Pid, {atomic, ok}}, Msg)
 1047:     after
 1048: 	timer:seconds(20) -> ?error("Operation timed out", [])
 1049:     end,
 1050:     
 1051:     sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
 1052:     ?match([], mnesia:system_info(held_locks)), 
 1053:     ?match([], mnesia:system_info(lock_queue)), 
 1054:     ok.
 1055: 
 1056: op(Father, Mod, Fun, Args) -> 
 1057:     Res = apply(Mod, Fun, Args),
 1058:     Father ! {self(), Res}.
 1059: 
 1060: insert(_Tab, 0) -> ok;
 1061: insert(Tab, N) when N > 0 ->
 1062:     ok = mnesia:sync_dirty(fun() -> mnesia:write({Tab, N, N, 0}) end),
 1063:     insert(Tab, N-1).
 1064: 
 1065: 
 1066: update_own(Tab, Key, Acc) ->
 1067:     Update = 
 1068: 	fun() -> 
 1069: 		Res = mnesia:read({Tab, Key}),
 1070: 		case Res of 
 1071: 		    [{Tab, Key, Extra, Acc}] ->
 1072: 			mnesia:write({Tab,Key,Extra, Acc+1});
 1073: 		    Val ->
 1074: 			{read, Val, {acc, Acc}}
 1075: 		end
 1076: 	end,
 1077:     receive 
 1078: 	{Pid, quit} -> Pid ! {self(), Acc}
 1079:     after
 1080: 	0 -> 
 1081: 	    case mnesia:transaction(Update) of
 1082: 		{atomic, ok} -> 	    
 1083: 		    update_own(Tab, Key, Acc+1);
 1084: 		Else -> 
 1085: 		    ?error("Trans failed on ~p with ~p~n"
 1086: 			   "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n", 
 1087: 			   [node(), 
 1088: 			    Else,
 1089: 			    mnesia:table_info(Tab, where_to_read),
 1090: 			    mnesia:table_info(Tab, where_to_write),
 1091: 			    mnesia:table_info(Tab, where_to_commit),
 1092: 			    mnesia:table_info(Tab, storage_type)])
 1093: 	    end
 1094:     end.
 1095: 
 1096: update_shared(Tab, Me, Acc) ->
 1097:     Update = 
 1098: 	fun() ->
 1099: 		W2R = mnesia:table_info(Tab, where_to_read),
 1100: 		Res = mnesia:read({Tab, 0}),
 1101: 		case Res of 
 1102: 		    [{Tab, Key, Extra, Val}] when element(Me, Extra) == Acc ->
 1103: 			Extra1 = setelement(Me, Extra, Acc+1),
 1104: 			Term = {Tab, Key, Extra1, Val+1},
 1105: 			ok = mnesia:write(Term),
 1106: %			?log("At ~p: ~p w2r ~p w2w ~p ~n", 
 1107: %			     [node(), Term, 
 1108: %			      mnesia:table_info(Tab, where_to_read),
 1109: 			W2W = mnesia:table_info(Tab, where_to_write),
 1110: 			W2C = mnesia:table_info(Tab, where_to_commit),
 1111: %%			      mnesia:table_info(Tab, storage_type)
 1112: %			     ]),
 1113: 			{_Mod, Tid, Ts} = get(mnesia_activity_state),
 1114: 			io:format("~p ~p~n", [Tid, ets:tab2list(element(2,Ts))]),
 1115: 			{ok,Term,{W2R,W2W,W2C}};
 1116: 		    Val ->
 1117: 			Info = [{acc, Acc}, {me, Me}, 
 1118: 				{tid, element(2, mnesia:get_activity_id())},
 1119: 				{locks, mnesia:system_info(held_locks)}],
 1120: 			{read, Val, Info}
 1121: 		end
 1122: 	end,
 1123:     receive 
 1124: 	{Pid, quit} -> Pid ! {self(), Acc}
 1125:     after
 1126: 	0 -> 
 1127: 	    case mnesia:transaction(Update) of
 1128: 		{atomic, {ok,Term,W2}} ->
 1129: 		    io:format("~p:~p:(~p,~p) ~w@~w~n", [erlang:now(),node(),Me,Acc,Term,W2]),
 1130: 		    update_shared(Tab, Me, Acc+1);
 1131: 		Else -> 
 1132: 		    ?error("Trans failed on ~p with ~p~n"
 1133: 			   "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n", 
 1134: 			   [node(), 
 1135: 			    Else,
 1136: 			    mnesia:table_info(Tab, where_to_read),
 1137: 			    mnesia:table_info(Tab, where_to_write),
 1138: 			    mnesia:table_info(Tab, where_to_commit),
 1139: 			    mnesia:table_info(Tab, storage_type)			   
 1140: 			   ])
 1141: 	    end
 1142:     end.
 1143: 
 1144: init_admin(Def, N1, N2, N3) ->
 1145:     Tab = schema_ops,
 1146:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
 1147:     insert(Tab, 1002),
 1148:     
 1149:     Pid1 = spawn_link(N1, ?MODULE, update_own, [Tab, 1, 0]),
 1150:     Pid2 = spawn_link(N2, ?MODULE, update_own, [Tab, 2, 0]),
 1151:     Pid3 = spawn_link(N3, ?MODULE, update_own, [Tab, 3, 0]),
 1152:     
 1153:     ?match({atomic, ok}, 
 1154: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 0, {0,0,0}, 0}) end)),
 1155: 
 1156:     Pid4 = spawn_link(N1, ?MODULE, update_shared, [Tab, 1, 0]),
 1157:     Pid5 = spawn_link(N2, ?MODULE, update_shared, [Tab, 2, 0]),
 1158:     Pid6 = spawn_link(N3, ?MODULE, update_shared, [Tab, 3, 0]),
 1159:     
 1160:     {Pid1, Pid2, Pid3, Pid4, Pid5, Pid6}.
 1161: 
 1162: verify_results({P1, P2, P3, P4, P5, P6}) ->
 1163:     Tab = schema_ops, N1 = node(P1),  N2 = node(P2),  N3 = node(P3),
 1164: 
 1165:     try 
 1166: 	P1 ! {self(), quit}, 
 1167: 	R1 = receive {P1, Res1} -> Res1 after 9000 -> throw({timeout,P1}) end,
 1168: 	P2 ! {self(), quit}, 
 1169: 	R2 = receive {P2, Res2} -> Res2 after 9000 -> throw({timeout,P2}) end,
 1170: 	P3 ! {self(), quit}, 
 1171: 	R3 = receive {P3, Res3} -> Res3 after 9000 -> throw({timeout,P3}) end,
 1172: 
 1173: 	P4 ! {self(), quit}, 
 1174: 	R4 = receive {P4, Res4} -> Res4 after 9000 -> throw({timeout,P4}) end,
 1175: 	P5 ! {self(), quit}, 
 1176: 	R5 = receive {P5, Res5} -> Res5 after 9000 -> throw({timeout,P5}) end,
 1177: 	P6 ! {self(), quit}, 
 1178: 	R6 = receive {P6, Res6} -> Res6 after 9000 -> throw({timeout,P6}) end,
 1179: 
 1180: 	?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write_lock_table(Tab) end)),
 1181: 	?log("Should be ~p~n", [R1]),
 1182: 	?match([{_, _, _, R1}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])),
 1183: 	?match([{_, _, _, R1}], rpc:call(N2, mnesia, dirty_read, [{Tab, 1}])),
 1184: 	?match([{_, _, _, R1}], rpc:call(N3, mnesia, dirty_read, [{Tab, 1}])),
 1185: 	?log("Should be ~p~n", [R2]),
 1186: 	?match([{_, _, _, R2}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])),
 1187: 	?match([{_, _, _, R2}], rpc:call(N2, mnesia, dirty_read, [{Tab, 2}])),
 1188: 	?match([{_, _, _, R2}], rpc:call(N3, mnesia, dirty_read, [{Tab, 2}])),
 1189: 	?log("Should be ~p~n", [R3]),
 1190: 	?match([{_, _, _, R3}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])),
 1191: 	?match([{_, _, _, R3}], rpc:call(N2, mnesia, dirty_read, [{Tab, 3}])),
 1192: 	?match([{_, _, _, R3}], rpc:call(N3, mnesia, dirty_read, [{Tab, 3}])),
 1193: 
 1194: 	Res = R4+R5+R6,
 1195: 	?log("Should be {~p+~p+~p}= ~p~n", [R4, R5, R6, Res]),
 1196: 	?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N1, mnesia, dirty_read, [{Tab, 0}])),
 1197: 	?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N2, mnesia, dirty_read, [{Tab, 0}])),
 1198: 	?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N3, mnesia, dirty_read, [{Tab, 0}]))
 1199:     catch throw:{timeout, Pid}  ->
 1200: 	    mnesia_lib:dist_coredump(),
 1201: 	    ?error("Timeout ~p ~n", [Pid])
 1202:     end.
 1203:     
 1204: 
 1205: get_info(Tab) ->
 1206:     Info = mnesia:table_info(Tab, all),
 1207:     mnesia_lib:verbose("~p~n", [Info]).
 1208: 
 1209: del_table_copy_1(suite) -> [];
 1210: del_table_copy_1(Config) when is_list(Config) ->
 1211:     [_Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
 1212:     del_table(Node2, Node2, Nodes). %Called on same Node as deleted
 1213: del_table_copy_2(suite) -> [];
 1214: del_table_copy_2(Config) when is_list(Config) ->
 1215:     [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
 1216:     del_table(Node1, Node2, Nodes). %Called from other Node 
 1217: del_table_copy_3(suite) -> [];
 1218: del_table_copy_3(Config) when is_list(Config) ->
 1219:     [_Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1220:     del_table(Node3, Node2, Nodes). %Called from Node w.o. table
 1221: 
 1222: %%% The actual test
 1223: del_table(CallFrom, DelNode, [Node1, Node2, Node3]) ->
 1224:     Def = [{ram_copies, [Node1]}, {disc_copies, [Node2]}, 
 1225: 	   {attributes, [key, attr1, attr2]}],
 1226:     Tab = schema_ops,
 1227:     Pids = init_admin(Def, Node1, Node2, Node3),
 1228: 
 1229:     ?log("Call from ~p delete table from ~p ~n", [CallFrom, DelNode]),
 1230:     rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
 1231:     
 1232:     ?match({atomic, ok}, 
 1233: 	   rpc:call(CallFrom, mnesia, del_table_copy, [Tab, DelNode])),
 1234: 
 1235:     verify_results(Pids),
 1236:     rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
 1237:     ?verify_mnesia([Node1, Node2, Node3], []).    
 1238: 
 1239: add_table_copy_1(suite) -> [];
 1240: add_table_copy_1(Config) when is_list(Config) ->
 1241:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1242:     Def = [{disc_only_copies, [Node1, Node2]}, 
 1243: 	   {attributes, [key, attr1, attr2]}], 
 1244:     add_table(Node1, Node3, Nodes, Def).
 1245: add_table_copy_2(suite) -> [];
 1246: add_table_copy_2(Config) when is_list(Config) ->
 1247:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1248:     Def = [{disc_only_copies, [Node1, Node2]}, 
 1249: 	   {attributes, [key, attr1, attr2]}], 
 1250:     add_table(Node2, Node3, Nodes, Def).
 1251: add_table_copy_3(suite) -> [];
 1252: add_table_copy_3(Config) when is_list(Config) ->
 1253:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1254:     Def = [{disc_only_copies, [Node1, Node2]},
 1255: 	   {attributes, [key, attr1, attr2]}], 
 1256:     add_table(Node3, Node3, Nodes, Def).
 1257: add_table_copy_4(suite) -> [];
 1258: add_table_copy_4(Config) when is_list(Config) ->
 1259:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1260:     Def = [{disc_only_copies, [Node1]}, 
 1261: 	   {attributes, [key, attr1, attr2]}], 
 1262:     add_table(Node2, Node3, Nodes, Def).
 1263: %%% The actual test
 1264: add_table(CallFrom, AddNode, [Node1, Node2, Node3], Def) ->
 1265:     Pids = init_admin(Def, Node1, Node2, Node3),    
 1266:     Tab = schema_ops,
 1267:     ?log("Call from ~p add table to ~p ~n", [CallFrom, AddNode]),
 1268:     rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
 1269:     ?match({atomic, ok}, rpc:call(CallFrom, mnesia, add_table_copy, 
 1270: 				  [Tab, AddNode, ram_copies])),
 1271:     verify_results(Pids),
 1272:     rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
 1273:     ?verify_mnesia([Node1, Node2, Node3], []).
 1274: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1275: move_table_copy_1(suite) -> [];
 1276: move_table_copy_1(Config) when is_list(Config) ->
 1277:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1278:     Def = [{disc_copies, [Node1, Node2]},
 1279: 	   {attributes, [key, attr1, attr2]}], 
 1280:     move_table(Node1, Node1, Node3, Nodes, Def).
 1281: move_table_copy_2(suite) -> [];
 1282: move_table_copy_2(Config) when is_list(Config) ->
 1283:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1284:     Def = [{disc_copies, [Node1, Node2]},
 1285: 	   {attributes, [key, attr1, attr2]}], 
 1286:     move_table(Node2, Node1, Node3, Nodes, Def).
 1287: move_table_copy_3(suite) -> [];
 1288: move_table_copy_3(Config) when is_list(Config) ->
 1289:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1290:     Def = [{disc_copies, [Node1, Node2]},
 1291: 	   {attributes, [key, attr1, attr2]}], 
 1292:     move_table(Node3, Node1, Node3, Nodes, Def).
 1293: move_table_copy_4(suite) -> [];
 1294: move_table_copy_4(Config) when is_list(Config) ->
 1295:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
 1296:     Def = [{disc_copies, [Node1]},
 1297: 	   {attributes, [key, attr1, attr2]}], 
 1298:     move_table(Node2, Node1, Node3, Nodes, Def).
 1299: %%% The actual test
 1300: move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) ->
 1301:     Pids = init_admin(Def, Node1, Node2, Node3),    
 1302:     Tab = schema_ops,   
 1303:     ?log("Call from ~p move table from ~p to ~p ~n", [CallFrom, FromNode, ToNode]),
 1304:     rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
 1305:     ?match({atomic, ok}, rpc:call(CallFrom, mnesia, move_table_copy, 
 1306: 				  [Tab, FromNode, ToNode])),
 1307:     verify_results(Pids),
 1308:     rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
 1309:     ?verify_mnesia([Node1, Node2, Node3], []).
 1310: 
 1311: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1312: 
 1313: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1314: dirty_updates_visible_direct(doc) ->
 1315:     ["One process can immediately see dirty updates of another"];
 1316: dirty_updates_visible_direct(suite) -> [];
 1317: dirty_updates_visible_direct(Config) when is_list(Config) ->
 1318:     dirty_visibility(outside_trans, Config).
 1319: 
 1320: dirty_reads_regardless_of_trans(doc) ->
 1321:     ["Dirty reads are not affected by transaction context"];
 1322: dirty_reads_regardless_of_trans(suite) -> [];
 1323: dirty_reads_regardless_of_trans(Config) when is_list(Config) ->
 1324:     dirty_visibility(inside_trans, Config).
 1325: 
 1326: dirty_visibility(Mode, Config) ->
 1327:     [Node1] = ?acquire_nodes(1, Config), 
 1328:     Tab = list_to_atom(lists:concat([dirty_visibility, '_', Mode])), 
 1329: 
 1330:    ?match({atomic, ok},  mnesia:create_table([{name, Tab}, {ram_copies, [Node1]}])), 
 1331:     ValPos = 3, 
 1332: 
 1333:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)), 
 1334:     
 1335:     %% Start two processes
 1336:     {success, [A]} = ?start_activities([Node1]), 
 1337: 
 1338:     case Mode of
 1339: 	inside_trans ->
 1340: 	    ?start_transactions([A]), 
 1341: 	    A ! fun() ->
 1342: 			mnesia:write({Tab, a, 11}), 
 1343: 			mnesia:write({Tab, b, 22}), 
 1344: 			mnesia:write({Tab, c, 1}), 
 1345: 			mnesia:write({Tab, d, 2}), 
 1346: 			mnesia:write({Tab, e, 3}), 
 1347: 			lists:sort(mnesia:all_keys(Tab))
 1348: 		end, 
 1349: 	    ?match_receive({A, [a, b, c, d, e]});
 1350: 	outside_trans ->
 1351: 	    ignore
 1352:     end, 
 1353: 
 1354:     RecA = {Tab, a, 1}, 
 1355:     PatA = {Tab, '$1', 1}, 
 1356:     RecB = {Tab, b, 3}, 
 1357:     PatB = {Tab, '$1', 3}, 
 1358:     RecB2 = {Tab, b, 2}, 
 1359:     PatB2 = {Tab, '$1', 2}, 
 1360:     ?match([], mnesia:dirty_read({Tab, a})), 
 1361:     ?match([], mnesia:dirty_read({Tab, b})), 
 1362:     ?match([], mnesia:dirty_match_object(PatA)), 
 1363:     ?match([], mnesia:dirty_match_object(PatB)), 
 1364:     ?match([], mnesia:dirty_match_object(PatB2)), 
 1365:     ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)), 
 1366:     ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)), 
 1367:     ?match([], mnesia:dirty_index_match_object(PatA, ValPos)), 
 1368:     ?match([], mnesia:dirty_index_match_object(PatB, ValPos)), 
 1369:     ?match([], mnesia:dirty_index_match_object(PatB2, ValPos)), 
 1370:     ?match('$end_of_table', mnesia:dirty_first(Tab)), 
 1371: 
 1372:     %% dirty_write
 1373:     A ! fun() -> mnesia:dirty_write(RecA) end, 
 1374:     ?match_receive({A, ok}), 
 1375:     ?match([RecA], mnesia:dirty_read({Tab, a})), 
 1376:     ?match([RecA], mnesia:dirty_match_object(PatA)), 
 1377:     ?match(a, mnesia:dirty_first(Tab)), 
 1378:     ?match([RecA], mnesia:dirty_index_read(Tab, 1, ValPos)), 
 1379:     ?match([RecA], mnesia:dirty_index_match_object(PatA, ValPos)), 
 1380:     ?match('$end_of_table', mnesia:dirty_next(Tab, a)), 
 1381: 
 1382:     %% dirty_create
 1383:     A ! fun() -> mnesia:dirty_write(RecB) end, 
 1384:     ?match_receive({A, ok}), 
 1385:     ?match([RecB], mnesia:dirty_read({Tab, b})), 
 1386:     ?match([RecB], mnesia:dirty_match_object(PatB)), 
 1387:     ?match([RecB], mnesia:dirty_index_read(Tab, 3, ValPos)), 
 1388:     ?match([RecB], mnesia:dirty_index_match_object(PatB, ValPos)), 
 1389:     ?match('$end_of_table', 
 1390: 	   mnesia:dirty_next(Tab, mnesia:dirty_next(Tab, mnesia:dirty_first(Tab)))), 
 1391:     
 1392:     %% dirty_update_counter
 1393:     A ! fun() -> mnesia:dirty_update_counter({Tab, b}, -1) end, 
 1394:     ?match_receive({A, _}), 
 1395:     ?match([RecB2], mnesia:dirty_read({Tab, b})), 
 1396:     ?match([], mnesia:dirty_match_object(PatB)), 
 1397:     ?match([RecB2], mnesia:dirty_match_object(PatB2)), 
 1398:     ?match([RecB2], mnesia:dirty_index_read(Tab, 2, ValPos)), 
 1399:     ?match([], mnesia:dirty_index_match_object(PatB, ValPos)), 
 1400:     ?match([RecB2], mnesia:dirty_index_match_object(PatB2, ValPos)), 
 1401:     ?match('$end_of_table', 
 1402: 	   mnesia:dirty_next(Tab, mnesia:dirty_next(Tab, mnesia:dirty_first(Tab)))), 
 1403: 
 1404:     %% dirty_delete
 1405:     A ! fun() -> mnesia:dirty_delete({Tab, b}) end, 
 1406:     ?match_receive({A, ok}), 
 1407:     ?match([], mnesia:dirty_read({Tab, b})), 
 1408:     ?match([], mnesia:dirty_match_object(PatB2)), 
 1409:     ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)), 
 1410:     ?match([], mnesia:dirty_index_match_object(PatB2, ValPos)), 
 1411:     ?match(a, mnesia:dirty_first(Tab)), 
 1412:     ?match('$end_of_table', mnesia:dirty_next(Tab, a)), 
 1413: 
 1414:     %% dirty_delete_object
 1415:     ?match([RecA], mnesia:dirty_match_object(PatA)), 
 1416:     A ! fun() -> mnesia:dirty_delete_object(RecA) end, 
 1417:     ?match_receive({A, ok}), 
 1418:     ?match([], mnesia:dirty_read({Tab, a})), 
 1419:     ?match([], mnesia:dirty_match_object(PatA)), 
 1420:     ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)), 
 1421:     ?match([], mnesia:dirty_index_match_object(PatA, ValPos)), 
 1422:     ?match('$end_of_table', mnesia:dirty_first(Tab)), 
 1423:     
 1424:     case Mode of
 1425: 	inside_trans ->
 1426: 	    A ! end_trans, 
 1427: 	    ?match_receive({A, {atomic, end_trans}});
 1428: 	outside_trans ->
 1429: 	    ignore
 1430:     end, 
 1431:     ok.
 1432: 
 1433: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1434: trans_update_invisibible_outside_trans(doc) ->
 1435:     ["Updates in a transaction are invisible outside the transaction"];
 1436: trans_update_invisibible_outside_trans(suite) -> [];
 1437: trans_update_invisibible_outside_trans(Config) when is_list(Config) ->
 1438:     [Node1] = ?acquire_nodes(1, Config), 
 1439:     Tab = trans_update_invisibible_outside_trans, 
 1440: 
 1441:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
 1442: 					     {ram_copies, [Node1]}])), 
 1443:     ValPos = 3, 
 1444:     RecA = {Tab, a, 1}, 
 1445:     PatA = {Tab, '$1', 1}, 
 1446:     RecB = {Tab, b, 3}, 
 1447:     PatB = {Tab, '$1', 3}, 
 1448:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)), 
 1449: 
 1450:     Verify =
 1451: 	fun() ->
 1452: 		?match([], mnesia:dirty_read({Tab, a})), 
 1453: 		?match([], mnesia:dirty_read({Tab, b})), 
 1454: 		?match([], mnesia:dirty_match_object(PatA)), 
 1455: 		?match([], mnesia:dirty_match_object(PatB)), 
 1456: 		?match([], mnesia:dirty_index_read(Tab, 1, ValPos)), 
 1457: 		?match([], mnesia:dirty_index_read(Tab, 3, ValPos)), 
 1458: 		?match([], mnesia:dirty_index_match_object(PatA, ValPos)), 
 1459: 		?match([], mnesia:dirty_index_match_object(PatB, ValPos)), 
 1460: 		?match('$end_of_table', mnesia:dirty_first(Tab))
 1461: 	     end, 
 1462: 
 1463:     Fun = fun() ->
 1464: 		  ?match(ok, mnesia:write(RecA)), 
 1465: 		  Verify(), 
 1466: 
 1467: 		  ?match(ok, mnesia:write(RecB)), 
 1468: 		  Verify(), 
 1469: 
 1470: 		  ?match(ok, mnesia:delete({Tab, b})), 
 1471: 		  Verify(), 
 1472: 		  
 1473: 		  ?match([RecA], mnesia:match_object(PatA)), 
 1474: 		  Verify(), 
 1475: 
 1476: 		  ?match(ok, mnesia:delete_object(RecA)), 
 1477: 		  Verify(), 
 1478: 		  ok
 1479: 	  end, 
 1480:     ?match({atomic, ok}, mnesia:transaction(Fun)), 
 1481:     Verify(), 
 1482:     ok.
 1483: 
 1484: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1485: trans_update_visible_inside_trans(doc) ->
 1486:     ["Updates in a transaction are visible in the same transaction"];
 1487: trans_update_visible_inside_trans(suite) -> [];
 1488: trans_update_visible_inside_trans(Config) when is_list(Config) ->
 1489:     [Node1] = ?acquire_nodes(1, Config), 
 1490:     Tab = trans_update_visible_inside_trans, 
 1491: 
 1492:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
 1493: 					     {ram_copies, [Node1]}])), 
 1494:     ValPos = 3, 
 1495:     RecA = {Tab, a, 1}, 
 1496:     PatA = {Tab, '$1', 1}, 
 1497:     RecB = {Tab, b, 3}, 
 1498:     PatB = {Tab, '$1', 3}, 
 1499:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)), 
 1500: 
 1501:     Fun = fun() ->
 1502: 		  %% write
 1503: 		  ?match(ok, mnesia:write(RecA)), 
 1504: 		  ?match([RecA], mnesia:read({Tab, a})), 
 1505: 		  ?match([RecA], mnesia:wread({Tab, a})), 
 1506: 		  ?match([RecA], mnesia:match_object(PatA)), 
 1507: 		  ?match([a], mnesia:all_keys(Tab)), 
 1508: 		  ?match([RecA], mnesia:index_match_object(PatA, ValPos)), 
 1509: 		  ?match([RecA], mnesia:index_read(Tab, 1, ValPos)), 
 1510: 
 1511: 		  %% create
 1512: 		  ?match(ok, mnesia:write(RecB)), 
 1513: 		  ?match([RecB], mnesia:read({Tab, b})), 
 1514: 		  ?match([RecB], mnesia:wread({Tab, b})), 
 1515: 		  ?match([RecB], mnesia:match_object(PatB)), 
 1516: 		  ?match([RecB], mnesia:index_match_object(PatB, ValPos)), 
 1517: 		  ?match([RecB], mnesia:index_read(Tab, 3, ValPos)), 
 1518: 		  
 1519: 		  %% delete
 1520: 		  ?match(ok, mnesia:delete({Tab, b})), 
 1521: 		  ?match([], mnesia:read({Tab, b})), 
 1522: 		  ?match([], mnesia:wread({Tab, b})), 
 1523: 		  ?match([], mnesia:match_object(PatB)), 
 1524: 		  ?match([a], mnesia:all_keys(Tab)), 
 1525: 		  ?match([], mnesia:index_match_object(PatB, ValPos)), 
 1526: 		  ?match([], mnesia:index_read(Tab, 2, ValPos)), 
 1527: 		  ?match([], mnesia:index_read(Tab, 3, ValPos)), 
 1528: 
 1529: 		  %% delete_object
 1530: 		  ?match(ok, mnesia:delete_object(RecA)), 
 1531: 		  ?match([], mnesia:read({Tab, a})), 
 1532: 		  ?match([], mnesia:wread({Tab, a})), 
 1533: 		  ?match([], mnesia:match_object(PatA)), 
 1534: 		  ?match([], mnesia:all_keys(Tab)), 
 1535: 		  ?match([], mnesia:index_match_object(PatA, ValPos)), 
 1536: 		  ?match([], mnesia:index_read(Tab, 2, ValPos)), 
 1537: 		  ?match([], mnesia:index_read(Tab, 3, ValPos)), 
 1538: 		  ok
 1539: 	  end, 
 1540:     ?match({atomic, ok}, mnesia:transaction(Fun)), 
 1541:     ok.
 1542: 
 1543: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1544: write_shadows(doc) ->
 1545:     ["Tests whether the shadow shows the correct object when",
 1546:      "writing to the table"];
 1547: write_shadows(suite) -> [];
 1548: write_shadows(Config) when is_list(Config) ->
 1549:     [Node1] = ?acquire_nodes(1, Config), 
 1550:     Tab = write_shadows, 
 1551: 
 1552:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
 1553: 					     {ram_copies, [Node1]}, 
 1554: 					     {type,  set}])), 
 1555:     ValPos = 3, 
 1556:     RecA1 = {Tab, a, 1}, 
 1557:     PatA1 = {Tab, '$1', 1}, 
 1558:     RecA2 = {Tab, a, 2}, 
 1559:     PatA2 = {Tab, '$1', 2}, 
 1560: 
 1561: 
 1562:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)), 
 1563:        
 1564:     Fun1 = fun() ->
 1565: 		   ?match(ok, mnesia:write(RecA1)), 
 1566: 		   ok
 1567: 	   end, 
 1568: 
 1569:     ?match({atomic, ok}, mnesia:transaction(Fun1)), 
 1570: 
 1571:     Fun2 = fun() ->
 1572: 		  %% write shadow old write - is the confirmed value visable
 1573: 		  %%                          in the shadow ?
 1574: 		  ?match([RecA1], mnesia:read({Tab, a})), 
 1575: 		  ?match([RecA1], mnesia:wread({Tab, a})), 
 1576: 		  ?match([RecA1], mnesia:match_object(PatA1)), 
 1577: 		  ?match([a], mnesia:all_keys(Tab)), 
 1578: 		  ?match([RecA1], mnesia:index_match_object(PatA1, ValPos)), 
 1579: 		  ?match([RecA1], mnesia:index_read(Tab, 1, ValPos)), 
 1580: 
 1581: 		  %% write shadow new write - is a new value visable instead
 1582: 		  %%                          of the old value ?
 1583: 		  ?match(ok, mnesia:write(RecA2)), 
 1584: 
 1585: 		  ?match([RecA2], mnesia:read({Tab, a})), 
 1586: 		  ?match([RecA2], mnesia:wread({Tab, a})), 
 1587: 		  ?match([RecA2], mnesia:match_object(PatA2)), 		  %% delete shadow old but not new write - is the new value visable
 1588: 
 1589: 		  ?match([a], mnesia:all_keys(Tab)), 
 1590: 		  ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 
 1591: 		  ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 
 1592: 		  ok
 1593: 
 1594: 	  end, 
 1595:     ?match({atomic, ok}, mnesia:transaction(Fun2)), 
 1596:     ok.
 1597: 
 1598: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1599: delete_shadows(doc) ->
 1600:     ["Test whether the shadow shows the correct object when deleting objects"];
 1601: delete_shadows(suite) -> [];
 1602: delete_shadows(Config) when is_list(Config) ->
 1603:     [Node1] = ?acquire_nodes(1, Config), 
 1604:     Tab = delete_shadows, 
 1605: 
 1606:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
 1607: 					     {ram_copies, [Node1]}, 
 1608: 					     {type,  set}])), 
 1609:     ValPos = 3, 
 1610:     OidA  = {Tab, a}, 
 1611:     RecA1 = {Tab, a, 1}, 
 1612:     PatA1 = {Tab, '$1', 1}, 
 1613:     RecA2 = {Tab, a, 2}, 
 1614:     PatA2 = {Tab, '$1', 2}, 
 1615: 
 1616: 
 1617:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)), 
 1618:        
 1619:     Fun1 = fun() ->
 1620: 		   ?match(ok, mnesia:write(RecA1)), 
 1621: 		   ok
 1622: 	   end, 
 1623: 
 1624:     ?match({atomic, ok}, mnesia:transaction(Fun1)), 
 1625: 
 1626:     Fun2 = fun() ->
 1627: 		   
 1628: 		   
 1629: 		  %% delete shadow old write - is the confirmed value invisible
 1630: 		  %%                           when deleted in the transaction ?
 1631: 		  ?match(ok, mnesia:delete(OidA)), 
 1632: 
 1633: 		  ?match([], mnesia:read({Tab, a})), 
 1634: 		  ?match([], mnesia:wread({Tab, a})), 
 1635: 		  ?match([], mnesia:match_object(PatA1)), 
 1636: 		  ?match([], mnesia:all_keys(Tab)), 
 1637: 		  ?match([], mnesia:index_match_object(PatA1, ValPos)), 
 1638: 		  ?match([], mnesia:index_read(Tab, 1, ValPos)), 
 1639: 
 1640: 		  %% delete shadow old but not new write - is the new value visable
 1641: 		  %%                           when the old one was deleted ?
 1642: 		  ?match(ok, mnesia:write(RecA2)), 
 1643: 
 1644: 		  ?match([RecA2], mnesia:read({Tab, a})), 
 1645: 		  ?match([RecA2], mnesia:wread({Tab, a})), 
 1646: 		  ?match([RecA2], mnesia:match_object(PatA2)), 
 1647: 		  ?match([a], mnesia:all_keys(Tab)), 
 1648: 		  ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 
 1649: 		  ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 
 1650: 	
 1651: 		   %% delete shadow old and new write - is the new value invisable
 1652: 		  %%                           when deleted ?
 1653: 		  ?match(ok, mnesia:delete(OidA)), 
 1654: 
 1655: 		  ?match([], mnesia:read({Tab, a})), 
 1656: 		  ?match([], mnesia:wread({Tab, a})), 
 1657: 		  ?match([], mnesia:match_object(PatA2)), 
 1658: 		  ?match([], mnesia:all_keys(Tab)), 
 1659: 		  ?match([], mnesia:index_match_object(PatA2, ValPos)), 
 1660: 		  ?match([], mnesia:index_read(Tab, 2, ValPos)), 
 1661: 		  ok
 1662: 
 1663: 	  end, 
 1664:     ?match({atomic, ok}, mnesia:transaction(Fun2)), 
 1665:     ok.
 1666: 
 1667: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1668: write_delete_shadows_bag(doc) ->
 1669:     ["Test the visibility of written and deleted objects in an bag type table"];
 1670: write_delete_shadows_bag(suite) -> [];
 1671: write_delete_shadows_bag(Config) when is_list(Config) ->
 1672:     [Node1] = ?acquire_nodes(1, Config), 
 1673:     Tab = write_delete_shadows_bag, 
 1674: 
 1675:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
 1676: 					       {ram_copies, [Node1]}, 
 1677: 					       {type,  bag}])), 
 1678:     ValPos = 3, 
 1679:     OidA  = {Tab, a}, 
 1680: 
 1681:     RecA1 = {Tab, a, 1}, 
 1682:     PatA1 = {Tab, '$1', 1}, 
 1683: 
 1684:     RecA2 = {Tab, a, 2}, 
 1685:     PatA2 = {Tab, '$1', 2}, 
 1686: 
 1687:     RecA3 = {Tab, a, 3}, 
 1688:     PatA3 = {Tab, '$1', 3}, 
 1689: 
 1690:     PatA  = {Tab, a, '_'}, 
 1691: 
 1692: 
 1693:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)), 
 1694: 
 1695:     Fun1 = fun() ->
 1696: 		   ?match(ok, mnesia:write(RecA1)), 
 1697: 		   ?match(ok, mnesia:write(RecA2)), 
 1698: 		   ok
 1699: 	   end, 
 1700: 
 1701:     ?match({atomic, ok}, mnesia:transaction(Fun1)), 
 1702: 
 1703:     Fun2 = fun() ->		   		   
 1704: 		   %% delete shadow old write - is the confirmed value invisible
 1705: 		   %%                           when deleted in the transaction ?
 1706: 		   ?match(ok, mnesia:delete_object(RecA1)), 
 1707: 
 1708: 		   ?match([RecA2], mnesia:read({Tab, a})), 
 1709: 		   ?match([RecA2], mnesia:wread({Tab, a})), 
 1710: 		   ?match([RecA2], mnesia:match_object(PatA2)), 
 1711: 		   ?match([a], mnesia:all_keys(Tab)), 
 1712: 		   ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 
 1713: 		   ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 
 1714: 
 1715: 		   ?match(ok, mnesia:delete(OidA)), 
 1716: 
 1717: 		   ?match([], mnesia:read({Tab, a})), 
 1718: 		   ?match([], mnesia:wread({Tab, a})), 
 1719: 		   ?match([], mnesia:match_object(PatA1)), 
 1720: 		   ?match([], mnesia:all_keys(Tab)), 
 1721: 		   ?match([], mnesia:index_match_object(PatA1, ValPos)), 
 1722: 		   ?match([], mnesia:index_read(Tab, 1, ValPos)), 
 1723: 
 1724: 		   %% delete shadow old but not new write - are both new value visable
 1725: 		   %%                           when the old one was deleted ?
 1726: 		   ?match(ok, mnesia:write(RecA2)), 
 1727: 		   ?match(ok, mnesia:write(RecA3)), 
 1728: 
 1729: 
 1730: 		   ?match([RecA2, RecA3], lists:sort(mnesia:read({Tab, a}))), 
 1731: 		   ?match([RecA2, RecA3], lists:sort(mnesia:wread({Tab, a}))), 
 1732: 		   ?match([RecA2], mnesia:match_object(PatA2)), 
 1733: 		   ?match([a], mnesia:all_keys(Tab)), 
 1734: 		   ?match([RecA2, RecA3], lists:sort(mnesia:match_object(PatA))),
 1735: 		   ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)),
 1736: 		   ?match([RecA3], mnesia:index_match_object(PatA3, ValPos)),
 1737: 		   ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 
 1738: 
 1739: 		   %% delete shadow old and new write - is the new value invisable
 1740: 		   %%                           when deleted ?
 1741: 		   ?match(ok, mnesia:delete(OidA)), 
 1742: 
 1743: 		   ?match([], mnesia:read({Tab, a})), 
 1744: 		   ?match([], mnesia:wread({Tab, a})), 
 1745: 		   ?match([], mnesia:match_object(PatA2)), 
 1746: 		   ?match([], mnesia:all_keys(Tab)), 
 1747: 		   ?match([], mnesia:index_match_object(PatA2, ValPos)), 
 1748: 		   ?match([], mnesia:index_read(Tab, 2, ValPos)), 
 1749: 		   ok
 1750: 	   end, 
 1751:     ?match({atomic, ok}, mnesia:transaction(Fun2)), 
 1752:     ok.
 1753: 
 1754: write_delete_shadows_bag2(doc) ->
 1755:     ["Test the visibility of written and deleted objects in an bag type table " 
 1756:      "and verifies the results"];
 1757: write_delete_shadows_bag2(suite) -> [];
 1758: write_delete_shadows_bag2(Config) when is_list(Config) ->
 1759: 
 1760:     [Node1] = ?acquire_nodes(1, Config), 
 1761:     Tab = w_d_s_b, 
 1762: 
 1763:     ?match({atomic, ok},  mnesia:create_table([{name, Tab}, 
 1764: 					       {ram_copies, [Node1]}, 
 1765: 					       {type,  bag}])),     
 1766:     Del = fun() ->
 1767: 		  R1 = mnesia:read({Tab, 1}),
 1768: 		  mnesia:delete({Tab, 1}),
 1769: 		  R2 = mnesia:read({Tab, 1}),
 1770: 		  mnesia:write({Tab, 1, 1}),
 1771: 		  mnesia:write({Tab, 1, 2}),
 1772: 		  R3 = mnesia:read({Tab, 1}),
 1773: 		  {R1, R2, R3}
 1774: 	  end,
 1775:     DelObj = fun() ->
 1776: 		     R1 = mnesia:read({Tab, 2}),
 1777: 		     mnesia:delete_object({Tab, 2, 2}),
 1778: 		     R2 = mnesia:read({Tab, 2}),
 1779: 		     mnesia:write({Tab, 2, 1}),
 1780: 		     mnesia:write({Tab, 2, 2}),
 1781: 		     R3 = mnesia:read({Tab, 2}),
 1782: 		     {R1, R2, R3}
 1783: 	     end,
 1784:     Both1 = [{Tab, 1, 1}, {Tab, 1, 2}],   
 1785:     Both2 = [{Tab, 2, 1}, {Tab, 2, 2}],   
 1786:     ?match({atomic, {[], [], Both1}}, mnesia:transaction(Del)),
 1787:     ?match({atomic, {Both1, [], Both1}}, mnesia:transaction(Del)),
 1788:     ?match({atomic, Both1}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
 1789:     ?match({atomic, {[], [], Both2}}, mnesia:transaction(DelObj)),
 1790:     ?match({atomic, {Both2, [{Tab, 2, 1}], Both2}}, mnesia:transaction(DelObj)),
 1791:     ?match({atomic, Both2}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
 1792:     ?verify_mnesia([Node1], []).
 1793: 
 1794: shadow_search(doc) ->
 1795:     ["Verifies that ordered_set tables are ordered, and the order is kept" 
 1796:      "even when table is shadowed by transaction updates"];
 1797: shadow_search(suite) -> [];
 1798: shadow_search(Config) when is_list(Config) ->
 1799:     [Node1] = ?acquire_nodes(1, Config), 
 1800:     Tab1  = ss_oset,
 1801:     Tab2 = ss_set,
 1802:     Tab3 = ss_bag,
 1803:     Tabs = [Tab1,Tab2,Tab3],
 1804:     RecName = ss,
 1805:     ?match({atomic, ok},  mnesia:create_table([{name, Tab1}, 
 1806: 					       {ram_copies, [Node1]}, 
 1807: 					       {record_name, RecName},
 1808: 					       {type,  ordered_set}])),
 1809:     ?match({atomic, ok},  mnesia:create_table([{name, Tab2},
 1810: 					       {record_name, RecName},
 1811: 					       {ram_copies, [Node1]},
 1812: 					       {type,  set}])),
 1813:     ?match({atomic, ok},  mnesia:create_table([{name, Tab3},
 1814: 					       {record_name, RecName},
 1815: 					       {ram_copies, [Node1]}, 
 1816: 					       {type,  bag}])),
 1817:     Recs = [{RecName, K, K} || K <- [1,3,5]],
 1818:     [mnesia:dirty_write(Tab1, R) || R <- Recs],
 1819:     [mnesia:dirty_write(Tab2, R) || R <- Recs],
 1820:     [mnesia:dirty_write(Tab3, R) || R <- Recs],
 1821:     
 1822:     Match  = fun(Tab) -> mnesia:match_object(Tab, {'_','_','_'}, write) end,
 1823:     Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end,
 1824: %    Trans = fun(Fun,Args) -> mnesia:transaction(Fun,Args) end,
 1825:     LoopHelp = fun('$end_of_table',_) -> [];
 1826: 		  ({Res,Cont},Fun) -> 
 1827: 		       Sel = mnesia:select(Cont),
 1828: 		       Res ++ Fun(Sel, Fun)
 1829: 	       end,
 1830:     SelLoop = fun(Table) -> 
 1831: 		      Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read),
 1832: 		      LoopHelp(Sel,LoopHelp)
 1833: 	      end,
 1834:     
 1835:     R1 = {RecName, 2, 2}, R2 = {RecName, 4, 4},
 1836:     R3 = {RecName, 2, 3}, R4 = {RecName, 3, 1},
 1837:     R5 = {RecName, 104, 104},
 1838:     W1 = fun(Tab,Search) -> mnesia:write(Tab,R1,write),
 1839: 			    mnesia:write(Tab,R2,write), 
 1840: 			    Search(Tab)
 1841: 	 end,
 1842:     S1 = lists:sort([R1,R2|Recs]),
 1843:     ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,Select])),
 1844:     ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,Match])),
 1845:     ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,SelLoop])),
 1846:     ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,Select]))),
 1847:     ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,SelLoop]))),
 1848:     ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,Match]))),
 1849:     ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,Select]))),
 1850:     ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,SelLoop]))),
 1851:     ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,Match]))),
 1852:     [mnesia:dirty_delete_object(Tab,R) || R <- [R1,R2], Tab <- Tabs],
 1853: 
 1854:     W2 = fun(Tab,Search) -> 
 1855: 		 mnesia:write(Tab,R3,write),
 1856: 		 mnesia:write(Tab,R1,write), 
 1857: 		 Search(Tab)
 1858: 	 end,
 1859:     S2 = lists:sort([R1|Recs]),
 1860:     S2Bag = lists:sort([R1,R3|Recs]),
 1861:     ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,Select])),
 1862:     ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,SelLoop])),
 1863:     ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,Match])),
 1864:     ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,Select]))),
 1865:     ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,SelLoop]))),
 1866:     ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,Match]))),
 1867:     ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,Select]))),
 1868:     ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,SelLoop]))),
 1869:     ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,Match]))),
 1870: %%    [mnesia:dirty_delete_object(Tab,R) || R <- [R1,R3], Tab <- Tabs],
 1871: 
 1872:     W3 = fun(Tab,Search) -> 
 1873: 		 mnesia:write(Tab,R4,write),
 1874: 		 mnesia:delete(Tab,element(2,R1),write), 
 1875: 		 Search(Tab)
 1876: 	 end,
 1877:     S3Bag = lists:sort([R4|lists:delete(R1,Recs)]),
 1878:     S3 = lists:delete({RecName,3,3},S3Bag),
 1879:     ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,Select])),
 1880:     ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,SelLoop])),
 1881:     ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,Match])),
 1882:     ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,SelLoop]))),
 1883:     ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,Select]))),
 1884:     ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,Match]))),
 1885:     ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,Select]))),
 1886:     ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,SelLoop]))),
 1887:     ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,Match]))),
 1888: 
 1889:     W4 = fun(Tab,Search) -> 
 1890: 		 mnesia:delete(Tab,-1,write),
 1891: 		 mnesia:delete(Tab,4 ,write),
 1892: 		 mnesia:delete(Tab,17,write),
 1893: 		 mnesia:delete_object(Tab,{RecName, -1, x},write),
 1894: 		 mnesia:delete_object(Tab,{RecName, 4, x},write),
 1895: 		 mnesia:delete_object(Tab,{RecName, 42, x},write),
 1896: 		 mnesia:delete_object(Tab,R2,write),
 1897: 		 mnesia:write(Tab, R5, write),
 1898: 		 Search(Tab)
 1899: 	 end,
 1900:     S4Bag = lists:sort([R5|S3Bag]),
 1901:     S4    = lists:sort([R5|S3]),
 1902:     ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,Select])),
 1903:     ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,SelLoop])),
 1904:     ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,Match])),
 1905:     ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,Select]))),
 1906:     ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,SelLoop]))),
 1907:     ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,Match]))),
 1908:     ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,Select]))),
 1909:     ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,SelLoop]))),
 1910:     ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,Match]))),
 1911:     [mnesia:dirty_delete_object(Tab,R) || R <- [{RecName,3,3},R5], Tab <- Tabs],
 1912:         
 1913:     %% hmmm anything more??
 1914:     
 1915:     ?verify_mnesia([Node1], []).
 1916: 
 1917: 
 1918: rr_kill_copy(suite) -> [];
 1919: rr_kill_copy(Config) when is_list(Config) ->
 1920:     Ns = ?acquire_nodes(3,Config ++ [{tc_timeout, 60000}]),
 1921:     DeleteMe = fun(_Tab,Where2read) ->
 1922: 		       ?match([], mnesia_test_lib:kill_mnesia([Where2read]))
 1923: 	       end,
 1924:     Del = removed_resources(Ns, DeleteMe),
 1925:     ?verify_mnesia(Ns -- [Del], []).
 1926: 
 1927: removed_resources([_N1,N2,N3], DeleteRes) ->
 1928:     Tab = del_res,
 1929:     ?match({atomic, ok}, mnesia:create_table(Tab,[{ram_copies, [N2,N3]}])),
 1930:     
 1931:     Init = fun() -> [mnesia:write({Tab,Key,Key}) || Key <- lists:seq(0,99)] end,
 1932:     ?match([], [Bad || Bad <- mnesia:sync_dirty(Init), Bad /= ok]),
 1933:     
 1934:     Where2Read = mnesia:table_info(Tab, where_to_read),
 1935:     [Keep]  = [N2,N3] -- [Where2Read],
 1936:     Tester = self(),
 1937:     
 1938:     Conflict = fun() -> 
 1939: 		       %% Read a value..
 1940: 		       [{Tab,1,Val}] = mnesia:read({Tab,1}),
 1941: 		       case get(restart) of
 1942: 			   undefined -> 
 1943: 			       Tester ! {pid_1, self()},
 1944: 			       %% Wait for sync, the read value have been 
 1945: 			       %% updated and this function should be restarted.
 1946: 			       receive {Tester,sync} -> ok  end,
 1947: 			       put(restart, restarted);
 1948: 			   restarted ->
 1949: 			       ok
 1950: 		       end,		       
 1951: 		       mnesia:write({Tab,1,Val+10})
 1952: 	       end,
 1953:     Lucky    = fun() -> 
 1954: 		       [{Tab,1,Val}] = mnesia:read({Tab,1}),
 1955: 		       mnesia:write({Tab,1,Val+100})
 1956: 	       end,
 1957:     
 1958:     CPid = spawn_link(fun() -> Tester ! {self(), mnesia:transaction(Conflict)} end),
 1959:     
 1960:     %% sync first transaction
 1961:     receive {pid_1, CPid} -> synced end,
 1962: 
 1963:     DeleteRes(Tab, Where2Read),
 1964: 
 1965:     ?match(Keep, mnesia:table_info(Tab, where_to_read)),
 1966:     
 1967:     %% Run the other/Lucky transaction, this should work since 
 1968:     %% it won't grab a lock on the conflicting transactions Where2Read node.
 1969:     
 1970:     LPid = spawn_link(Keep, fun() -> Tester ! {self(),mnesia:transaction(Lucky)} end),
 1971:     ?match_receive({LPid,{atomic,ok}}),
 1972:     
 1973:     %% Continue Transaction no 1
 1974:     CPid ! {self(), sync},
 1975:     
 1976:     ?match(ok, receive {CPid,{atomic,ok}} -> ok after 2000 -> process_info(self()) end),
 1977:     
 1978:     ?match({atomic,[{del_res,1,111}]}, mnesia:transaction(fun() -> mnesia:read({Tab,1}) end)),
 1979:     Where2Read.
 1980: 
 1981: nasty(suite) -> [];
 1982: 
 1983: nasty(doc) ->
 1984:     ["Tries to fullfill a rather nasty locking scenario, where we have had "
 1985:      "bugs, the testcase tries a combination of locks in locker queue"];
 1986: 
 1987: %%  This testcase no longer works as it was intended to show errors when 
 1988: %%  tablelocks was allowed to be placed in the queue though locks existed 
 1989: %%  in the queue with less Tid's. This is no longer allowed and the testcase
 1990: %%  has been update.
 1991: 
 1992: nasty(Config) ->
 1993:     ?acquire_nodes(1, Config),
 1994:     Tab = nasty,
 1995:     ?match({atomic, ok}, mnesia:create_table(Tab, [])),    
 1996:     Coord = self(),        				    
 1997:     Write = fun(Key) ->
 1998: 		    mnesia:write({Tab, Key, write}),	 
 1999: 		    Coord ! {write, Key, self(), mnesia:get_activity_id()},
 2000: 		    receive 
 2001: 			continue ->
 2002: 			    ok
 2003: 		    end,	    
 2004: 		    Coord ! {done, {write, Key}, self()}		    
 2005: 	    end,
 2006:     
 2007:     Update = fun(Key) ->
 2008: 		     Coord ! {update, Key, self(), mnesia:get_activity_id()},
 2009: 		     receive
 2010: 			 continue ->
 2011: 			     ok
 2012: 		     end,
 2013: 		     mnesia:read({Tab, Key}),
 2014: 		     mnesia:write({Tab, Key, update}),
 2015: 		     receive
 2016: 			 continue ->
 2017: 			     ok
 2018: 		     end,
 2019: 		     
 2020: 		     Coord ! {done, {update, Key}, self()}
 2021: 	     end,
 2022:     
 2023:     TabLock = fun() ->
 2024: 		      Coord ! {tablock, Tab, self(), mnesia:get_activity_id()},
 2025: 		      receive
 2026: 			  continue ->
 2027: 			      ok
 2028: 		      end,
 2029: 		      mnesia:lock({table, Tab}, write),
 2030: 		      Coord ! {done, {tablock, Tab}, self()}
 2031: 	      end,
 2032: 
 2033:     Up = spawn_link(mnesia, transaction, [Update,  [0]]),
 2034:     ?match_receive({update, 0, Up, _Tid}),
 2035:     TL = spawn_link(mnesia, transaction, [TabLock]), 
 2036:     ?match_receive({tablock, Tab, _Tl, _Tid}),
 2037:     W0 = spawn_link(mnesia, transaction, [Write, [0]]), 
 2038:     ?match_receive({write, 0, W0, _Tid}),
 2039:     W1 = spawn_link(mnesia, transaction, [Write, [1]]), 
 2040:     ?match_receive({write, 1, W1, _Tid}),
 2041:     
 2042:     %% Nothing should be in msg queue!
 2043:     ?match(timeout, receive A -> A after 1000 -> timeout end),
 2044:     Up ! continue,  %% Should be queued
 2045:     ?match(timeout, receive A -> A after 1000 -> timeout end),
 2046:     TL ! continue,  %% Should be restarted
 2047: %    ?match({tablock, _, _, _}, receive A -> A after 1000 -> timeout end),
 2048:     ?match(timeout, receive A -> A after 1000 -> timeout end),
 2049:     
 2050:     LQ1 = mnesia_locker:get_lock_queue(),    
 2051:     ?match({2, _}, {length(LQ1), LQ1}),
 2052:     W0 ! continue,                          % Up should be in queue
 2053:     ?match_receive({done, {write, 0}, W0}),
 2054:     ?match_receive({'EXIT', W0, normal}),
 2055: 
 2056:     TL ! continue,   % Should stay in queue W1
 2057:     ?match(timeout, receive A -> A after 1000 -> timeout end),
 2058:     Up ! continue,   % Should stay in queue (TL got higher tid)
 2059:     ?match(timeout, receive A -> A after 1000 -> timeout end),
 2060: 
 2061:     LQ2 = mnesia_locker:get_lock_queue(),
 2062:     ?match({2, _}, {length(LQ2), LQ2}),    
 2063:     
 2064:     W1 ! continue,
 2065:     ?match_receive({done, {write, 1}, W1}),
 2066:     get_exit(W1),
 2067:     get_exit(TL),
 2068:     ?match_receive({done, {tablock,Tab}, TL}),
 2069:     get_exit(Up),
 2070:     ?match_receive({done, {update, 0}, Up}),
 2071:     
 2072:     ok.
 2073: 
 2074: get_exit(Pid) ->
 2075:     receive 
 2076: 	{'EXIT', Pid, normal} ->
 2077: 	    ok
 2078:     after 10000 ->
 2079: 	    ?error("Timeout EXIT ~p~n", [Pid])
 2080:     end.
 2081: 
 2082: 
 2083: foldl(doc) ->
 2084:     [""];
 2085: foldl(suite) ->
 2086:     [];
 2087: foldl(Config) when is_list(Config) ->
 2088:     Nodes = [_,N2] = ?acquire_nodes(2, Config),
 2089:     Tab1 = foldl_local,
 2090:     Tab2 = foldl_remote,
 2091:     Tab3 = foldl_ordered, 
 2092:     Tab11 = foldr_local,
 2093:     Tab21 = foldr_remote,
 2094:     Tab31 = foldr_ordered, 
 2095:     ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
 2096:     ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}, {type, bag}])),
 2097:     ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes}, 
 2098: 						    {type, ordered_set}])),    
 2099:     ?match({atomic, ok}, mnesia:create_table(Tab11, [{ram_copies, Nodes}])),
 2100:     ?match({atomic, ok}, mnesia:create_table(Tab21, [{ram_copies, [N2]}, {type, bag}])),
 2101:     ?match({atomic, ok}, mnesia:create_table(Tab31, [{ram_copies, Nodes}, 
 2102: 						    {type, ordered_set}])),    
 2103: 
 2104: 
 2105:     Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 10)],
 2106:     Tab2Els = [{Tab2, 1, 2} | [{Tab2, N, N} || N <- lists:seq(1, 10)]],
 2107:     Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 10)],
 2108:     Tab11Els = [{Tab11, N, N} || N <- lists:seq(1, 10)],
 2109:     Tab21Els = [{Tab21, 1, 2} | [{Tab21, N, N} || N <- lists:seq(1, 10)]],
 2110:     Tab31Els = [{Tab31, N, N} || N <- lists:seq(1, 10)],
 2111:     
 2112:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab1Els],
 2113:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab2Els],
 2114:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab3Els],
 2115:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab11Els],
 2116:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab21Els],
 2117:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab31Els],
 2118:     
 2119:     Get = fun(E, A) -> [E | A] end,
 2120:     
 2121:     %% Before 
 2122:     AddB = fun(Tab, Func) -> 
 2123: 		   mnesia:write({Tab, 0, 0}),
 2124: 		   mnesia:write({Tab, 1, 0}),
 2125: 		   mnesia:write({Tab, 11, 0}),
 2126: 		   mnesia:Func(Get, [], Tab)
 2127: 	   end,        
 2128:     AddT1 = [{Tab1, 0, 0}, {Tab1, 1, 0}] ++ tl(Tab1Els) ++ [{Tab1, 11, 0}],
 2129:     AddT2 = lists:sort([{Tab2, 0, 0}, {Tab2, 1, 0}] ++ Tab2Els ++ [{Tab2, 11, 0}]),
 2130:     AddT3 = [{Tab3, 0, 0}, {Tab3, 1, 0}] ++ tl(Tab3Els) ++ [{Tab3, 11, 0}],
 2131:     AddT11 = [{Tab11, 0, 0}, {Tab11, 1, 0}] ++ tl(Tab11Els) ++ [{Tab11, 11, 0}],
 2132:     AddT21 = lists:sort([{Tab21, 0, 0}, {Tab21, 1, 0}] ++ Tab21Els ++ [{Tab21, 11, 0}]),
 2133:     AddT31 = [{Tab31, 0, 0}, {Tab31, 1, 0}] ++ tl(Tab31Els) ++ [{Tab31, 11, 0}],
 2134:     
 2135:     ?match({atomic, AddT1}, sort_res(mnesia:transaction(AddB, [Tab1, foldl]))),
 2136:     ?match({atomic, AddT2}, sort_res(mnesia:transaction(AddB, [Tab2, foldl]))),
 2137:     ?match({atomic, AddT3}, rev_res(mnesia:transaction(AddB, [Tab3, foldl]))),
 2138:     ?match({atomic, AddT11}, sort_res(mnesia:transaction(AddB, [Tab11, foldr]))),
 2139:     ?match({atomic, AddT21}, sort_res(mnesia:transaction(AddB, [Tab21, foldr]))),
 2140:     ?match({atomic, AddT31}, mnesia:transaction(AddB, [Tab31, foldr])),
 2141:     
 2142:     ?match({atomic, ok}, mnesia:create_table(copy, [{ram_copies, [N2]},
 2143: 						    {record_name, Tab1}])),
 2144:     CopyRec = fun(NewRec, Acc) -> 
 2145: 		      %% OTP-5495
 2146: 		      W = fun() -> mnesia:write(copy, NewRec, write), [NewRec| Acc] end,
 2147: 		      {atomic,Res} = sort_res(mnesia:transaction(W)),
 2148: 		      Res
 2149: 	      end,
 2150:     Copy = fun() -> 
 2151: 		   AddT1 = mnesia:foldl(CopyRec, [], Tab1),
 2152: 		   AddT1 = sort_res(mnesia:foldl(Get, [], copy))
 2153: 	   end,
 2154:     ?match({atomic, AddT1}, sort_res(mnesia:transaction(Copy))),
 2155:     
 2156:     Del  = fun(E, A) -> mnesia:delete_object(E), [E|A] end,
 2157:     DelD = fun(Tab) ->
 2158: 		   mnesia:write({Tab, 12, 12}),
 2159: 		   mnesia:delete({Tab, 0}),
 2160: 		   mnesia:foldr(Del, [], Tab),
 2161: 		   mnesia:foldl(Get, [], Tab)
 2162: 	   end,
 2163:     ?match({atomic, []}, sort_res(mnesia:transaction(DelD, [Tab1]))),
 2164:     ?match({atomic, []}, sort_res(mnesia:transaction(DelD, [Tab2]))),
 2165:     ?match({atomic, []}, rev_res(mnesia:transaction(DelD, [Tab3]))),
 2166:     
 2167:     ListWrite = fun(Tab) ->   %% OTP-3893 
 2168: 			mnesia:write({Tab, [12], 12}),	
 2169: 			mnesia:foldr(Get, [], Tab)
 2170: 		end,
 2171:     ?match({atomic, [{Tab1, [12], 12}]}, sort_res(mnesia:transaction(ListWrite, [Tab1]))),
 2172:     ?match({atomic, [{Tab2, [12], 12}]}, sort_res(mnesia:transaction(ListWrite, [Tab2]))),
 2173:     ?match({atomic, [{Tab3, [12], 12}]}, rev_res(mnesia:transaction(ListWrite, [Tab3]))),
 2174:     
 2175:     ?verify_mnesia(Nodes, []).	   
 2176: 
 2177: sort_res({atomic, List}) when is_list(List) ->
 2178:     {atomic, lists:sort(List)};
 2179: sort_res(Else) when is_list(Else) ->
 2180:     lists:sort(Else);
 2181: sort_res(Else) ->
 2182:     Else.
 2183: 
 2184: rev_res({atomic, List}) ->
 2185:     {atomic, lists:reverse(List)};
 2186: rev_res(Else) ->
 2187:     Else.
 2188: 
 2189: 
 2190: first_next(doc) ->    [""];
 2191: first_next(suite) ->  [];
 2192: first_next(Config) when is_list(Config) ->
 2193:     Nodes = [_,N2] = ?acquire_nodes(2, Config),
 2194:     Tab1 = local,
 2195:     Tab2 = remote,
 2196:     Tab3 = ordered, 
 2197:     Tab4 = bag,
 2198:     Tabs = [Tab1,Tab2,Tab3,Tab4],
 2199:     
 2200:     ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
 2201:     ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}])),
 2202:     ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes}, 
 2203: 						    {type, ordered_set}])),
 2204:     ?match({atomic, ok}, mnesia:create_table(Tab4, [{ram_copies, Nodes}, 
 2205: 						    {type, bag}])),
 2206: 
 2207:     %% Some Helpers
 2208:     Trans = fun(Fun) -> mnesia:transaction(Fun) end,
 2209:     Continue = fun(first) -> next;
 2210: 		  (last) -> prev
 2211: 	       end,
 2212:     LoopHelp = fun('$end_of_table',_,_,_Fun) -> [];
 2213: 		  (Key,Tab,Op,Fun) -> 
 2214: 		       Next = mnesia:Op(Tab,Key),
 2215: 		       [Next |Fun(Next,Tab,Op,Fun)] 
 2216: 	       end,
 2217:     Loop = fun(Tab,Start) -> 
 2218: 		   First = mnesia:Start(Tab),
 2219: 		   Res = [First|LoopHelp(First,Tab,Continue(Start),LoopHelp)],
 2220: 		   case mnesia:table_info(Tab, type) of
 2221: 		       ordered_set when Start == first -> Res;
 2222: 		       ordered_set -> 
 2223: 			   {L1,L2} = lists:split(length(Res)-1,Res),
 2224: 			   lists:reverse(L1) ++ L2;
 2225: 		       _ -> lists:sort(Res)
 2226: 		   end	  
 2227: 	   end,
 2228: 
 2229:     %% Verify empty tables
 2230:     [?match({atomic, ['$end_of_table']}, 
 2231:  	    Trans(fun() -> Loop(Tab,first) end)) 
 2232:      || Tab <- Tabs],
 2233:     [?match({atomic, ['$end_of_table']}, 
 2234:  	    Trans(fun() -> Loop(Tab,last) end)) 
 2235:      || Tab <- Tabs],
 2236:     %% Verify that trans write is visible inside trans
 2237:     [?match({atomic, [0,10,'$end_of_table']}, 
 2238:  	    Trans(fun() ->
 2239:  			  mnesia:write({Tab,0,0}),
 2240:  			  mnesia:write({Tab,10,10}),
 2241:  			  Loop(Tab,first) end)) 
 2242:      || Tab <- Tabs],
 2243:     [?match({atomic, ['$end_of_table']}, 
 2244:  	    Trans(fun() -> 
 2245:  			  mnesia:delete({Tab,0}),
 2246:  			  mnesia:delete({Tab,10}),
 2247:  			  Loop(Tab,first) end)) 
 2248:      || Tab <- Tabs],
 2249: 
 2250:     [?match({atomic, [0,10,'$end_of_table']}, 
 2251:  	    Trans(fun() -> 
 2252:  			  mnesia:write({Tab,0,0}),
 2253:  			  mnesia:write({Tab,10,10}),
 2254:  			  Loop(Tab,last) end))
 2255:      || Tab <- Tabs],
 2256:     [?match({atomic, ['$end_of_table']}, 
 2257:  	    Trans(fun() -> 
 2258:  			  mnesia:delete({Tab,0}),
 2259:  			  mnesia:delete({Tab,10}),
 2260:  			  Loop(Tab,last) end)) 
 2261:      || Tab <- Tabs],
 2262: 
 2263:     Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 5)],
 2264:     Tab2Els = [{Tab2, N, N} || N <- lists:seq(1, 5)],
 2265:     Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 5)],
 2266:     Tab4Els = [{Tab4, 1, 2} | [{Tab4, N, N} || N <- lists:seq(1, 5)]],
 2267: 
 2268:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab1Els],
 2269:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab2Els],
 2270:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab3Els],
 2271:     [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab4Els],
 2272:     Keys = lists:sort(mnesia:dirty_all_keys(Tab1)),
 2273:     R1  =  Keys++ ['$end_of_table'],
 2274:     [?match({atomic, R1}, Trans(fun() -> Loop(Tab,first) end)) 
 2275:      || Tab <- Tabs],
 2276: 
 2277:     [?match({atomic, R1}, Trans(fun() -> Loop(Tab,last) end)) 
 2278:      || Tab <- Tabs],
 2279:     R2 = R1 -- [3],
 2280: 
 2281:     [?match({atomic, R2}, Trans(fun() -> mnesia:delete({Tab,3}),Loop(Tab,first) end)) 
 2282:      || Tab <- Tabs],
 2283:     [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,3,3}),Loop(Tab,first) end)) 
 2284:      || Tab <- Tabs],
 2285:     [?match({atomic, R2}, Trans(fun() -> mnesia:delete({Tab,3}),Loop(Tab,last) end)) 
 2286:      || Tab <- Tabs],
 2287:     [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,3,3}),Loop(Tab,last) end)) 
 2288:      || Tab <- Tabs],
 2289:     [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,4,19}),Loop(Tab,first) end)) 
 2290:      || Tab <- Tabs],
 2291:     [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,4,4}),Loop(Tab,last) end)) 
 2292:      || Tab <- Tabs],
 2293: 
 2294:     ?verify_mnesia(Nodes, []).
 2295: 
 2296: 
 2297: snmp_shadows(doc) ->    [""];
 2298: snmp_shadows(suite) ->  [];
 2299: snmp_shadows(Config) when is_list(Config) ->
 2300:     Nodes = ?acquire_nodes(1, Config),
 2301:     Tab = snmp_shadows,
 2302:     io:format("With fixstring~n", []),
 2303:     ?match({atomic, ok}, mnesia:create_table(Tab,[{snmp,[{key,{fix_string,integer}}]}])),
 2304:     snmp_shadows_test(Tab),
 2305:     ?match({atomic, ok}, mnesia:delete_table(Tab)),
 2306:     io:format("Without fixstring~n", []),
 2307:     ?match({atomic, ok}, mnesia:create_table(Tab,[{snmp,[{key,{string,integer}}]}])),
 2308:     snmp_shadows_test(Tab),
 2309:     ?verify_mnesia(Nodes, []).
 2310: 
 2311: snmp_shadows_test(Tab) ->
 2312:     [mnesia:dirty_write({Tab, {"string", N}, {N, init}}) || N <- lists:seq(2,8,2)],
 2313:     
 2314:     CheckOrder = fun(A={_,_,{_,_,State}}, Prev) ->
 2315: 			 ?match({true, A, Prev}, {Prev < A, A, Prev}),
 2316: 			 {State,A} 
 2317: 		 end,
 2318:     R1 = mnesia:sync_dirty(fun() -> loop_snmp(Tab, []) end),
 2319:     lists:mapfoldl(CheckOrder, {[],foo,foo}, R1),
 2320:     R2 = mnesia:transaction(fun() -> loop_snmp(Tab, []) end),
 2321:     ?match({atomic, R1}, R2),
 2322:     
 2323:     Shadow = fun() ->
 2324: 		     ok = mnesia:write({Tab, {"string",1}, {1,update}}),
 2325: 		     ok = mnesia:write({Tab, {"string",4}, {4,update}}),
 2326: 		     ok = mnesia:write({Tab, {"string",6}, {6,update}}),
 2327: 		     ok = mnesia:delete({Tab, {"string",6}}),
 2328: 		     ok = mnesia:write({Tab, {"string",9}, {9,update}}),
 2329: 		     ok = mnesia:write({Tab, {"string",3}, {3,update}}),
 2330: 		     ok = mnesia:write({Tab, {"string",5}, {5,update}}),
 2331: 		     [Row5] = mnesia:read({Tab, {"string",5}}),
 2332: 		     ok = mnesia:delete_object(Row5),
 2333: 		     loop_snmp(Tab, [])
 2334: 	     end,
 2335:     R3 = mnesia:sync_dirty(Shadow),
 2336:     {L3,_} = lists:mapfoldl(CheckOrder, {[],foo,foo}, R3),
 2337:     ?match([{1,update},{2,init},{3,update},{4,update},{8,init},{9,update}], L3),
 2338:     ?match({atomic, ok}, mnesia:clear_table(Tab)),
 2339: 
 2340:     [mnesia:dirty_write({Tab, {"string", N}, {N, init}}) || N <- lists:seq(2,8,2)],
 2341:     {atomic, R3} = mnesia:transaction(Shadow),
 2342:     {L4,_} = lists:mapfoldl(CheckOrder, {[],foo,foo}, R3),
 2343:     ?match([{1,update},{2,init},{3,update},{4,update},{8,init},{9,update}], L4),
 2344:     ok.
 2345:    
 2346: loop_snmp(Tab,Prev) ->
 2347:     case mnesia:snmp_get_next_index(Tab,Prev) of
 2348: 	{ok, SKey} ->
 2349: 	    {{ok,Row},_} = {mnesia:snmp_get_row(Tab, SKey),{?LINE,Prev,SKey}},
 2350: 	    {{ok,MKey},_} = {mnesia:snmp_get_mnesia_key(Tab,SKey),{?LINE,Prev,SKey}},
 2351: 	    ?match({[Row],Row,SKey,MKey}, {mnesia:read({Tab,MKey}),Row,SKey,MKey}),
 2352: 	    [{SKey, MKey, Row} | loop_snmp(Tab, SKey)];
 2353: 	endOfTable ->
 2354: 	    []
 2355:     end.