=== CALL TRACE OF net_kernel:connect\1
% otp/lib/kernel/src/net_kernel.erl
connect(node_name@machine) -> do_connect(node_name@machine, normal, false). ->
% Node, Type, WaitForBarred
do_connect(node_name@machine, normal, false ) -> %% Type = normal | hidden
case catch ets:lookup(sys_dist, Node) of
% [{connection, ...}] if node is in sys_dist
% [] if not
{'EXIT', _} ->
[#barred_connection{}] ->
Else ->
case application:get_env(kernel, dist_auto_connect) of
% so far I always got undefined
{ok, never} ->
{ok, once} when Else =/= [],
(hd(Else))#connection.state =:= up ->
% from this point on it is the same as connect_node/1
_ ->
request({connect, Type=normal, Node})
=== CALL TRACE OF net_kernel:connect_node\1
% otp/lib/kernel/src/net_kernel.erl
net_kernel:connect_node(node_name@machine) ->
net_kernel:request({connect, normal, node_name@machine}) ->
gen_server:call(net_kernel,Req = {connect, normal, node_name@machine},infinity) ->
% otp/lib/stdlib/src/gen_server.erl
% Name Request Timeout
gen_server:call(net_kernel, {connect, normal, node_name@machine}, infinity) ->
case catch gen:call(Name, '$gen_call', Request, Timeout) of
{ok, Res} -> Res;
% otp/lib/stdlib/src/gen.erl
% Name, Label, Request, Timeout
gen:call(net_kernel, '$gen_call', {connect, normal, node_name@machine}, infinity)
% Pid, Label, Request, Timeout
gen:do_call(whereis(net_kernel), '$gen_call', {connect, normal, node_name@machine}, infinity);
try erlang:monitor(process, Process) of
Mref ->
catch erlang:send(Process, {Label, {self(), Mref}, Request},[noconnect]),
receive
{Mref, Reply} ->
erlang:demonitor(Mref, [flush]),
{ok, Reply};
% otp/lib/stdlib/src/gen_server.erl
% {ok,Reply} -> Reply is false if node doesn't exist
% true if it does and is up, atom spelled correctly etc.
case catch gen:call(Name, '$gen_call', Request, Timeout) of
{ok, Res} -> Res;
otp/lib/kernel/src/net_kernel.erl
-spec connect_node(Node) -> boolean() | ignored when
Node :: node().
%% explicit connects
connect_node(Node) when is_atom(Node) ->
request({connect, normal, Node}).
otp/lib/kernel/src/net_kernel.erl
%% If the net_kernel isn't running we ignore all requests to the
%% kernel, thus basically accepting them :-)
request(Req) ->
case whereis(net_kernel) of
P when is_pid(P) ->
gen_server:call(net_kernel,Req,infinity);
_ -> ignored
end.
otp/lib/stdlib/src/gen_server.erl
%% -----------------------------------------------------------------
%% Make a call to a generic server.
%% If the server is located at another node, that node will
%% be monitored.
%% If the client is trapping exits and is linked server termination
%% is handled here (? Shall we do that here (or rely on timeouts) ?).
%% -----------------------------------------------------------------
call(Name, Request) ->
case catch gen:call(Name, '$gen_call', Request) of
{ok,Res} ->
Res;
{'EXIT',Reason} ->
exit({Reason, {?MODULE, call, [Name, Request]}})
end.
call(Name, Request, Timeout) ->
case catch gen:call(Name, '$gen_call', Request, Timeout) of
{ok,Res} ->
Res;
{'EXIT',Reason} ->
exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
end.
otp/lib/stdlib/src/gen.erl
%%-----------------------------------------------------------------
%% 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 pid
call(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 name
call(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 name
call(Process, Label, Request, Timeout)
when ((tuple_size(Process) == 2 andalso element(1, Process) == global)
orelse
(tuple_size(Process) == 3 andalso element(1, Process) == via))
andalso
(Timeout =:= infinity orelse (is_integer(Timeout) andalso Timeout >= 0)) ->
case where(Process) of
Pid when is_pid(Pid) ->
Node = node(Pid),
try do_call(Pid, Label, Request, Timeout)
catch
exit:{nodedown, Node} ->
%% A nodedown not yet detected by global,
%% pretend that it was.
exit(noproc)
end;
undefined ->
exit(noproc)
end;
%% Local by name in disguise
call({Name, Node}, Label, Request, Timeout)
when Node =:= node(), Timeout =:= infinity;
Node =:= node(), is_integer(Timeout), Timeout >= 0 ->
call(Name, Label, Request, Timeout);
%% Remote by name
call({_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) ->
try erlang:monitor(process, Process) of
Mref ->
%% If the monitor/2 call failed to set up a connection to a
%% remote node, we don't want the '!' operator to attempt
%% to set up the connection again. (If the monitor/2 call
%% failed due to an expired timeout, '!' too would probably
%% have to wait for the timeout to expire.) Therefore,
%% use erlang:send/3 with the 'noconnect' option so that it
%% will fail immediately if there is no connection to the
%% remote node.
catch erlang:send(Process, {Label, {self(), Mref}, Request},
[noconnect]),
receive
{Mref, Reply} ->
erlang:demonitor(Mref, [flush]),
{ok, Reply};
{'DOWN', Mref, _, _, noconnection} ->
Node = get_node(Process),
exit({nodedown, Node});
{'DOWN', Mref, _, _, Reason} ->
exit(Reason)
after Timeout ->
erlang:demonitor(Mref, [flush]),
exit(timeout)
end
catch
error:_ ->
%% Node (C/Java?) 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 featureweak remote nodes.
Node = get_node(Process),
monitor_node(Node, true),
receive
{nodedown, Node} ->
monitor_node(Node, false),
exit({nodedown, Node})
after 0 ->
Tag = make_ref(),
Process ! {Label, {self(), Tag}, Request},
wait_resp(Node, Tag, Timeout)
end
end.