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 + -
显示快捷键?