mnesia_locker.erl

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

ERL
1,176
字号
%% True if  WaitForTid > Tid -> % Important orderallowed_to_be_queued(WaitForTid, Tid) ->    case get(pid_sort_order) of	undefined -> WaitForTid > Tid;	r9b_plain -> 	    cmp_tid(true, WaitForTid, Tid) =:= 1;	standard  -> 	    cmp_tid(false, WaitForTid, Tid) =:= 1    end.	    	    %% Check queue for conflicting locks%% Assume that all queued locks belongs to other tid'scheck_queue(Tid, Tab, X, AlreadyQ) ->    TabLocks = ets:lookup(mnesia_lock_queue, {Tab,?ALL}),    Greatest = max(TabLocks),    case Greatest of	empty ->  X;	Tid ->    X; 	WaitForTid -> 	    case allowed_to_be_queued(WaitForTid,Tid) of		true ->		    {queue, WaitForTid};		false when AlreadyQ =:= {no, bad_luck} -> 		    {no, WaitForTid}	    end    end.sort_queue(QL) ->    case get(pid_sort_order) of	undefined -> 	    lists:reverse(lists:keysort(#queue.tid, QL));	r9b_plain -> 	    lists:sort(fun(#queue{tid=X},#queue{tid=Y}) -> 			       cmp_tid(true, X, Y) == 1		       end, QL);	standard  -> 	    lists:sort(fun(#queue{tid=X},#queue{tid=Y}) ->			       cmp_tid(false, X, Y) == 1		       end, QL)    end.max([]) ->                 empty;max([#queue{tid=Max}]) ->  Max;max(L) ->    [#queue{tid=Max}|_] = sort_queue(L),    Max.%% We can't queue the ixlock requests since it%% becomes to complivated for little me :-)%% If we encounter an object with a wlock we reject the%% entire lock request%% %% BUGBUG: this is actually a bug since we may starveset_read_lock_on_all_keys(Tid, From, Tab, [RealKey | Tail], Orig, Ack) ->    Oid = {Tab, RealKey},    case can_lock(Tid, read, Oid, {no, bad_luck}) of	yes ->	    {granted, Val} = grant_lock(Tid, read, read, Oid),	    case opt_lookup_in_client(Val, Oid, read) of  % Ought to be invoked		C when record(C, cyclic) ->               % in the client		    reply(From, {not_granted, C});		Val2 -> 		    Ack2 = lists:append(Val2, Ack),		    set_read_lock_on_all_keys(Tid, From, Tab, Tail, Orig, Ack2)	    end;	{no, Lucky} ->	    C = #cyclic{op = read, lock = read, oid = Oid, lucky = Lucky},	    reply(From, {not_granted, C});	{queue, Lucky} ->	    C = #cyclic{op = read, lock = read, oid = Oid, lucky = Lucky},	    reply(From, {not_granted, C})    end;set_read_lock_on_all_keys(_Tid, From, _Tab, [], Orig, Ack) ->    reply(From, {granted, Ack, Orig}).%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Release of locks%% Release remote non-pending nodesrelease_remote_non_pending(Node, Pending) ->    %% Clear the mnesia_sticky_locks table first, to avoid    %% unnecessary requests to the failing node    ?ets_match_delete(mnesia_sticky_locks, {'_' , Node}),    %% Then we have to release all locks held by processes    %% running at the failed node and also simply remove all    %% queue'd requests back to the failed node    AllTids = ?ets_match(mnesia_tid_locks, {'$1', '_', '_'}),    Tids = [T || [T] <- AllTids, Node == node(T#tid.pid), not lists:member(T, Pending)],    do_release_tids(Tids).do_release_tids([Tid | Tids]) ->    do_release_tid(Tid),    do_release_tids(Tids);do_release_tids([]) ->    ok.do_release_tid(Tid) ->    Locks = ?ets_lookup(mnesia_tid_locks, Tid),    ?dbg("Release ~p ~p ~n", [Tid, Locks]),    ?ets_delete(mnesia_tid_locks, Tid),    release_locks(Locks),    %% Removed queued locks which has had locks    UniqueLocks = keyunique(lists:sort(Locks),[]),    rearrange_queue(UniqueLocks).keyunique([{_Tid, Oid, _Op}|R], Acc = [{_, Oid, _}|_]) ->    keyunique(R, Acc);keyunique([H|R], Acc) ->    keyunique(R, [H|Acc]);keyunique([], Acc) ->    Acc.release_locks([Lock | Locks]) ->    release_lock(Lock),    release_locks(Locks);release_locks([]) ->    ok.release_lock({Tid, Oid, {queued, _}}) ->    ?ets_match_delete(mnesia_lock_queue, #queue{oid=Oid, tid = Tid, op = '_',						pid = '_', lucky = '_'});release_lock({Tid, Oid, Op}) ->    if	Op == write ->	    ?ets_delete(mnesia_held_locks, Oid);	Op == read ->	    ets:delete_object(mnesia_held_locks, {Oid, Op, Tid})    end.rearrange_queue([{_Tid, {Tab, Key}, _} | Locks]) ->    if	Key /= ?ALL->	    	    Queue =  		ets:lookup(mnesia_lock_queue, {Tab, ?ALL}) ++ 		ets:lookup(mnesia_lock_queue, {Tab, Key}),	    case Queue of 		[] -> 		    ok;		_ ->		    Sorted = sort_queue(Queue),		    try_waiters_obj(Sorted)	    end;		true -> 	    Pat = ?match_oid_lock_queue({Tab, '_'}),	    Queue = ?ets_match_object(mnesia_lock_queue, Pat),	    	    Sorted = sort_queue(Queue),	    try_waiters_tab(Sorted)    end,    ?dbg("RearrQ ~p~n", [Queue]),    rearrange_queue(Locks);rearrange_queue([]) ->    ok.try_waiters_obj([W | Waiters]) ->    case try_waiter(W) of	queued ->	    no;	_ -> 	    	    try_waiters_obj(Waiters)    end;try_waiters_obj([]) ->    ok.try_waiters_tab([W | Waiters]) ->    case W#queue.oid of	{_Tab, ?ALL} ->	    case try_waiter(W) of		queued ->		    no;		_ ->		    try_waiters_tab(Waiters)	    end;	Oid ->	    case try_waiter(W) of		queued -> 	    		    Rest = key_delete_all(Oid, #queue.oid, Waiters),		    try_waiters_tab(Rest);		_ ->		    		    try_waiters_tab(Waiters)	    end    end;try_waiters_tab([]) ->    ok.try_waiter({queue, Oid, Tid, read_write, ReplyTo, _}) ->    try_waiter(Oid, read_write, read, write, ReplyTo, Tid);try_waiter({queue, Oid, Tid, Op, ReplyTo, _}) ->    try_waiter(Oid, Op, Op, Op, ReplyTo, Tid).try_waiter(Oid, Op, SimpleOp, Lock, ReplyTo, Tid) ->    case can_lock(Tid, Lock, Oid, {queue, bad_luck}) of	yes ->	    %% Delete from queue: Nice place for trace output	    ?ets_match_delete(mnesia_lock_queue, 			      #queue{oid=Oid, tid = Tid, op = Op,				     pid = ReplyTo, lucky = '_'}),	    Reply = grant_lock(Tid, SimpleOp, Lock, Oid),	    	    ReplyTo ! {?MODULE, node(), Reply},	    locked;	{queue, _Why} ->	    ?dbg("Keep ~p ~p ~p ~p~n", [Tid, Oid, Lock, _Why]),	    queued; % Keep waiter in queue		{no, Lucky} ->	    C = #cyclic{op = SimpleOp, lock = Lock, oid = Oid, lucky = Lucky},	    verbose("** WARNING ** Restarted transaction, possible deadlock in lock queue ~w: cyclic = ~w~n",		    [Tid, C]),	    ?ets_match_delete(mnesia_lock_queue, 			      #queue{oid=Oid, tid = Tid, op = Op,				     pid = ReplyTo, lucky = '_'}),	    Reply = {not_granted, C},	    ReplyTo ! {?MODULE, node(), Reply},	    removed    end.key_delete_all(Key, Pos, TupleList) ->    key_delete_all(Key, Pos, TupleList, []).key_delete_all(Key, Pos, [H|T], Ack) when element(Pos, H) == Key ->    key_delete_all(Key, Pos, T, Ack);key_delete_all(Key, Pos, [H|T], Ack) ->    key_delete_all(Key, Pos, T, [H|Ack]);key_delete_all(_, _, [], Ack) ->    lists:reverse(Ack).%% ********************* end server code ********************%% The following code executes at the client side of a transactionsmnesia_down(N, Pending) ->    case whereis(?MODULE) of	undefined ->	    %% Takes care of mnesia_down's in early startup	    mnesia_monitor:mnesia_down(?MODULE, N);	Pid ->	    %% Syncronously call needed in order to avoid	    %% race with mnesia_tm's coordinator processes	    %% that may restart and acquire new locks.	    %% mnesia_monitor ensures the sync.	    Pid ! {release_remote_non_pending, N, Pending}    end.%% Aquire a write lock, but do a read, used by %% mnesia:wread/1rwlock(Tid, Store, Oid) ->    {Tab, Key} = Oid,    case val({Tab, where_to_read}) of	nowhere ->	    mnesia:abort({no_exists, Tab});	Node ->	    Lock = write,	    case need_lock(Store, Tab, Key, Lock)  of		yes ->		    Ns = w_nodes(Tab),		    Res = get_rwlocks_on_nodes(Ns, rwlock, Node, Store, Tid, Oid),		    ?ets_insert(Store, {{locks, Tab, Key}, Lock}),		    Res;		no ->		    if			Key == ?ALL ->			    w_nodes(Tab);			Tab == ?GLOBAL ->			    w_nodes(Tab);			true ->			    dirty_rpc(Node, Tab, Key, Lock)		    end	    end    end.%% Return a list of nodes or abort transaction%% WE also insert any additional where_to_write nodes%% in the local store under the key == nodesw_nodes(Tab) ->    Nodes = ?catch_val({Tab, where_to_write}),    case Nodes of	[_ | _] -> Nodes;	_ ->  mnesia:abort({no_exists, Tab})    end.%% aquire a sticky wlock, a sticky lock is a lock%% which remains at this node after the termination of the%% transaction.sticky_wlock(Tid, Store, Oid) ->    sticky_lock(Tid, Store, Oid, write).sticky_rwlock(Tid, Store, Oid) ->    sticky_lock(Tid, Store, Oid, read_write).sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) ->    N = val({Tab, where_to_read}),     if	node() == N ->	    case need_lock(Store, Tab, Key, write) of	    	yes ->		    do_sticky_lock(Tid, Store, Oid, Lock);		no ->		    dirty_sticky_lock(Tab, Key, [N], Lock)	    end;	true ->	    mnesia:abort({not_local, Tab})    end.do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) ->    ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}},    N = node(),    receive	{?MODULE, N, granted} ->	    ?ets_insert(Store, {{locks, Tab, Key}, write}),	    [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)],	    granted;	{?MODULE, N, {granted, Val}} -> %% for rwlocks	    case opt_lookup_in_client(Val, Oid, write) of		C when record(C, cyclic) ->		    exit({aborted, C});		Val2 ->		    ?ets_insert(Store, {{locks, Tab, Key}, write}),		    [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)],		    Val2	    end;	{?MODULE, N, {not_granted, Reason}} ->	    exit({aborted, Reason});	{?MODULE, N, not_stuck} ->	    not_stuck(Tid, Store, Tab, Key, Oid, Lock, N),	    dirty_sticky_lock(Tab, Key, [N], Lock);	{mnesia_down, Node} ->	    EMsg = {aborted, {node_not_running, Node}},	    flush_remaining([N], Node, EMsg);	{?MODULE, N, {stuck_elsewhere, _N2}} ->	    stuck_elsewhere(Tid, Store, Tab, Key, Oid, Lock),	    dirty_sticky_lock(Tab, Key, [N], Lock)    end.not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) ->    rlock(Tid, Store, {Tab, ?ALL}),   %% needed?    wlock(Tid, Store, Oid),           %% perfect sync    wlock(Tid, Store, {Tab, ?STICK}), %% max one sticker/table    Ns = val({Tab, where_to_write}),    rpc:abcast(Ns, ?MODULE, {stick, Oid, N}).stuck_elsewhere(Tid, Store, Tab, _Key, Oid, _Lock) ->    rlock(Tid, Store, {Tab, ?ALL}),   %% needed?    wlock(Tid, Store, Oid),           %% perfect sync    wlock(Tid, Store, {Tab, ?STICK}), %% max one sticker/table    Ns = val({Tab, where_to_write}),    rpc:abcast(Ns, ?MODULE, {unstick, Tab}).dirty_sticky_lock(Tab, Key, Nodes, Lock) ->    if	Lock == read_write ->	    mnesia_lib:db_get(Tab, Key);	Key == ?ALL ->	    Nodes;	Tab == ?GLOBAL ->	    Nodes;	true ->	    ok    end.sticky_wlock_table(Tid, Store, Tab) ->    sticky_lock(Tid, Store, {Tab, ?ALL}, write).%% aquire a wlock on Oid%% We store a {Tabname, write, Tid} in all locktables%% on all nodes containing a copy of Tabname%% We also store an item {{locks, Tab, Key}, write} in the %% local store when we have aquired the lock.%% wlock(Tid, Store, Oid) ->    {Tab, Key} = Oid,    case need_lock(Store, Tab, Key, write) of	yes ->	    Ns = w_nodes(Tab),	    Op = {self(), {write, Tid, Oid}},	    ?ets_insert(Store, {{locks, Tab, Key}, write}),	    get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid);	no when Key /= ?ALL, Tab /= ?GLOBAL ->	    [];	no ->	    w_nodes(Tab)    end.wlock_table(Tid, Store, Tab) ->    wlock(Tid, Store, {Tab, ?ALL}).%% Write lock even if the table does not existwlock_no_exist(Tid, Store, Tab, Ns) ->

⌨️ 快捷键说明

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