Reputation: 1957
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
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
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.
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>)
For example it might that some other processes may send a kill signal to your registered server
process which should be checked.
Upvotes: 1
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