global.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,642 行 · 第 1/5 页
ERL
1,642 行
%% ``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(global).-behaviour(gen_server).%% Global provides global registration of process names. The names are%% dynamically kept up to date with the entire network. Global can%% operate in two modes: in a fully connected network, or in a%% non-fully connected network. In the latter case, the name%% registration mechanism won't work. %% As a separate service Global also provides global locks.%% External exports-export([start/0, start_link/0, stop/0, sync/0, sync/1, safe_whereis_name/1, whereis_name/1, register_name/2, register_name/3, register_name_external/2, register_name_external/3, unregister_name_external/1,re_register_name/2, re_register_name/3, unregister_name/1, registered_names/0, send/2, node_disconnected/1, set_lock/1, set_lock/2, set_lock/3, del_lock/1, del_lock/2, trans/2, trans/3, trans/4, random_exit_name/3, random_notify_name/3, notify_all_name/3]).%% Internal exports-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, resolve_it/4]).-export([info/0]).-include_lib("stdlib/include/ms_transform.hrl").%% Set this variable to 'allow' to allow several names of a process.%% This is for backward compatibility only; the functionality is broken.-define(WARN_DUPLICATED_NAME, global_multi_name_action).%% Undocumented Kernel variable. Set this to 0 (zero) to get the old%% behaviour.-define(N_CONNECT_RETRIES, global_connect_retries).-define(DEFAULT_N_CONNECT_RETRIES, 5).%%% In certain places in the server, calling io:format hangs everything,%%% so we'd better use erlang:display/1.%%% my_tracer is used in testsuites-define(trace(_), ok).%-define(trace(T), (catch my_tracer ! {node(), {line,?LINE}, T})).%-define(trace(T), erlang:display({format, node(), cs(), T})).%cs() ->% {_Big, Small, Tiny} = now(),% (Small rem 100) * 100 + (Tiny div 10000).%% These are the protocol versions:%% Vsn 1 is the original protocol.%% Vsn 2 is enhanced with code to take care of registration of names from%% non erlang nodes, e.g. C-nodes.%% Vsn 3 is enhanced with a tag in the synch messages to distinguish%% different synch sessions from each other, see OTP-2766.%% Vsn 4 uses a single, permanent, locker process, but works like vsn 3%% when communicating with vsn 3 nodes. Current version of global does%% not support vsn 3 nodes.%% Vsn 5 uses an ordered list of self() and HisTheLocker when locking%% nodes in the own partition.-define(vsn, 5).%%-----------------------------------------------------------------%% connect_all = bool() - true if we are supposed to set up a%% fully connected net%% known = [Node] - all nodes known to us%% synced = [Node] - all nodes that have the same names as us%% resolvers = [{Node, MyTag, Resolver}] - %% the tag separating different synch sessions, %% and the pid of the name resolver process%% syncers = [pid()] - all current syncers processes%% node_name = atom() - our node name (can change if distribution%% is started/stopped dynamically)%%%% In addition to these, we keep info about messages arrived in%% the process dictionary:%% {pre_connect, Node} = {Vsn, InitMsg} - init_connect msgs that%% arrived before nodeup%% {wait_lock, Node} = {exchange, NameList, _NamelistExt} | lock_is_set%% - see comment below (handle_cast)%% {save_ops, Node} = {resolved, HisKnown, NamesExt, Res} | [operation()] %% - save the ops between exchange and resolved%% {prot_vsn, Node} = Vsn - the exchange protocol version (not used now)%% {sync_tag_my, Node} = My tag, used at synchronization with Node%% {sync_tag_his, Node} = The Node's tag, used at synchronization%% {lock_id, Node} = The resource locking the partitions%%------------------------------------------------------------------record(state, {connect_all, known = [], synced = [], resolvers = [], syncers = [], node_name = node(), the_locker, the_deleter, the_registrar, trace, global_lock_down = false }).%%% There are also ETS tables used for bookkeeping of locks and names%%% (the first position is the key):%%%%%% global_locks (set): {ResourceId, LockRequesterId, [{Pid,RPid,ref()]}%%% pid() is locking ResourceId, ref() is the monitor ref.%%% RPid =/= Pid if there is an extra process calling erlang:monitor().%%% global_names (set): {Name, Pid, Method, RPid, ref()}%%% Registered names. ref() is the monitor ref.%%% RPid =/= Pid if there is an extra process calling erlang:monitor().%%% global_names_ext (set): {Name, Pid, RegNode}%%% External registered names (C-nodes).%%% (The RPid:s can be removed when/if erlang:monitor() returns before %%% trying to connect to the other node.)%%% %%% Helper tables:%%% global_pid_names (bag): {Pid, Name} | {ref(), Name}%%% Name(s) registered for Pid.%%% There is one {Pid, Name} and one {ref(), Name} for every Pid.%%% ref() is the same ref() as in global_names.%%% global_pid_ids (bag): {Pid, ResourceId} | {ref(), ResourceId}%%% Resources locked by Pid.%%% ref() is the same ref() as in global_locks.%%%%%% global_pid_names is a 'bag' for backward compatibility.%%% (Before vsn 5 more than one name could be registered for a process.)%%%%%% R11B-3 (OTP-6341): The list of pids in the table 'global_locks'%%% was replaced by a list of {Pid, Ref}, where Ref is a monitor ref.%%% It was necessary to use monitors to fix bugs regarding locks that%%% were never removed. The signal {async_del_lock, ...} has been%%% kept for backward compatibility. It can be removed later.%%% %%% R11B-4 (OTP-6428): Monitors are used for registered names.%%% The signal {delete_name, ...} has been kept for backward compatibility.%%% It can be removed later as can the deleter process.%%% An extra process calling erlang:monitor() is sometimes created.%%% The new_nodes messages has been augmented with the global lock id.start() -> gen_server:start({local, global_name_server}, ?MODULE, [], []).start_link() -> gen_server:start_link({local, global_name_server}, ?MODULE, [], []).stop() -> gen_server:call(global_name_server, stop, infinity).sync() -> case check_sync_nodes() of {error, Error} -> {error, Error}; SyncNodes -> gen_server:call(global_name_server, {sync, SyncNodes}, infinity) end.sync(Nodes) -> case check_sync_nodes(Nodes) of {error, Error} -> {error, Error}; SyncNodes -> gen_server:call(global_name_server, {sync, SyncNodes}, infinity) end.send(Name, Msg) -> case whereis_name(Name) of Pid when is_pid(Pid) -> Pid ! Msg, Pid; undefined -> exit({badarg, {Name, Msg}}) end.%% See OTP-3737.whereis_name(Name) -> where(Name).safe_whereis_name(Name) -> gen_server:call(global_name_server, {whereis, Name}, infinity).node_disconnected(Node) -> global_name_server ! {nodedown, Node}.%%-----------------------------------------------------------------%% Method = function(Name, Pid1, Pid2) -> Pid | Pid2 | none%% Method is called if a name conflict is detected when two nodes%% are connecting to each other. It is supposed to return one of%% the Pids or 'none'. If a pid is returned, that pid is%% registered as Name on all nodes. If 'none' is returned, the%% Name is unregistered on all nodes. If anything else is returned,%% the Name is unregistered as well.%% Method is called once at one of the nodes where the processes reside%% only. If different Methods are used for the same name, it is%% undefined which one of them is used.%% Method blocks the name registration, but does not affect global locking.%%-----------------------------------------------------------------register_name(Name, Pid) when is_pid(Pid) -> register_name(Name, Pid, {?MODULE, random_exit_name}).register_name(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> case (where(Name) =:= undefined) andalso check_dupname(Name, Pid) of true -> gen_server:multi_call(Nodes, global_name_server, {register, Name, Pid, Method}), yes; _ -> no end end, ?trace({register_name, self(), Name, Pid, Method}), gen_server:call(global_name_server, {registrar, Fun}, infinity).check_dupname(Name, Pid) -> case ets:lookup(global_pid_names, Pid) of [] -> true; PidNames -> case application:get_env(kernel, ?WARN_DUPLICATED_NAME) of {ok, allow} -> true; _ -> S = "global: ~w registered under several names: ~w\n", Names = [Name | [Name1 || {_Pid, Name1} <- PidNames]], error_logger:error_msg(S, [Pid, Names]), false end end.unregister_name(Name) -> case where(Name) of undefined -> ok; _ -> Fun = fun(Nodes) -> gen_server:multi_call(Nodes, global_name_server, {unregister, Name}), ok end, ?trace({unregister_name, self(), Name}), gen_server:call(global_name_server, {registrar, Fun}, infinity) end.re_register_name(Name, Pid) when is_pid(Pid) -> re_register_name(Name, Pid, {?MODULE, random_exit_name}).re_register_name(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> gen_server:multi_call(Nodes, global_name_server, {register, Name, Pid, Method}), yes end, ?trace({re_register_name, self(), Name, Pid, Method}), gen_server:call(global_name_server, {registrar, Fun}, infinity).registered_names() -> MS = ets:fun2ms(fun({Name,_Pid,_M,_RP,_R}) -> Name end), ets:select(global_names, MS).%%-----------------------------------------------------------------%% The external node (e.g. a C-node) registers the name on an Erlang%% node which links to the process (an Erlang node has to be used%% since there is no global_name_server on the C-node). If the Erlang%% node dies the name is to be unregistered on all nodes. Normally%% node(Pid) is compared to the node that died, but that does not work%% for external nodes (the process does not run on the Erlang node%% that died). Therefore a table of all names registered by external%% nodes is kept up-to-date on all nodes.%%%% Note: if the Erlang node dies an EXIT signal is also sent to the%% C-node due to the link between the global_name_server and the%% registered process. [This is why the link has been kept despite%% the fact that monitors do the job now.]%%-----------------------------------------------------------------register_name_external(Name, Pid) when is_pid(Pid) -> register_name_external(Name, Pid, {?MODULE, random_exit_name}).register_name_external(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> case where(Name) of undefined -> gen_server:multi_call(Nodes, global_name_server, {register_ext, Name, Pid, Method, node()}), yes; _Pid -> no end end, ?trace({register_name_external, self(), Name, Pid, Method}), gen_server:call(global_name_server, {registrar, Fun}, infinity).unregister_name_external(Name) -> unregister_name(Name).set_lock(Id) -> set_lock(Id, [node() | nodes()], infinity, 1).set_lock(Id, Nodes) -> set_lock(Id, Nodes, infinity, 1).set_lock(Id, Nodes, Retries) when is_integer(Retries), Retries >= 0 -> set_lock(Id, Nodes, Retries, 1);set_lock(Id, Nodes, infinity) -> set_lock(Id, Nodes, infinity, 1).set_lock({_ResourceId, _LockRequesterId}, [], _Retries, _Times) -> true;set_lock({_ResourceId, _LockRequesterId} = Id, Nodes, Retries, Times) -> ?trace({set_lock,{me,self()},Id,{nodes,Nodes}, {retries,Retries}, {times,Times}}), case set_lock_on_nodes(Id, Nodes) of true -> ?trace({set_lock_true, Id}), true;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?