Beginner OTP question

Scott Lystig Fritchie fritchie@REDACTED
Fri Feb 18 07:30:00 CET 2005


Here's a unified diff for one way (of several) to fix things up.  I
removed the bits trying to use global naming service.

--- myserver.erl        Thu Feb 17 23:29:49 2005
+++ myserver2.erl       Thu Feb 17 23:28:56 2005
@@ -2,16 +2,16 @@
 
 -behaviour(gen_server).
 
--export([start/0, init/1, send/0, handle_call/3]).
+-export([start/0, init/1, send/1, handle_call/3]).
 
 start() ->
-    gen_server:start({global, myserver}, myserver, [], []).
+    gen_server:start({local, myserver}, myserver, [], []).
 
 init(_Args) ->
     {ok, ""}.
 
-send() ->
-    gen_server:call(myserver, send).
+send(ServerID) ->
+    gen_server:call(ServerID, send).
 
 handle_call(send, From, _State) ->
     io:format("send called from ~p", [From]),

Then, over on the gbye node, use this function call:

    myserver:send({myserver, hello@REDACTED}).

On the hello node, you can use:

    myserver:send(myserver).
    myserver:send({myserver, node()}).
    myserver:send({myserver, hello@REDACTED}).

If you'd started the server on hello using:

    {ok, MyServerPid} = myserver:start().

Then this would work, too:

    myserver:send(MyServerPid).

Using the list_to_pid() function is generally frowned upon.  Joe
Armstrong would say that it allows you to violate the principle of a
process's "unforgeable name", namely its process ID number.  If you're
not using a local naming scheme (as shown above) or a global naming
scheme, either of which maps a well-known name to a process ID, the
only way you're supposed to know a process ID is if someone else tells
you what it is.  list_to_pid() allows you to circumvent it.

One handy trick with the shell is using the history mechanism.  Here,
at command number 12, I started the server but forgot to save the
pid.  But I can easily get the real, honest-to-goodness pid after the
fact.

(a@REDACTED)12> myserver:start().                      
{ok,<0.56.0>}
(a@REDACTED)13> {ok, MyServerPid} = v(12).
{ok,<0.56.0>}
(a@REDACTED)14> myserver:send(MyServerPid).
send called from {<0.50.0>,#Ref<0.0.0.94>}"Thanks for calling"

In practice, you need someone with a well-known name to act as an
introducer to help break the ice.(*)  Otherwise, you wouldn't be able to
communicate with anyone: you wouldn't know any pids at all!

If over on the gbye node you said:

(gbye@REDACTED)13> register(a_local_name, self()).
true

... then the shell over on the hello node can easily send messages to
the shell on gbye.  On the hello node, use:

(hello@REDACTED)17> {a_local_name, gbye@REDACTED} ! {ice_breaker, MyServerPid}.
{ice_breaker,<0.56.0>}

That uses the "!" notation to send a simple message over to
a_local_name over on gbye@REDACTED(**)  

That message is waiting in the shell process's mailbox over on the
other node.  You can fetch it by:

(gbye@REDACTED)19> receive {ice_breaker, Pid} -> Pid end.
<6348.56.0>

And then you can use it to call your gen_server process without
relying on the local name registration scheme.

(gbye@REDACTED)20> myserver:send(Pid).                   
"Thanks for calling"

You can do this same message passing stuff between Erlang nodes
running on different UNIX/Windows/Mac/whatever machines.(****) 

-Scott

(*) The only other way would be some kind of side communication
channel.  For example, if you could write a pid to a file and then
have some other process read the file to get that pid.

(**) "!" is not used to call a function, like you'd tried to do.  "!"
is the Erlang primitive to send a message to another process.

The gen_server:call() function ends up using this notation to
send the message, but it hides some additional details from you.  So
does the gen_server code receiving side.  When using gen_server, you
usually don't need to know the details.

(***) I dunno where this file lives on a Windows platform, sorry.

(****) It's common to run into a problem with an Erlang security
mechanism: there's a file called ".erlang.cookie" that's placed in
your UNIX home directory(***).  If your machines don't have a common
home directory (e.g. via NFS), then you'll have to copy one machine's
".erlang.cookie" file over to the other machine, then restart both
Erlang nodes.



More information about the erlang-questions mailing list