global.erl

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

ERL
1,642
字号
        false=Reply when Retries =:= 0 ->            Reply;	false ->	    random_sleep(Times),	    set_lock(Id, Nodes, dec(Retries), Times+1)    end.del_lock(Id) ->    del_lock(Id, [node() | nodes()]).del_lock({ResourceId, LockRequesterId}, Nodes) ->    Id = {ResourceId, LockRequesterId},    ?trace({del_lock, {me,self()}, {ResourceId,LockRequesterId},             {nodes,Nodes}}),    gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),    true.trans(Id, Fun) -> trans(Id, Fun, [node() | nodes()], infinity).trans(Id, Fun, Nodes) -> trans(Id, Fun, Nodes, infinity).trans(Id, Fun, Nodes, Retries) ->    case set_lock(Id, Nodes, Retries) of	true ->            try                 Fun()            after                del_lock(Id, Nodes)            end;	false ->	    aborted    end.info() ->    gen_server:call(global_name_server, info, infinity).%%%-----------------------------------------------------------------%%% Call-back functions from gen_server%%%-----------------------------------------------------------------init([]) ->    process_flag(trap_exit, true),    _ = ets:new(global_locks, [set, named_table, protected]),    _ = ets:new(global_names, [set, named_table, protected]),    _ = ets:new(global_names_ext, [set, named_table, protected]),    _ = ets:new(global_pid_names, [bag, named_table, protected]),    _ = ets:new(global_pid_ids, [bag, named_table, protected]),    %% This is for troubleshooting only.    DoTrace = os:getenv("GLOBAL_HIGH_LEVEL_TRACE") =:= "TRUE",    T0 = case DoTrace of             true ->                  send_high_level_trace(),                 [];             false ->                  no_trace         end,    S = #state{the_locker = start_the_locker(DoTrace),               trace = T0,	       the_deleter = start_the_deleter(self()),               the_registrar = start_the_registrar()},    S1 = trace_message(S, {init, node()}, []),    case init:get_argument(connect_all) of	{ok, [["false"]]} ->	    {ok, S1#state{connect_all = false}};	_ ->	    {ok, S1#state{connect_all = true}}    end.%%-----------------------------------------------------------------%% Connection algorithm%% ====================%% This algorithm solves the problem with partitioned nets as well.%%%% The main idea in the algorithm is that when two nodes connect, they%% try to set a lock in their own partition (i.e. all nodes already%% known to them; partitions are not necessarily disjoint). When the%% lock is set in each partition, these two nodes send each other a%% list with all registered names in resp partition (*). If no conflict%% is found, the name tables are just updated. If a conflict is found,%% a resolve function is called once for each conflict. The result of%% the resolving is sent to the other node. When the names are%% exchanged, all other nodes in each partition are informed of the%% other nodes, and they ping each other to form a fully connected%% net.%%%% A few remarks:%% %% (*) When this information is being exchanged, no one is allowed to%%     change the global register table. All calls to register etc are%%     protected by a lock. If a registered process dies during this%%     phase the name is unregistered on the local node immediately,%%     but the unregistration on other nodes will take place when the%%     deleter manages to acquire the lock. This is necessary to%%     prevent names from spreading to nodes where they cannot be%%     deleted.%%%% - It is assumed that nodeups and nodedowns arrive in an orderly%%   fashion: for every node, nodeup is followed by nodedown, and vice%%   versa. "Double" nodeups and nodedowns must never occur. It is%%   the responsibility of net_kernel to assure this.%%%% - There is always a delay between the termination of a registered%%   process and the removal of the name from Global's tables. This%%   delay can sometimes be quite substantial. Global guarantees that%%   the name will eventually be removed, but there is no%%   synchronization between nodes; the name can be removed from some%%   node(s) long before it is removed from other nodes. Using%%   safe_whereis_name is no cure.%%%% - Global cannot handle problems with the distribution very well.%%   Depending on the value of the kernel variable 'net_ticktime' long%%   delays may occur. This does not affect the handling of locks but%%   will block name registration.%% %% - Old synch session messages may linger on in the message queue of%%   global_name_server after the sending node has died. The tags of%%   such messages do not match the current tag (if there is one),%%   which makes it possible to discard those messages and cancel the%%   corresponding lock.%%%% Suppose nodes A and B connect, and C is connected to A.%% Here's the algorithm's flow:%%%% Node A%% ------%% << {nodeup, B}%%   TheLocker ! {nodeup, ..., Node, ...} (there is one locker per node)%% B ! {init_connect, ..., {..., TheLockerAtA, ...}}%% << {init_connect, TheLockerAtB}%%   [The lockers try to set the lock]%% << {lock_is_set, B, ...}%%   [Now, lock is set in both partitions]%% B ! {exchange, A, Names, ...}%% << {exchange, B, Names, ...}%%   [solve conflict]%% B ! {resolved, A, ResolvedA, KnownAtA, ...}%% << {resolved, B, ResolvedB, KnownAtB, ...}%% C ! {new_nodes, ResolvedAandB, [B]}%%%% Node C%% ------%% << {new_nodes, ResolvedOps, NewNodes}%%   [insert Ops]%% ping(NewNodes)%% << {nodeup, B}%% <ignore this one>%%%% Several things can disturb this picture.%%%% First, the init_connect message may arrive _before_ the nodeup%% message due to delay in net_kernel. We handle this by keeping track%% of these messages in the pre_connect variable in our state.%%%% Of course we must handle that some node goes down during the%% connection.%%%%-----------------------------------------------------------------%% Messages in the protocol%% ========================%% 1. Between global_name_servers on connecting nodes%%    {init_connect, Vsn, Node, InitMsg}%%         InitMsg = {locker, _Unused, HisKnown, HisTheLocker}%%    {exchange, Node, ListOfNames, _ListOfNamesExt, Tag}%%    {resolved, Node, HisOps, HisKnown, _Unused, ListOfNamesExt, Tag}%%         HisKnown = list of known nodes in Node's partition%% 2. Between lockers on connecting nodes%%    {his_locker, Pid} (from our global)%%    {lock, Bool} loop until both lockers have lock = true,%%          then send to global_name_server {lock_is_set, Node, Tag}%% 3. Connecting node's global_name_server informs other nodes in the same %%    partition about hitherto unknown nodes in the other partition%%    {new_nodes, Node, Ops, ListOfNamesExt, NewNodes, ExtraInfo}%% 4. Between global_name_server and resolver%%    {resolve, NameList, Node} to resolver%%    {exchange_ops, Node, Tag, Ops, Resolved} from resolver%% 5. sync protocol, between global_name_servers in different partitions%%    {in_sync, Node, IsKnown}%%          sent by each node to all new nodes (Node becomes known to them)%%-----------------------------------------------------------------handle_call({whereis, Name}, From, S) ->    do_whereis(Name, From),    {noreply, S};handle_call({registrar, Fun}, From, S) ->    S#state.the_registrar ! {trans_all_known, Fun, From},    {noreply, S};%% The pattern {register,'_','_','_'} is traced by the inviso%% application. Do not change.handle_call({register, Name, Pid, Method}, {FromPid, _Tag}, S0) ->    S = ins_name(Name, Pid, Method, FromPid, [], S0),    {reply, yes, S};handle_call({unregister, Name}, _From, S0) ->    S = delete_global_name2(Name, S0),    {reply, ok, S};handle_call({register_ext, Name, Pid, Method, RegNode}, {FromPid,_Tag}, S0) ->    S = ins_name_ext(Name, Pid, Method, RegNode, FromPid, [], S0),    {reply, yes, S};handle_call({set_lock, Lock}, {Pid, _Tag}, S0) ->    {Reply, S} = handle_set_lock(Lock, Pid, S0),    {reply, Reply, S};handle_call({del_lock, Lock}, {Pid, _Tag}, S0) ->    S = handle_del_lock(Lock, Pid, S0),    {reply, true, S};handle_call(get_known, _From, S) ->    {reply, S#state.known, S};handle_call(get_synced, _From, S) ->    {reply, S#state.synced, S};handle_call({sync, Nodes}, From, S) ->    %% If we have several global groups, this won't work, since we will    %% do start_sync on a nonempty list of nodes even if the system    %% is quiet.    Pid = start_sync(lists:delete(node(), Nodes) -- S#state.synced, From),    {noreply, S#state{syncers = [Pid | S#state.syncers]}};handle_call(get_protocol_version, _From, S) ->    {reply, ?vsn, S};handle_call(get_names_ext, _From, S) ->    {reply, get_names_ext(), S};handle_call(info, _From, S) ->    {reply, S, S};%% "High level trace". For troubleshooting only.handle_call(high_level_trace_start, _From, S) ->    S#state.the_locker ! {do_trace, true},    send_high_level_trace(),    {reply, ok, trace_message(S#state{trace = []}, {init, node()}, [])};handle_call(high_level_trace_stop, _From, S) ->    #state{the_locker = TheLocker, trace = Trace} = S,    TheLocker ! {do_trace, false},    wait_high_level_trace(),    {reply, Trace, S#state{trace = no_trace}};handle_call(high_level_trace_get, _From, #state{trace = Trace}=S) ->    {reply, Trace, S#state{trace = []}};handle_call(stop, _From, S) ->    {stop, normal, stopped, S};handle_call(Request, From, S) ->    error_logger:warning_msg("The global_name_server "                             "received an unexpected message:\n"                             "handle_call(~p, ~p, _)\n",                              [Request, From]),    {noreply, S}.%%========================================================================%% init_connect%%%%========================================================================handle_cast({init_connect, Vsn, Node, InitMsg}, S) ->    %% Sent from global_name_server at Node.    ?trace({'####', init_connect, {vsn, Vsn}, {node,Node},{initmsg,InitMsg}}),    case Vsn of	%% It is always the responsibility of newer versions to understand	%% older versions of the protocol.	{HisVsn, HisTag} when HisVsn > ?vsn ->	    init_connect(?vsn, Node, InitMsg, HisTag, S#state.resolvers, S);	{HisVsn, HisTag} ->	    init_connect(HisVsn, Node, InitMsg, HisTag, S#state.resolvers, S);	%% To be future compatible	Tuple when is_tuple(Tuple) ->	    List = tuple_to_list(Tuple),	    [_HisVsn, HisTag | _] = List,	    %% use own version handling if his is newer.	    init_connect(?vsn, Node, InitMsg, HisTag, S#state.resolvers, S);	_ ->	    Txt = io_lib:format("Illegal global protocol version ~p Node: ~p\n",                                [Vsn, Node]),	    error_logger:info_report(lists:flatten(Txt))    end,    {noreply, S};%%=======================================================================%% lock_is_set%%%% Ok, the lock is now set on both partitions. Send our names to other node.%%=======================================================================handle_cast({lock_is_set, Node, MyTag, LockId}, S) ->    %% Sent from the_locker at node().    ?trace({'####', lock_is_set , {node,Node}}),    case get({sync_tag_my, Node}) of	MyTag ->            lock_is_set(Node, S#state.resolvers, LockId),            {noreply, S};	_ -> %% Illegal tag, delete the old sync session.	    NewS = cancel_locker(Node, S, MyTag),	    {noreply, NewS}    end;%%========================================================================%% exchange%%%% Here the names are checked to detect name clashes.%%========================================================================handle_cast({exchange, Node, NameList, _NameExtList, MyTag}, S) ->    %% Sent from global_name_server at Node.    case get({sync_tag_my, Node}) of	MyTag ->	    exchange(Node, NameList, S#state.resolvers),	    {noreply, S};	_ -> %% Illegal tag, delete the old sync session.	    NewS = cancel_locker(Node, S, MyTag),	    {noreply, NewS}    end;%% {exchange_ops, ...} is sent by the resolver process (which then%% dies). It could happen that {resolved, ...} has already arrived%% from the other node. In that case we can go ahead and run the%% resolve operations. Otherwise we have to save the operations and%% wait for {resolve, ...}. This is very much like {lock_is_set, ...}%% and {exchange, ...}.handle_cast({exchange_ops, Node, MyTag, Ops, Resolved}, S0) ->    %% Sent from the resolver for Node at node().    ?trace({exchange_ops, {node,Node}, {ops,Ops},{resolved,Resolved},            {mytag,MyTag}}),    S = trace_message(S0, {exit_resolver, Node}, [MyTag]),

⌨️ 快捷键说明

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