gen.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 356 行

ERL
356
字号
%% ``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(gen).%%%-----------------------------------------------------------------%%% This module implements the really generic stuff of the generic%%% standard behaviours (e.g. gen_server, gen_fsm).%%%%%% The standard behaviour should export init_it/6.%%%------------------------------------------------------------------export([start/5, start/6, debug_options/1,	 call/3, call/4, reply/2]).-export([init_it/6, init_it/7]).-define(default_timeout, 5000).%%-----------------------------------------------------------------%% Starts a generic process.%% start(GenMod, LinkP, Mod, Args, Options)%% start(GenMod, LinkP, Name, Mod, Args, Options)%% start_link(Mod, Args, Options)%% start_link(Name, Mod, Args, Options) where:%%    Name = {local, atom()} | {global, atom()}%%    Mod  = atom(), callback module implementing the 'real' fsm%%    Args = term(), init arguments (to Mod:init/1)%%    Options = [{debug, [Flag]}]%%      Flag = trace | log | {logfile, File} | statistics | debug%%          (debug == log && statistics)%% Returns: {ok, Pid} |%%          {error, {already_started, Pid}} |%%          {error, Reason}%%-----------------------------------------------------------------start(GenMod, LinkP, Name, Mod, Args, Options) ->    case where(Name) of	undefined ->	    do_spawn(GenMod, LinkP, Name, Mod, Args, Options);	Pid ->	    {error, {already_started, Pid}}    end.start(GenMod, LinkP, Mod, Args, Options) ->    do_spawn(GenMod, LinkP, Mod, Args, Options).%%-----------------------------------------------------------------%% Spawn the process (and link) maybe at another node.%% If spawn without link, set parent to our selves "self"!!!%%-----------------------------------------------------------------do_spawn(GenMod, link, Mod, Args, Options) ->    Time = timeout(Options),    proc_lib:start_link(gen, init_it,			[GenMod, self(), self(), Mod, Args, Options], 			Time,			spawn_opts(Options));do_spawn(GenMod, _, Mod, Args, Options) ->    Time = timeout(Options),    proc_lib:start(gen, init_it,		   [GenMod, self(), self, Mod, Args, Options], 		   Time,		   spawn_opts(Options)).do_spawn(GenMod, link, Name, Mod, Args, Options) ->    Time = timeout(Options),    proc_lib:start_link(gen, init_it,			[GenMod, self(), self(), Name, Mod, Args, Options],			Time,			spawn_opts(Options));do_spawn(GenMod, _, Name, Mod, Args, Options) ->    Time = timeout(Options),    proc_lib:start(gen, init_it,		   [GenMod, self(), self, Name, Mod, Args, Options], 		   Time,		   spawn_opts(Options)).%%-----------------------------------------------------------------%% Initiate the new process.%% Register the name using the Rfunc function%% Calls the Mod:init/Args function.%% Finally an acknowledge is sent to Parent and the main%% loop is entered.%%-----------------------------------------------------------------init_it(GenMod, Starter, Parent, Mod, Args, Options) ->    init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options).init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) ->    case name_register(Name) of	true ->	    init_it2(GenMod, Starter, Parent, name(Name), Mod, Args, Options);	{false, Pid} ->	    proc_lib:init_ack(Starter, {error, {already_started, Pid}})    end.init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->    GenMod:init_it(Starter, Parent, Name, Mod, Args, Options).%%-----------------------------------------------------------------%% Makes a synchronous call to a generic process.%% Request is sent to the Pid, and the response must be%% {Tag, _, Reply}.%%-----------------------------------------------------------------%%% New call function which uses the new monitor BIF%%% call(ServerId, Label, Request)call(Process, Label, Request) ->     call(Process, Label, Request, ?default_timeout).%% Local or remote by pidcall(Pid, Label, Request, Timeout)   when is_pid(Pid), Timeout =:= infinity;       is_pid(Pid), is_integer(Timeout), Timeout >= 0 ->    do_call(Pid, Label, Request, Timeout);%% Local by namecall(Name, Label, Request, Timeout)   when is_atom(Name), Timeout =:= infinity;       is_atom(Name), is_integer(Timeout), Timeout >= 0 ->    case whereis(Name) of	Pid when is_pid(Pid) ->	    do_call(Pid, Label, Request, Timeout);	undefined ->	    exit(noproc)    end;%% Global by namecall({global, _Name}=Process, Label, Request, Timeout)  when Timeout =:= infinity;       is_integer(Timeout), Timeout >= 0 ->    case where(Process) of	Pid when is_pid(Pid) ->	    Node = node(Pid),	    case catch do_call(Pid, Label, Request, Timeout) of		{'EXIT', {nodedown, Node}} ->		    % A nodedown not yet detected by global, pretend that it		    % was.		    exit(noproc);		{'EXIT', noproc} ->		    exit(noproc);		{'EXIT', OtherExits} ->		    exit(OtherExits);		Result ->		    Result	    end;	undefined ->	    exit(noproc)    end;%% Local by name in disguisecall({Name, Node}, Label, Request, Timeout)  when Node =:= node(), Timeout =:= infinity;       Node =:= node(), is_integer(Timeout), Timeout >= 0 ->    call(Name, Label, Request, Timeout);%% Remote by namecall({_Name, Node}=Process, Label, Request, Timeout)  when is_atom(Node), Timeout =:= infinity;       is_atom(Node), is_integer(Timeout), Timeout >= 0 ->    if 	node() =:= nonode@nohost -> 	    exit({nodedown, Node}); 	true -> 	    do_call(Process, Label, Request, Timeout)    end.do_call(Process, Label, Request, Timeout) ->    %% We trust the arguments to be correct, i.e    %% Process is either a local or remote pid,    %% or a {Name, Node} tuple (of atoms) and in this     %% case this node (node()) _is_ distributed and Node =/= node().    Node = case Process of	       {_S, N} ->		   N;	       _ when is_pid(Process) ->		   node(Process);	       _ ->		   node()	   end,    case catch erlang:monitor(process, Process) of	Mref when is_reference(Mref) ->	    receive		{'DOWN', Mref, _, Pid1, noconnection} when is_pid(Pid1) ->		    exit({nodedown, node(Pid1)});		{'DOWN', Mref, _, _, noconnection} ->		    exit({nodedown, Node});		{'DOWN', Mref, _, _, _} ->		    exit(noproc)	    after 0 ->		    Process ! {Label, {self(), Mref}, Request},		    wait_resp_mon(Process, Mref, Timeout)	    end;	{'EXIT', _} ->	    %% Old node is not supporting the monitor.	    %% The other possible case -- this node is not distributed	    %% -- should have been handled earlier.	    %% Do the best possible with monitor_node/2.	    %% This code may hang indefinitely if the Process 	    %% does not exist. It is only used for old remote nodes.	    monitor_node(Node, true),	    receive		{nodedown, Node} -> 		    monitor_node(Node, false),		    exit({nodedown, Node})	    after 0 -> 		    Mref = make_ref(),		    Process ! {Label, {self(),Mref}, Request},		    Res = wait_resp(Node, Mref, Timeout),		    monitor_node(Node, false),		    Res	    end    end.wait_resp_mon(Process, Mref, Timeout) ->    Node = case Process of	       {_S, N} ->		   N;	       _ when is_pid(Process) ->		   node(Process);	       _ ->		   node()	   end,    receive	{Mref, Reply} ->	    erlang:demonitor(Mref),	    receive 		{'DOWN', Mref, _, _, _} -> 		    {ok, Reply}	    after 0 -> 		    {ok, Reply}	    end;	{'DOWN', Mref, _, Pid, Reason} when is_pid(Pid) ->	    receive		{'EXIT', Pid, noconnection} -> 		    exit({nodedown, Node});		{'EXIT', Pid, What} -> 		    exit(What)	    after 1 -> % Give 'EXIT' message time to arrive		    case Reason of			noconnection ->			    exit({nodedown, Node});			_ ->			    exit(Reason)		    end	    end;	{'DOWN', Mref, _, _, noconnection} ->	    %% Here is a hole, when the monitor is remote by name	    %% and the remote node goes down, we will never find 	    %% out the Pid and cannot know which 'EXIT' message	    %% to read out. This awkward case should have been 	    %% handled earlier (except for against rex) 	    %% by not using remote monitor by name.	    case Process of		_ when is_pid(Process) ->		    receive			{'EXIT', Process, noconnection} ->			    exit({nodedown, Node});			{'EXIT', Process, What} ->			    exit(What)		    after 1 -> % Give 'EXIT' message time to arrive			    exit({nodedown, node(Process)})		    end;		_ ->		    exit({nodedown, Node})	    end;	%% {'DOWN', Mref, _, _, noproc} ->	%%     exit(noproc);	{'DOWN', Mref, _Tag, _Item, Reason} ->	    exit(Reason)    after Timeout ->	    erlang:demonitor(Mref),	    receive		{'DOWN', Mref, _, _, _Reason} -> true 	    after 0 -> true	    end,	    exit(timeout)    end.wait_resp(Node, Tag, Timeout) ->    receive	{Tag, Reply} ->	    {ok,Reply};	{nodedown, Node} ->	    monitor_node(Node, false),	    exit({nodedown, Node})    after Timeout ->	    monitor_node(Node, false),	    exit(timeout)    end.%% Send a reply to the client.%reply({To, Tag}, Reply) ->    catch To ! {Tag, Reply}.%%%-----------------------------------------------------------------%%%  Misc. functions.%%%-----------------------------------------------------------------where({global, Name})    -> global:safe_whereis_name(Name);where({local, Name})  -> whereis(Name).name({global, Name})    -> Name;name({local, Name})     -> Name.name_register({local, Name}) ->    case catch register(Name, self()) of	true -> true;	{'EXIT', _} ->	    {false, where({local, Name})}    end;name_register({global, Name}) ->    case global:register_name(Name, self()) of	yes -> true;	no -> {false, where({global, Name})}    end.timeout(Options) ->    case opt(timeout, Options) of	{ok, Time} ->	    Time;	_ ->	    infinity    end.spawn_opts(Options) ->    case opt(spawn_opt, Options) of	{ok, Opts} ->	    Opts;	_ ->	    []    end.opt(Op, [{Op, Value}|_]) ->    {ok, Value};opt(Op, [_|Options]) ->    opt(Op, Options);opt(_, []) ->    false.debug_options(Opts) ->    case opt(debug, Opts) of	{ok, Options} -> sys:debug_options(Options);	_ -> []    end.

⌨️ 快捷键说明

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