Search code examples
erlangerlang-shell

Executing concurrent example in Erlang code from command line


I'm testing the code in Getting Started with Erlang User's Guide Concurrent Programming Section.

In tut17.erl, I started a process with erl -sname ping, and another process with al -sname pong as the guide described.

-module(tut17).
-export([start_ping/1, start_pong/0, ping/2, pong/0]).

ping(0, Pong_Node) ->
    {pong, Pong_Node} ! finished, 
    io:format("ping finished~n", []);

ping(N, Pong_Node) ->
    {pong, Pong_Node} ! {ping, self()}, 
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,        
    ping(N - 1, Pong_Node).

pong() ->
    receive
        finished -> io:format("Pong finished~n", []);
        {ping, Ping_PID} -> 
            io:format("Pong received ping~n", []), 
            Ping_PID ! pong,
            pong() 
    end.

start_pong() ->
    register(pong, spawn(tut17, pong, [])).

start_ping(Pong_Node) ->
    spawn(tut17, ping, [3, Pong_Node]).

From the ping and pong process, I could invoke the start_ping and start_pong to check everything works fine.

(ping@smcho)1> tut17:start_ping(pong@smcho).
<0.40.0>
Ping received pong
Ping received pong
Ping received pong
ping finished  

(pong@smcho)2> tut17:start_pong().
true
Pong received ping
Pong received ping
Pong received ping
Pong finished 

I'm trying to run the same code from a command line; For a simple hello world example:

-module(helloworld).
-export([start/0]).

start() ->
    io:fwrite("Hello, world!\n").

I use the following command line:

erlc helloworld.erl
erl -noshell -s helloworld start -s init stop

So, I just tried with the following, but ended up with a crash.

  • From ping node: erl -noshell -sname ping -s tut17 start_ping pong@smcho -s init stop
  • From pong node: erl -noshell -sname pong -s tut17 start_pong -s init stop

However, I got this error report from the ping when pong ends without printing anything.

=ERROR REPORT==== 6-Mar-2015::20:29:24 ===
Error in process <0.35.0> on node 'ping@smcho' with exit value: 
    {badarg,[{tut17,ping,2,[{file,"tut17.erl"},{line,9}]}]}

Compared to the REPL approach, with command line, each process does not wait for the counterpart to response, but stops after some time. What might be wrong?


Solution

  • Arguments from the command line are received as a list of atoms when using the -s switch, and a list of strings when using the -run switch. With this in mind, let's think through what happens...

    This command is issued from the shell:

    erl -noshell -sname ping \
        -s tut17 start_ping pong@smcho \
        -s init stop
    

    So start_ping/1 is being called with the argument [pong@smcho]. It is then calling ping/2 as ping(3, [pong@smcho]), which in its first line tries to do {pong, [pong@smcho]} ! {ping, self()}. Because a list is not a valid target for a message... your world explodes.

    To run this both from the Erlang shell and the system shell comfortably you could add a clause to start_ping/1:

    start_ping([Pong_Node]) ->
        spawn(tut17, ping, [3, Pong_Node]);
    start_ping(Pong_Node) ->
        start_ping([Pong_Node]).