Saurav Prakash
Saurav Prakash

Reputation: 1957

Erlang process getting automatically killed

I am learning erlang and was trying to write a simple client-server program.

The server code is as below:

-module(frequency_server).
-author("Saurav Prakash").

%% API
-export([init/0]).

init() ->
  Frequencies = [1,2,3,4],
  listen(Frequencies),
  io:format("Server leaving init...~n").

listen(Frequencies) ->
  io:format("Server inside init...~n"),
  receive
    {allocate, Pid} ->
      io:format("Request for allocation from ~w~n", [Pid]),
      if
        length(Frequencies) > 0 ->
          Frequency_Selected = random:uniform(length(Frequencies)),
          io:format("Freq selected is ~w~n", Frequency_Selected),
          Pid ! {ok, Frequency_Selected},
          listen(lists:delete(Frequency_Selected, Frequencies));
        true ->
          io:format("Not available freq...~n"),
          Pid ! {error, "Not Available"}
     end;
   {deallocate, Frequency, Pid} ->
     io:format("deallocation request...~n"),
     listen([Frequency | Frequencies])
  end,
  io:format("Server dying...~n").

The server is spawned as follows from another erlang script

 register(server, spawn(frequency_server, init, [])),
 io:format("server registered with ~w~n", [whereis(server)]).

But I found that as soon as server is inside listen(), it executes io:format() and gets killed.

What can be the problem?

Upvotes: 0

Views: 659

Answers (3)

zxq9
zxq9

Reputation: 13164

Your process is not dying, it is waiting for a message like you told it to:

Eshell V7.2  (abort with ^G)
1> c(frequency_server).
frequency_server.erl:26: Warning: variable 'Pid' is unused
{ok,frequency_server}
2> {Pid, Ref} = spawn_monitor(frequency_server, init, []).
Server inside init...
{<0.40.0>,#Ref<0.0.2.85>}
3> Pid ! {deallocate, 5, "This part of the message is meaningless data."}.
deallocation request...
{deallocate,5,
            "This part of the message is meaningless data."}
Server inside init...
4>

See? Its there, just waiting around for messages so it can execute its incredibly weird logic.

Here is a different version you might want to read over if you have time:

-module(wonky_serv).

%% API
-export([init/0]).

init() ->
    Frequencies = [1,2,3,4],
    ok = io:format("~tp ~tp: Starting with Frequencies: ~tp~n",
                   [self(), ?MODULE, Frequencies]),
    listen(Frequencies).

listen(Frequencies) ->
    ok = io:format("~tp ~tp: inside listen(~tp)~n", 
                   [self(), ?MODULE, Frequencies]),
    receive
        {allocate, Pid} ->
            {ok, NewFreqs} = allocate(Frequencies, Pid),
            listen(NewFreqs);
        {deallocate, Frequency, Pid} ->
            {ok, NewFreqs} = deallocate(Frequencies, Frequency, Pid),
            listen(NewFreqs);
        {terminate, Pid} ->
            ok = io:format("~tp ~tp: ~tp told me to terminate, so, bye!~n",
                           [self(), ?MODULE, Pid]);
        Unexpected ->
            ok = io:format("~tp ~tp: Unexpected message: ~tp~n", 
                           [self(), ?MODULE, Unexpected]),
            listen(Frequencies)
    end.

allocate([], Pid) ->
    ok = io:format("~tp ~tp: ~tp is requesting allocation, but no freqs available.~n",
                   [self(), ?MODULE, Pid]),
    {ok, []};
allocate([Alloc | Rest], Pid) ->
    ok = io:format("~tp ~tp: Allocating Freq ~tp to ~tp~n",
                   [self(), ?MODULE, Alloc, Pid]),
    {ok, Rest}.

deallocate(Freqs, Dealloc, Pid) ->
    NewFreqs = [Dealloc | Freqs],
    ok = io:format("~tp ~tp: Deallocating ~tp from ~tp~n"
                   "         Available freqs: ~tp~n",
                   [self(), ?MODULE, Dealloc, Pid, NewFreqs]),
    {ok, NewFreqs}.

Here is how that version plays out:

1> {Pid, Ref} = spawn_monitor(wonky_serv, init, []).
<0.35.0> wonky_serv: Starting with Frequencies: [1,2,3,4]
{<0.35.0>,#Ref<0.0.1.44>}
<0.35.0> wonky_serv: inside listen([1,2,3,4])
2> Pid ! {deallocate, 5, self()}.
<0.35.0> wonky_serv: Deallocating 5 from <0.33.0>
         Available freqs: [5,1,2,3,4]
{deallocate,5,<0.33.0>}
<0.35.0> wonky_serv: inside listen([5,1,2,3,4])
3> Pid ! {allocate, self()}.
<0.35.0> wonky_serv: Allocating Freq 5 to <0.33.0>
{allocate,0.33.0>}
<0.35.0> wonky_serv: inside listen([1,2,3,4])
4> Pid ! {terminate, self()}.
<0.35.0> wonky_serv: <0.33.0> told me to terminate, so, bye!
{terminate,<0.33.0>}
5> flush().
Shell got {'DOWN',#Ref<0.0.1.44>,process,<0.35.0>,normal}
ok
6>

Upvotes: 1

Hamidreza Soleimani
Hamidreza Soleimani

Reputation: 2554

I checked your code in a clean Erlang shell and it doesn't get killed and waits for incoming messages. Maybe there are some other hidden activities or environmental conditions which are the cause of problem.

Environmental conditions:

For example if you register server name with another process, when you do it again you encounter the following exception:

1> register(server, spawn(fun() -> receive _ -> ok end end)).
true
2> register(server, spawn(frequency_server, init, [])),
2> io:format("server registered with ~w~n", [whereis(server)]).
Server inside init...
** exception error: bad argument
     in function  register/2
        called as register(server,<0.36.0>)

Hidden activities:

For example it might that some other processes may send a kill signal to your registered server process which should be checked.

Upvotes: 1

Steve Vinoski
Steve Vinoski

Reputation: 20024

The process gets killed because of the incorrect io:format/2 call on line 20. Arguments to io:format/2 should be in a list, so this:

io:format("Freq selected is ~w~n", Frequency_Selected),

should instead be:

io:format("Freq selected is ~w~n", [Frequency_Selected]),

But you have other problems here as well, such as the way the code allocates frequencies. It does so by choosing a random number based on the length of the list of available frequencies and then deleting that number from the list. That works only if the frequency list is either the range 1-N where N is the length of the list, or the special case [1]. But this code allows for example the list to become [4] — when it has that value, this approach will choose the frequency 1 every single time, due to calling random:uniform(length([4])), even if another process has already allocated frequency 1, and frequency 4 remains available. The code should instead be checking that the chosen number is in the list before considering it allocated. Another problem is that deallocating a frequency doesn't check to see if the frequency is currently allocated, which allows the caller to keep deallocating the same frequency repeatedly and thus add that frequency back into the Frequencies list more than once.

Upvotes: 1

Related Questions