Search code examples
erlangerlang-shell

Why second call to receive doesn't retrieve a message in Erlang shell?


I'm playing with processes in Erlang and trying to create a simple counter process. It receives a PID of a sender, increments inner counter and sends new counter value to the sender. I'm launching my code from erl shell (Erlang/OTP 20, Eshell V9.2). And I'm able to successfully receive first reply from the counter process, but not the second.

%% Process function that serves as a counter
CounterFun = fun() ->
  (fun Receiver(Current) ->
    io:format("  -- Entering receiver, current ~p~n", [Current]),
    receive
      Sender ->
        New = Current + 1,
        io:format("  -- Sending ~p to ~p~n", [New, Sender]),
        Sender ! New,
        Receiver(New)
    end
  end)(0)
end.

CounterPid = spawn(CounterFun).

CounterPid ! self().
receive V -> V after 3000 -> timeout end. % Will provide 1

CounterPid ! self().
receive V -> V after 3000 -> timeout end. % Will result in timeout

Here how it looks like in my console. enter image description here


Solution

  • The first receive binds the variable V to 1, so the second receive becomes:

    receive 
        1 -> ...
    
    end
    

    And, because the message 1 never arrives, the second receive times out. After the timeout, you can call flush() in the shell, and you will see that there was a message 2 in the mailbox. You can also call b() at any time to display the current variables and their values (which are known as bindings)--try that after executing the first receive.

    In your function, you are also doing a recursive receive within a receive within a receive such that the first receive never ends. To prove that, you can put a print statement after:

    Receiver(New)
    

    like:

    io:format("receive ending with Current= ~w~n", [Current])
    

    and you will never see any output. You should change your receive to something like this:

    New = Currrent + 1
    receive
      Sender ->
        io:format("  -- Sending ~p to ~p~n", [New, Sender]),
        Sender ! New,
        io:format("receive ending with Current= ~w~n", [Current])
    end,
    counter(New).