mnesia_tm.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,898 行 · 第 1/5 页

ERL
1,898
字号
    end.execute_outer(Mod, Fun, Args, Factor, Retries, Type) ->    case req(start_outer) of	{error, Reason} -> 	    {aborted, Reason};	{new_tid, Tid, Store} ->	    Ts = #tidstore{store = Store},	    NewTidTs = {Mod, Tid, Ts},	    put(mnesia_activity_state, NewTidTs),	    execute_transaction(Fun, Args, Factor, Retries, Type)    end.execute_inner(Mod, Tid, OldMod, Ts, Fun, Args, Factor, Retries, Type) ->    case req({add_store, Tid}) of	{error, Reason} ->	    {aborted, Reason};	{new_store, Ets} ->	    copy_ets(Ts#tidstore.store, Ets),	    Up = [{OldMod,Ts#tidstore.store} | Ts#tidstore.up_stores],	    NewTs = Ts#tidstore{level = 1 + Ts#tidstore.level,				store = Ets,				up_stores = Up},	    NewTidTs = {Mod, Tid, NewTs},	    put(mnesia_activity_state, NewTidTs),	    execute_transaction(Fun, Args, Factor, Retries, Type)    end.copy_ets(From, To) ->    do_copy_ets(?ets_first(From), From, To).do_copy_ets('$end_of_table', _,_) ->     ok;do_copy_ets(K, From, To) ->    Objs = ?ets_lookup(From, K),    insert_objs(Objs, To),    do_copy_ets(?ets_next(From, K), From, To).insert_objs([H|T], Tab) ->    ?ets_insert(Tab, H),    insert_objs(T, Tab);insert_objs([], _Tab) ->    ok.execute_transaction(Fun, Args, Factor, Retries, Type) ->    case catch apply_fun(Fun, Args, Type) of	{'EXIT', Reason} ->	    check_exit(Fun, Args, Factor, Retries, Reason, Type);	{atomic, Value} ->	    mnesia_lib:incr_counter(trans_commits),	    erase(mnesia_activity_state),	    %% no need to clear locks, already done by commit ...	    %% Flush any un processed mnesia_down messages we might have 	    flush_downs(),	    catch unlink(whereis(?MODULE)),	    {atomic, Value};	{nested_atomic, Value} ->	    mnesia_lib:incr_counter(trans_commits),	    {atomic, Value};	Value -> %% User called throw	    Reason = {aborted, {throw, Value}},	    return_abort(Fun, Args, Reason)    end.apply_fun(Fun, Args, Type) ->    Result = apply(Fun, Args),    case t_commit(Type) of	do_commit ->            {atomic, Result};        do_commit_nested ->            {nested_atomic, Result};        {do_abort, {aborted, Reason}} ->            {'EXIT', {aborted, Reason}};        {do_abort, Reason} ->            {'EXIT', {aborted, Reason}}    end.check_exit(Fun, Args, Factor, Retries, Reason, Type) ->    case Reason of	{aborted, C} when record(C, cyclic) ->	    maybe_restart(Fun, Args, Factor, Retries, Type, C);	{aborted, {node_not_running, N}} ->	    maybe_restart(Fun, Args, Factor, Retries, Type, {node_not_running, N});	{aborted, {bad_commit, N}} ->	    maybe_restart(Fun, Args, Factor, Retries, Type, {bad_commit, N});	_ -> 	    return_abort(Fun, Args, Reason)    end.maybe_restart(Fun, Args, Factor, Retries, Type, Why) ->    {Mod, Tid, Ts} = get(mnesia_activity_state),    case try_again(Retries) of	yes when Ts#tidstore.level == 1 ->	    restart(Mod, Tid, Ts, Fun, Args, Factor, Retries, Type, Why);	yes ->	    return_abort(Fun, Args, Why);	no ->	    return_abort(Fun, Args, {aborted, nomore})    end.try_again(infinity) -> yes;try_again(X) when number(X) , X > 1 -> yes;try_again(_) -> no.%% We can only restart toplevel transactions.%% If a deadlock situation occurs in a nested transaction%% The whole thing including all nested transactions need to be%% restarted. The stack is thus popped by a consequtive series of%% exit({aborted, #cyclic{}}) callsrestart(Mod, Tid, Ts, Fun, Args, Factor0, Retries0, Type, Why) ->    mnesia_lib:incr_counter(trans_restarts),    Retries = decr(Retries0),    case Why of	{bad_commit, _N} ->	    return_abort(Fun, Args, Why),	    Factor = 1,	    SleepTime = mnesia_lib:random_time(Factor, Tid#tid.counter),	    dbg_out("Restarting transaction ~w: in ~wms ~w~n", [Tid, SleepTime, Why]),	    timer:sleep(SleepTime),	    execute_outer(Mod, Fun, Args, Factor, Retries, Type);	{node_not_running, _N} ->   %% Avoids hanging in receive_release_tid_ack	    return_abort(Fun, Args, Why),	    Factor = 1,	    SleepTime = mnesia_lib:random_time(Factor, Tid#tid.counter),	    dbg_out("Restarting transaction ~w: in ~wms ~w~n", [Tid, SleepTime, Why]),	    timer:sleep(SleepTime),	    execute_outer(Mod, Fun, Args, Factor, Retries, Type);		_ ->	    SleepTime = mnesia_lib:random_time(Factor0, Tid#tid.counter),	    dbg_out("Restarting transaction ~w: in ~wms ~w~n", [Tid, SleepTime, Why]),	    	    if		Factor0 /= 10 ->		    ignore;		true ->		    %% Our serial may be much larger than other nodes ditto		    AllNodes = val({current, db_nodes}),		    verbose("Sync serial ~p~n", [Tid]),		    rpc:abcast(AllNodes, ?MODULE, {sync_trans_serial, Tid})	    end,	    intercept_friends(Tid, Ts),	    Store = Ts#tidstore.store,	    Nodes = get_elements(nodes,Store),	    ?MODULE ! {self(), {restart, Tid, Store}},	    mnesia_locker:send_release_tid(Nodes, Tid),	    timer:sleep(SleepTime),	    mnesia_locker:receive_release_tid_acc(Nodes, Tid),	    case get_restarted(Tid) of		{restarted, Tid} ->		    execute_transaction(Fun, Args, Factor0 + 1, 					Retries, Type);		{error, Reason} ->		    mnesia:abort(Reason)	    end    end.get_restarted(Tid) ->    case Res = rec() of	{restarted, Tid} ->	    Res;	{error,_} ->	    Res;	_ -> %% We could get a couple of aborts to many.	    get_restarted(Tid)    end.decr(infinity) -> infinity;decr(X) when integer(X), X > 1 -> X - 1;decr(_X) -> 0.return_abort(Fun, Args, Reason)  ->    {_Mod, Tid, Ts} = get(mnesia_activity_state),    dbg_out("Transaction ~p calling ~p with ~p failed: ~n ~p~n", 	    [Tid, Fun, Args, Reason]),    OldStore = Ts#tidstore.store,    Nodes = get_elements(nodes, OldStore),    intercept_friends(Tid, Ts),    catch mnesia_lib:incr_counter(trans_failures),    Level = Ts#tidstore.level,    if	Level == 1 ->	    mnesia_locker:async_release_tid(Nodes, Tid),	    ?MODULE ! {delete_transaction, Tid},	    erase(mnesia_activity_state),	    	    flush_downs(),	    catch unlink(whereis(?MODULE)),	    {aborted, mnesia_lib:fix_error(Reason)};	true ->	    %% Nested transaction	    [{OldMod,NewStore} | Tail] = Ts#tidstore.up_stores,	    req({del_store, Tid, NewStore, OldStore, true}),	    Ts2 = Ts#tidstore{store = NewStore,			      up_stores = Tail,			      level = Level - 1},	    NewTidTs = {OldMod, Tid, Ts2},	    put(mnesia_activity_state, NewTidTs),	    case Reason of 		#cyclic{} ->		    exit({aborted, Reason});		{node_not_running, _N} -> 		    exit({aborted, Reason});		{bad_commit, _N}-> 		    exit({aborted, Reason});		_ -> 		    {aborted, mnesia_lib:fix_error(Reason)}	    end    end.flush_downs() ->    receive	{?MODULE, _, _} -> flush_downs(); % Votes	{mnesia_down, _} -> flush_downs()    after 0 -> flushed    end.put_activity_id(MTT) ->    put_activity_id(MTT, undefined).put_activity_id(undefined,_) ->    erase_activity_id();put_activity_id({Mod, Tid, Ts},Fun)   when record(Tid, tid), record(Ts, tidstore) ->    flush_downs(),    Store = Ts#tidstore.store,    if 	is_function(Fun) ->	    ?ets_insert(Store, {friends, {stop,Fun}});	true ->	    ?ets_insert(Store, {friends, self()})    end,    NewTidTs = {Mod, Tid, Ts},    put(mnesia_activity_state, NewTidTs);put_activity_id(SimpleState,_) ->    put(mnesia_activity_state, SimpleState).erase_activity_id() ->    flush_downs(),    erase(mnesia_activity_state).get_elements(Type,Store) ->        case catch ?ets_lookup(Store, Type) of	[] -> [];	[{_,Val}] -> [Val];	{'EXIT', _} -> [];	Vals -> [Val|| {_,Val} <- Vals]    end.    opt_propagate_store(_Current, _Obsolete, false) ->    ok;opt_propagate_store(Current, Obsolete, true) ->    propagate_store(Current, nodes, get_elements(nodes,Obsolete)),    propagate_store(Current, fixtable, get_elements(fixtable,Obsolete)),    propagate_store(Current, friends, get_elements(friends, Obsolete)).propagate_store(Store, Var, [Val | Vals]) ->    ?ets_insert(Store, {Var, Val}),    propagate_store(Store, Var, Vals);propagate_store(_Store, _Var, []) ->    ok.%% Tell all processes that are cooperating with the current transactionintercept_friends(_Tid, Ts) ->    Friends = get_elements(friends,Ts#tidstore.store),    intercept_best_friend(Friends, false).intercept_best_friend([],_) ->    ok;intercept_best_friend([{stop,Fun} | R],Ignore) ->    catch Fun(),    intercept_best_friend(R,Ignore);intercept_best_friend([Pid | R],false) ->        Pid ! {activity_ended, undefined, self()},     wait_for_best_friend(Pid, 0),    intercept_best_friend(R,true);intercept_best_friend([_|R],true) ->    intercept_best_friend(R,true).wait_for_best_friend(Pid, Timeout) ->    receive	{'EXIT', Pid, _} -> ok;	{activity_ended, _, Pid} -> ok    after Timeout ->	    case my_process_is_alive(Pid) of		true -> wait_for_best_friend(Pid, 1000);		false -> ok	    end    end.    my_process_is_alive(Pid) ->    case catch erlang:is_process_alive(Pid) of % New BIF in R5	true -> 	    true;	false -> 	    false;	{'EXIT', _} -> % Pre R5 backward compatibility 	    case process_info(Pid, message_queue_len) of		undefined -> false;		_ -> true	    end     end.dirty(Protocol, Item) ->    {{Tab, Key}, _Val, _Op} = Item,    Tid = {dirty, self()},    Prep = prepare_items(Tid, Tab, Key, [Item], #prep{protocol= Protocol}),    CR =  Prep#prep.records,    case Protocol of	async_dirty ->	    %% Send commit records to the other involved nodes,	    %% but do only wait for one node to complete.	    %% Preferrably, the local node if possible. 		    	    ReadNode = val({Tab, where_to_read}),	    {WaitFor, FirstRes} = async_send_dirty(Tid, CR, Tab, ReadNode),	    rec_dirty(WaitFor, FirstRes);			sync_dirty ->	    %% Send commit records to the other involved nodes,	    %% and wait for all nodes to complete	    {WaitFor, FirstRes} = sync_send_dirty(Tid, CR, Tab, []),	    rec_dirty(WaitFor, FirstRes);	_ ->	    mnesia:abort({bad_activity, Protocol})    end.%% This is the commit function, The first thing it does,%% is to find out which nodes that have been participating%% in this particular transaction, all of the mnesia_locker:lock*%% functions insert the names of the nodes where it aquires locks%% into the local shadow Store%% This function exacutes in the context of the user processt_commit(Type) ->    {_Mod, Tid, Ts} = get(mnesia_activity_state),    Store = Ts#tidstore.store,    if	Ts#tidstore.level == 1 ->	    intercept_friends(Tid, Ts),	    %% N is number of updates 	    case arrange(Tid, Store, Type) of		{N, Prep} when N > 0 ->		    multi_commit(Prep#prep.protocol,				 Tid, Prep#prep.records, Store);		{0, Prep} ->		    multi_commit(read_only, Tid, Prep#prep.records, Store)	    end;	true ->	    %% nested commit	    Level = Ts#tidstore.level,	    [{OldMod,Obsolete} | Tail] = Ts#tidstore.up_stores,	    req({del_store, Tid, Store, Obsolete, false}),	    NewTs = Ts#tidstore{store = Store,				up_stores = Tail,				level = Level - 1},	    NewTidTs = {OldMod, Tid, NewTs},	    put(mnesia_activity_state, NewTidTs),	    do_commit_nested    end.%% This function arranges for all objects we shall write in S to be%% in a list of {Node, CommitRecord}%% Important function for the performance of mnesia.arrange(Tid, Store, Type) ->    %% The local node is always included    Nodes = get_elements(nodes,Store),    Recs = prep_recs(Nodes, []),    Key = ?ets_first(Store),    N = 0,    Prep = 	case Type of 	    async -> #prep{protocol = sym_trans, records = Recs};	    sync -> #prep{protocol = sync_sym_trans, records = Recs}	end,    case catch do_arrange(Tid, Store, Key, Prep, N) of	{'EXIT', Reason} ->	    dbg_out("do_arrange failed ~p ~p~n", [Reason, Tid]),	    case Reason of		{aborted, R} ->		    mnesia:abort(R);		_ -> 		    mnesia:abort(Reason)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?