mnesia_tm.erl

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

ERL
1,898
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(mnesia_tm).-export([	 start/0,	 init/1,	 non_transaction/5,	 transaction/6,	 commit_participant/5,	 dirty/2,	 display_info/2,	 do_update_op/3,	 get_info/1,	 get_transactions/0,	 info/1,	 mnesia_down/1,	 prepare_checkpoint/2,	 prepare_checkpoint/1, % Internal	 prepare_snmp/3,	 do_snmp/2,	 	 put_activity_id/1,	 put_activity_id/2,	 block_tab/1,	 unblock_tab/1,	 fixtable/3	]).%% sys callback functions-export([system_continue/3,	 system_terminate/4,	 system_code_change/4	]).-include("mnesia.hrl").-import(mnesia_lib, [set/2]).-import(mnesia_lib, [fatal/2, verbose/2, dbg_out/2]).-record(state, {coordinators = gb_trees:empty(), participants = gb_trees:empty(), supervisor,		blocked_tabs = [], dirty_queue = [], fixed_tabs = []}).%% Format on coordinators is [{Tid, EtsTabList} .....-record(prep, {protocol = sym_trans,	       %% async_dirty | sync_dirty | sym_trans | sync_sym_trans | asym_trans	       records = [],	       prev_tab = [], % initiate to a non valid table name	       prev_types,	       prev_snmp,	       types 	      }).-record(participant, {tid, pid, commit, disc_nodes = [], 		      ram_nodes = [], protocol = sym_trans}).start() ->    mnesia_monitor:start_proc(?MODULE, ?MODULE, init, [self()]).init(Parent) ->    register(?MODULE, self()),    process_flag(trap_exit, true),        %% Initialize the schema    IgnoreFallback = mnesia_monitor:get_env(ignore_fallback_at_startup),    mnesia_bup:tm_fallback_start(IgnoreFallback),    mnesia_schema:init(IgnoreFallback),        %% Handshake and initialize transaction recovery    mnesia_recover:init(),    Early = mnesia_monitor:init(),    AllOthers = mnesia_lib:uniq(Early ++ mnesia_lib:all_nodes()) -- [node()],    set(original_nodes, AllOthers),    mnesia_recover:connect_nodes(AllOthers),    %% Recover transactions, may wait for decision    case mnesia_monitor:use_dir() of	true ->	    P = mnesia_dumper:opt_dump_log(startup), % previous log	    L = mnesia_dumper:opt_dump_log(startup), % latest log	    Msg = "Initial dump of log during startup: ~p~n",	    mnesia_lib:verbose(Msg, [[P, L]]),	    mnesia_log:init();	false ->	    ignore    end,        mnesia_schema:purge_tmp_files(),    mnesia_recover:start_garb(),        ?eval_debug_fun({?MODULE, init},  [{nodes, AllOthers}]),		       case val(debug) of	Debug when Debug /= debug, Debug /= trace ->	    ignore;	_ ->	    mnesia_subscr:subscribe(whereis(mnesia_event), {table, schema})    end,    proc_lib:init_ack(Parent, {ok, self()}),    doit_loop(#state{supervisor = Parent}).val(Var) ->    case ?catch_val(Var) of	{'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); 	_VaLuE_ -> _VaLuE_     end.reply({From,Ref}, R) ->    From ! {?MODULE, Ref, R};reply(From, R) ->    From ! {?MODULE, node(), R}.reply(From, R, State) ->    reply(From, R),    doit_loop(State).req(R) ->    case whereis(?MODULE) of	undefined ->	    {error, {node_not_running, node()}};	Pid ->	    Ref = make_ref(), 	    Pid ! {{self(), Ref}, R},	    rec(Pid, Ref)    end.rec() ->    rec(whereis(?MODULE)).rec(Pid) when pid(Pid) ->    receive	{?MODULE, _, Reply} ->	    Reply;	{'EXIT', Pid, _} ->	    {error, {node_not_running, node()}}    end;rec(undefined) ->    {error, {node_not_running, node()}}.rec(Pid, Ref) ->    receive	{?MODULE, Ref, Reply} ->	    Reply;	{'EXIT', Pid, _} ->	    {error, {node_not_running, node()}}    end.   tmlink({From, Ref}) when reference(Ref) ->    link(From);tmlink(From) ->    link(From).tmpid({Pid, _Ref}) when pid(Pid) ->    Pid;tmpid(Pid) ->    Pid.%% Returns a list of participant transaction Tid'smnesia_down(Node) ->    %% Syncronously call needed in order to avoid    %% race with mnesia_tm's coordinator processes    %% that may restart and acquire new locks.    %% mnesia_monitor takes care of the sync    case whereis(?MODULE) of	undefined ->	    mnesia_monitor:mnesia_down(?MODULE, {Node, []});	Pid ->	    Pid ! {mnesia_down, Node}    end.prepare_checkpoint(Nodes, Cp) ->    rpc:multicall(Nodes, ?MODULE, prepare_checkpoint, [Cp]).prepare_checkpoint(Cp) ->    req({prepare_checkpoint,Cp}).block_tab(Tab) ->    req({block_tab, Tab}).unblock_tab(Tab) ->    req({unblock_tab, Tab}).doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=Sup}=State) ->    receive	{_From, {async_dirty, Tid, Commit, Tab}} ->	    case lists:member(Tab, State#state.blocked_tabs) of		false ->		    do_async_dirty(Tid, Commit, Tab),		    doit_loop(State);		true ->		    Item = {async_dirty, Tid, Commit, Tab},		    State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},		    doit_loop(State2)	    end;		{From, {sync_dirty, Tid, Commit, Tab}} ->	    case lists:member(Tab, State#state.blocked_tabs) of		false ->		    do_sync_dirty(From, Tid, Commit, Tab),		    doit_loop(State);		true ->		    Item = {sync_dirty, From, Tid, Commit, Tab},		    State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},		    doit_loop(State2)	    end;		{From, start_outer} -> %% Create and associate ets_tab with Tid	    case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of		{'EXIT', Reason} -> %% system limit		    Msg = "Cannot create an ets table for the "	                  "local transaction store",		    reply(From, {error, {system_limit, Msg, Reason}}, State);		Etab ->		    tmlink(From),		    C = mnesia_recover:incr_trans_tid_serial(),		    ?ets_insert(Etab, {nodes, node()}),		    Tid = #tid{pid = tmpid(From), counter = C},		    A2 = gb_trees:insert(Tid,[Etab],Coordinators),		    S2 = State#state{coordinators = A2},		    reply(From, {new_tid, Tid, Etab}, S2)	    end;		{From, {ask_commit, Protocol, Tid, Commit, DiscNs, RamNs}} ->	    ?eval_debug_fun({?MODULE, doit_ask_commit}, 			    [{tid, Tid}, {prot, Protocol}]),	    mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),	    Pid = 		case Protocol of		    asym_trans when node(Tid#tid.pid) /= node() ->			Args = [tmpid(From), Tid, Commit, DiscNs, RamNs],			spawn_link(?MODULE, commit_participant, Args);				    _ when node(Tid#tid.pid) /= node() -> %% *_sym_trans			reply(From, {vote_yes, Tid}),			nopid		end,	    P = #participant{tid = Tid,			     pid = Pid,			     commit = Commit,			     disc_nodes = DiscNs,			     ram_nodes = RamNs,			     protocol = Protocol},	    State2 = State#state{participants = gb_trees:insert(Tid,P,Participants)},	    doit_loop(State2);		{Tid, do_commit} ->	    case gb_trees:lookup(Tid, Participants) of		none ->		    verbose("Tried to commit a non participant transaction ~p~n",[Tid]),		    doit_loop(State);		{value, P} ->		    ?eval_debug_fun({?MODULE,do_commit,pre},[{tid,Tid},{participant,P}]),		    case P#participant.pid of			nopid ->			    Commit = P#participant.commit,			    Member = lists:member(node(), P#participant.disc_nodes),			    if Member == false ->				    ignore;			       P#participant.protocol == sym_trans -> 				    mnesia_log:log(Commit);			       P#participant.protocol == sync_sym_trans -> 				    mnesia_log:slog(Commit)			    end,			    mnesia_recover:note_decision(Tid, committed),			    do_commit(Tid, Commit),			    if 				P#participant.protocol == sync_sym_trans ->				    Tid#tid.pid ! {?MODULE, node(), {committed, Tid}};				true ->				    ignore			    end,			    mnesia_locker:release_tid(Tid),			    transaction_terminated(Tid),			    ?eval_debug_fun({?MODULE,do_commit,post},[{tid,Tid},{pid,nopid}]),			    doit_loop(State#state{participants=						  gb_trees:delete(Tid,Participants)});			Pid when pid(Pid) ->			    Pid ! {Tid, committed},			    ?eval_debug_fun({?MODULE, do_commit, post}, [{tid, Tid}, {pid, Pid}]),			    doit_loop(State)		    end	    end;		{Tid, simple_commit} ->	    mnesia_recover:note_decision(Tid, committed),	    mnesia_locker:release_tid(Tid),	    transaction_terminated(Tid),	    doit_loop(State);		{Tid, {do_abort, Reason}} ->	    ?eval_debug_fun({?MODULE, do_abort, pre}, [{tid, Tid}]),	    case gb_trees:lookup(Tid, Participants) of		none ->		    verbose("Tried to abort a non participant transaction ~p: ~p~n",			    [Tid, Reason]),		    mnesia_locker:release_tid(Tid),		    doit_loop(State);		{value, P} ->		    case P#participant.pid of			nopid ->			    Commit = P#participant.commit,			    mnesia_recover:note_decision(Tid, aborted),			    do_abort(Tid, Commit),			    if 				P#participant.protocol == sync_sym_trans ->				    Tid#tid.pid ! {?MODULE, node(), {aborted, Tid}};				true ->				    ignore			    end,			    transaction_terminated(Tid),			    mnesia_locker:release_tid(Tid),			    ?eval_debug_fun({?MODULE, do_abort, post}, [{tid, Tid}, {pid, nopid}]),			    doit_loop(State#state{participants=						  gb_trees:delete(Tid,Participants)});			Pid when pid(Pid) ->			    Pid ! {Tid, {do_abort, Reason}},			    ?eval_debug_fun({?MODULE, do_abort, post},					    [{tid, Tid}, {pid, Pid}]),			    doit_loop(State)		    end	    end;		{From, {add_store, Tid}} -> %% new store for nested  transaction	    case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of		{'EXIT', Reason} -> %% system limit		    Msg = "Cannot create an ets table for a nested "                          "local transaction store",		    reply(From, {error, {system_limit, Msg, Reason}}, State);		Etab ->		    A2 = add_coord_store(Coordinators, Tid, Etab),		    reply(From, {new_store, Etab},			  State#state{coordinators = A2})	    end;	{From, {del_store, Tid, Current, Obsolete, PropagateStore}} ->	    opt_propagate_store(Current, Obsolete, PropagateStore),	    A2 = del_coord_store(Coordinators, Tid, Current, Obsolete),	    reply(From, store_erased, State#state{coordinators = A2});	{'EXIT', Pid, Reason} ->	    handle_exit(Pid, Reason, State);		{From, {restart, Tid, Store}} ->	    A2 = restore_stores(Coordinators, Tid, Store),	    clear_fixtable([Store]),	    ?ets_match_delete(Store, '_'),	    ?ets_insert(Store, {nodes, node()}),	    reply(From, {restarted, Tid}, State#state{coordinators = A2});		{delete_transaction, Tid} ->	    %% used to clear transactions which are committed	    %% in coordinator or participant processes	    case gb_trees:is_defined(Tid, Participants) of		false ->		    case gb_trees:lookup(Tid, Coordinators) of			none ->			    verbose("** ERROR ** Tried to delete a non transaction ~p~n",				    [Tid]),			    doit_loop(State);			{value, Etabs} ->			    clear_fixtable(Etabs),			    erase_ets_tabs(Etabs),			    transaction_terminated(Tid),			    doit_loop(State#state{coordinators = 						  gb_trees:delete(Tid,Coordinators)})		    end;		true ->		    transaction_terminated(Tid),

⌨️ 快捷键说明

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