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