3
votes

Does it make a difference if I register a newly spawned process using register(atom, spawn..) or if I do Pid = spawn..?

To take an example, I just did this with an old program from the Programming Erlang book:

Let's first make a simple server loop:

-module(geometry_server).
-export([loop/0]).

loop() ->
    receive
        {Client, {square, S} = Tuple} ->
            io:format("Server: Area of square of Side ~p is ~p and Client was ~p~n", [S, S*S, Client]),
            Client ! {self(), Tuple,  S*S},
            loop()
    end.

Now a client:

-module(geometry_client).
-export([client/2, start_server/0]).

client(Pid_server, Geom_tuple) ->
    Pid_server ! {self(), Geom_tuple},
    receive
        {Pid_server, Geom_tuple, Area} -> io:format("Client: Area of ~p is ~p and       server was ~p~n", [Geom_tuple, Area, Pid_server])

    after 1000 ->
          io:format("~p~n",["received nothing from server"] )
    end.

start_server() ->    spawn(geometry_server, loop, []).

After compiling both, I do

 register(q, Q = geometry_client:start_server()).

Then I call them and get results as follows:

5> geometry_client:client(Q, {square,2}).
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
Client: Area of {square,2} is 4 and server was <0.77.0>
ok
6> geometry_client:client(q, {square,2}).
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
"received nothing from server"
ok

Why does the client not receive anything from the server when I use the registered atom?? The server obviously received the message from the client.

I can confirm that the server sent a message, because after the above if I do

7> geometry_client:client(whereis(q), {square,2}).
Client: Area of {square,2} is 4 and server was <0.77.0>
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
ok
12>

So I conclude that the mailbox already has the message from the server from the previous command, which is why the Client output gets printed before the Server message has been received and printed...

What am I missing?? Why is there a problem receiving the message when I use the registered atom?

1

1 Answers

3
votes

The receive in your client/2 function waits for a message matching {Pid_server, Geom_tuple, Area}. When you pass q as the argument, Pid_server is q, but the message the server sends back to the client is a tuple with the first element always being the actual PID of the server, not its name, which means your receive ends up in the after block.

There are many ways to solve this. You can modify client/2 to use whereis/1 to get the PID of the registered process and use that in receive if Pid_server is an atom.

The best way would be to use references here (see make_ref/0). You create a reference when sending a message to the server, and the server sends it back in the response. This way you're guaranteed that you're receiving the response for the request you just sent, since every reference returned by make_ref/0 is guaranteed to be unique.

In client/2, do:

client(Pid_server, Geom_tuple) ->
    Ref = make_ref(),
    Pid_server ! {Ref, self(), Geom_tuple},
    receive
        {Ref, Geom_tuple, Area} -> io:format("Client: Area of ~p is ~p and       server was ~p~n", [Geom_tuple, Area, Pid_server])
    after 1000 ->
          io:format("~p~n",["received nothing from server"] )
    end.

And in the server:

loop() ->
    receive
        {Ref, Client, {square, S} = Tuple} ->
            io:format("Server: Area of square of Side ~p is ~p and Client was ~p~n", [S, S*S, Client]),
            Client ! {Ref, Tuple,  S*S},
            loop()
    end.