各种有用的erlang行为
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

129 rivejä
4.1 KiB

-module(gen_call).
-export([gcall/3, gcall/4, greply/2]).
-define(default_timeout, 5000).
%% 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().
-define(get_node(Process), case Process of {_S, N} -> N; _ -> node(Process) end).
gcall(Process, Label, Request) ->
gcall(Process, Label, Request, ?default_timeout).
gcall(Process, Label, Request, Timeout) ->
%%-----------------------------------------------------------------
%% Map different specifications of a process to either Pid or
%% {Name,Node}. Execute the given Fun with the process as only
%% argument.
%% -----------------------------------------------------------------
case where(Process) of
undefined ->
exit(noproc);
PidOrNameNode ->
%Node = ?get_node(PidOrNameNode),
try do_call(PidOrNameNode, Label, Request, Timeout)
catch
exit:{nodedown, _Node} ->
%% A nodedown not yet detected by global, pretend that it was.
exit(noproc)
end
end.
-dialyzer({no_improper_lists, do_call/4}).
do_call(Process, Label, Request, Timeout) ->
CurNode = node(),
case ?get_node(Process) of
CurNode ->
Mref = erlang:monitor(process, Process),
%% Local without timeout; no need to use alias since we unconditionally
%% will wait for either a reply or a down message which corresponds to
%% the process being terminated (as opposed to 'noconnection')...
case self() of
Process ->
exit(calling_self);
_ ->
Process ! {Label, {self(), Mref}, Request},
end,
receive
{Mref, Reply} ->
erlang:demonitor(Mref, [flush]),
{ok, Reply};
{'DOWN', Mref, _, _, Reason} ->
exit(Reason)
after Timeout ->
erlang:demonitor(Mref, [flush]),
receive
{[alias | Mref], Reply} ->
{ok, Reply}
after 0 ->
exit(timeout)
end
end;
_PNode ->
Mref = erlang:monitor(process, Process, [{alias, demonitor}]),
Tag = [alias | Mref],
%% OTP-24:
%% Using alias to prevent responses after 'noconnection' and timeouts.
%% We however still may call nodes responding via process identifier, so
%% we still use 'noconnect' on send in order to try to send on the
%% monitored connection, and not trigger a new auto-connect.
%%
erlang:send(Process, {Label, {self(), Tag}, Request}, [noconnect]),
receive
{[alias | Mref], Reply} ->
erlang:demonitor(Mref, [flush]),
{ok, Reply};
{'DOWN', Mref, _, _, noconnection} ->
exit({nodedown, _PNode});
{'DOWN', Mref, _, _, Reason} ->
exit(Reason)
after Timeout ->
erlang:demonitor(Mref, [flush]),
receive
{[alias | Mref], Reply} ->
{ok, Reply}
after 0 ->
exit(timeout)
end
end
end.
where({global, Name}) -> global:whereis_name(Name);
where({local, Name}) -> whereis(Name);
where({via, Module, Name}) -> Module:whereis_name(Name);
where({Name, Node} = Process) ->
CurNode = node(),
case CurNode of
Node ->
whereis(Name);
nonode@nohost ->
exit({nodedown, Node});
_ when is_atom(Name), is_atom(Node) ->
Process;
_ ->
undefined
end;
where(Name) ->
if
is_pid(Name) ->
Name;
is_atom(Name) ->
whereis(Name);
true ->
undefined
end.
%%
%% Send a reply to the client.
%%
greply({_To, [alias | Alias] = Tag}, Reply) when is_reference(Alias) ->
Alias ! {Tag, Reply}, ok;
greply({_To, [[alias | Alias] | _] = Tag}, Reply) when is_reference(Alias) ->
Alias ! {Tag, Reply}, ok;
greply({To, Tag}, Reply) ->
try To ! {Tag, Reply}, ok catch _:_ -> ok end.