1
votes

working through Joe's book, got stuck on Chapter 12 exercise 1. That exercise is asking one to write a function start(AnAtom,Fun) that would register AnAtom as spawn(Fun). I've decided to try something seemingly easier - took the chapter's finished 'area_server' module, and modified it's start/0 function like this:

start() ->
    Pid = spawn(ex1, loop, []),
    io:format("Spawned ~p~n",[Pid]),
    register(area, Pid).

so in place of a process executing the arbitrary Fun, I am registering the 'loop', which is a function in the area_server module doing all the work:

loop() ->
    receive
        {From, {rectangle, Width, Ht}} ->
            io:format("Computing for rectangle...~n"),
            From ! {self(), Width*Ht},
            loop();
        {From, {square, Side}} ->
            io:format("Computing for square...~n"),
            From ! {self(), Side*Side},
            loop();
        {From, Other} ->
            io:format("lolwut?~n"),
            From ! {self(), {error, Other}},
            loop()
    end.

It seems to be working just fine:

1> c("ex1.erl").
{ok,ex1}
2> ex1:start(). 
Spawned <0.68.0>
true
3> 
3> area ! {self(), hi}.
lolwut?
{<0.61.0>,hi}
4> flush().
Shell got {<0.68.0>,{error,hi}}
ok
5> area ! {self(), {square, 7}}.
Computing for square...
{<0.61.0>,{square,7}}
6> flush().
Shell got {<0.68.0>,49}
ok

Thing went bad when I've tried to test that multiple processes can talk to the registered "server". (CTRL-G, s, c 2)

I'm in a new shell, running alongside the first - but the moment I send a message from this new shell to my 'area' registered process, something nasty happens - when querying process_info(whereis(area)), process moves from this state:

 {current_function,{ex1,loop,0}},
 {initial_call,{ex1,loop,0}},

to this one:

 {current_function,{io,execute_request,2}},
 {initial_call,{ex1,loop,0}},

while the message queue starts to grow, messages not getting processed. Hanging in module io, huh! Something is blocked on the io operations? Apparently the process is moved from my ex1:loop/0 into io:execute_request/2 (whatever that is)... are my silly prints causing the problem?

1
Hm I wonder if it's the same problem as is discussed here: elixirforum.com/t/…alexakarpov

1 Answers

1
votes

Your processes are doing what you expect with the exception of handling who has control over STDOUT at what moment. And yes, this can cause weird seeming behaviors in the shell.

So let's try something like this without any IO commands that are implied to go to STDOUT and see what happens. Below is a shell session where I define a loop that accumulates messages until I ask it to send me the messages it has accumulated. We can see from this example (which does not get hung up on who is allowed to talk to the single output resource) that the processes behave as expected.

One thing to take note of is that you do not need multiple shells to talk to or from multiple processes.

Note the return value of flush/0 in the shell -- it is a special shell command that dumps the shell's mailbox to STDOUT.

Eshell V9.0  (abort with ^G)
1> Loop = 
1>   fun L(History) ->
1>     receive
1>       halt ->
1>         exit(normal);
1>       {Sender, history} ->
1>         Sender ! History,
1>         L([]);
1>       Message ->
1>         NewHistory = [Message | History],
1>         L(NewHistory)
1>     end
1>   end.
#Fun<erl_eval.30.87737649>
2> {Pid1, Ref1} = spawn_monitor(fun() -> Loop([]) end).
{<0.64.0>,#Ref<0.1663562856.2369257474.102541>}
3> {Pid2, Ref2} = spawn_monitor(fun() -> Loop([]) end).
{<0.66.0>,#Ref<0.1663562856.2369257474.102546>}
4> Pid1 ! "blah".
"blah"
5> Pid1 ! "blee".
"blee"
6> Pid1 ! {self(), history}.
{<0.61.0>,history}
7> flush().
Shell got ["blee","blah"]
ok
8> Pid1 ! "Message from shell 1". 
"Message from shell 1"
9> Pid2 ! "Message from shell 1".
"Message from shell 1"
10> 
User switch command
 --> s
 --> j
   1  {shell,start,[init]}
   2* {shell,start,[]}
 --> c 2
Eshell V9.0  (abort with ^G)
1> Shell1_Pid1 = pid(0,64,0).
<0.64.0>
2> Shell1_Pid2 = pid(0,66,0).
<0.66.0>
3> Shell1_Pid1 ! "Message from shell 2".
"Message from shell 2"
4> Shell1_Pid2 ! "Another message from shell 2".
"Another message from shell 2"
5> Shell1_Pid1 ! {self(), history}.
{<0.77.0>,history}
6> flush().
Shell got ["Message from shell 2","Message from shell 1"]
ok
7> 
User switch command
 --> c 1

11> Pid2 ! {self(), history}.
{<0.61.0>,history}
12> flush().
Shell got ["Another message from shell 2","Message from shell 1"]
ok