Performing processes communication - erlang

I have just started reading the Programming Erlang book. There is very single example that works just file.
If I perform:
> c(afile_server).
> c(afile_client).
> Server = afile_server:start(".").
> client:get_file(Server, "file1").
> client:get_file(Server, "file2").
> client:get_file(Server, "file3").
Everything works just file. The three files content are displayed.
But if I do:
> c(afile_server).
> Server = afile_server:start(".").
> Server ! { self(), { get_file, "file1" } }.
> receive
{ Server, Content }
Content
end.
It works just file.
But if I change the file that I am trying to read (after invoking the first time), e.g.
> Server ! { self(), { get_file, "file2" } }.
> receive
{ Server, Content }
Content
end.
The receive blocks and nothing is returned.
Could you guys help somehow? I guess, it is a very stupid, mistake!
Regards.
Files:
Server:
-module(afile_server).
-export([start/1, loop/1]).
start(Dir) -> spawn(afile_server, loop, [Dir]).
loop(Dir) ->
receive
{Client, list_dir} ->
Client ! {self(), file:list_dir(Dir)};
{Client, {get_file, File}} ->
Full = filename:join(Dir, File),
Client ! {self(), file:read_file(Full)}
end,
loop(Dir).
Client
%% ---
%% Excerpted from "Programming Erlang, Second Edition",
%% published by The Pragmatic Bookshelf.
%% Copyrights apply to this code. It may not be used to create training material,
%% courses, books, articles, and the like. Contact us if you are in doubt.
%% We make no guarantees that this code is fit for any purpose.
%% Visit http://www.pragmaticprogrammer.com/titles/jaerlang2 for more book information.
%%---
-module(afile_client).
-export([ls/1, get_file/2]).
ls(Server) ->
Server ! {self(), list_dir},
receive
{Server, FileList} ->
FileList
end.
get_file(Server, File) ->
Server ! {self(), {get_file, File}},
receive
{Server, Content} ->
Content
end.

That is because you're running this in the shell and reusing the name Content in the receive pattern and the 2 files presumably have different content in your filesystem, so the receive blocks until the process gets a message with the same content. To fix this, you can either use a different name for Content every time, or explicitly "forget" the variable using f(Content).
1> c(afile_server).
{ok,afile_server}
2> Server = afile_server:start(".").
<0.64.0>
3> Server ! { self(), { get_file, "file1" } }.
{<0.57.0>,{get_file,"file1"}}
4> receive {Server, Content} -> Content end.
{ok,<<"file1\n">>}
5> Server ! { self(), { get_file, "file2" } }.
{<0.57.0>,{get_file,"file2"}}
6> receive {Server, Content} -> Content after 1000 -> timeout end.
timeout
7> receive {Server, Content2} -> Content2 end.
{ok,<<"file2\n">>}
8> Server ! { self(), { get_file, "file3" } }.
{<0.57.0>,{get_file,"file3"}}
9> receive {Server, Content} -> Content after 1000 -> timeout end.
timeout
10> f(Content).
ok
11> receive {Server, Content} -> Content after 1000 -> timeout end.
{ok,<<"file3\n">>}

Related

Erlang: Chat server crashing on startup

I got a set of tests that the program should pass and all the local tests works just fine with my server it's when I try to run the remote tests that the server crashes.
The crash message is the following:
=ERROR REPORT==== 23-Jul-2015::23:59:17 === Error in process <0.39.0> on
node 'nodeS#127.0.0.1' with exit value:
{undef,[{genserver,start,[server, {server_st,[],[]},#Fun<server.loop.2>],[]}]}
My start-up function looks as following:
loop(St, {From, Nick, connection_wanted}) ->
case lists:keymember(Nick, 2, St#server_st.users) of
false -> {ok, St#server_st{users = St#server_st.users ++ [{From, Nick}]}};
true -> {{user_already_connected, St}, St}
end;
With the record "server_st" is defined as:
-record(server_st, {users = [], channels = []}).
Finally the genserver start&loop function is:
start(Name, State, F) ->
Pid = spawn(fun() -> loop(State, F) end),
register(Name, Pid),
Pid.
loop(State, F) ->
receive
{request, From, Ref, Data} ->
case catch(F(State, Data)) of
{'EXIT', Reason} ->
From!{exit, Ref, Reason},
loop(State, F);
{R, NewState} ->
From!{result, Ref, R},
loop(NewState, F)
end;
{update, From, Ref, NewF} ->
From ! {ok, Ref},
loop(State, NewF);
stop ->
true
end.
Then genserver functions I'm not allowed to change. If needed I can post the whole testsuite too.
Edit
Digging a bit further into the test cases and I'm unsure if it really is the server that's causing the issue, my remote connect function looks as following:
loop(St, {connect, {_Server, _Machine}}) ->
ServerPID = {list_to_atom(_Server), list_to_atom(_Machine)},
case genserver:request(ServerPID, {self(), St#cl_st.nick, connection_wanted}) of
ok -> {ok, St#cl_st{connected_to = ServerPID}};
_ -> {{error, user_already_connected, "A user with the nick " ++ St#cl_st.nick ++ "is already connected to" ++ _Server}, St}
end;
Edit 2
Found the specific row inside the testsuite that's causing the error:
-define(HOST, '127.0.0.1').
new_client(Nick, GUIName) ->
ClientName = test_client:find_unique_name("client_"),
ClientAtom = list_to_atom(ClientName),
% Row below is causing the error
Result = slave:start(?HOST, ClientAtom),
assert_ok("start client node "++ClientName, element(1,Result)),
ClientNode = element(2,Result),
InitState = client:initial_state(Nick, GUIName),
Result2 = spawn(ClientNode, genserver, start, [ClientAtom, InitState, fun client:loop/2]),
assert("client startup "++ClientName, is_pid(Result2)),
{Nick, ClientAtom, ClientNode}.
Your function genserver:start/3 is most probably not exported or module genserver is not available at the node where you run code which calls it.
Solved it, was in a completely unrelated part where the client is communicating with other users. Still used the whereis command to locate other users from an older version of the program.

Erlang: push and pull from a shared queue

I need to maintain a shared queue where I can push data and a separate thread will periodically check and pull data from the queue if its not empty. I came up with the below solution where I can send data to the process and it adds up to a list. However, is there a cleaner / easier solution to do this ?
And im not sure how I may pull data from the below code.
-module(abc).
-export(queue/0).
queue() ->
receive
{push, Xmpp} ->
io:format("Push"),
queue(Xmpp);
{pull} ->
io:format("pull"),
queue()
end.
queue(E) ->
receive
{push, Xmpp} ->
io:format("Push ~w",[E]),
E1 = lists:append([E],[Xmpp]),
queue(E1);
{reset} ->
queue([])
end.
The code probably won't do exactly what you want as written. When you call queue/1 from the receive block ( queue(Xmpp); in line 7), queue/1 will fire and then wait for a message. Because this is not spawned in a separate process, queue/0 will block (since queue/1 is now waiting for a message that is never sent).
Also, queue/0 won't ever send anything back to the process sending it messages. It has no way of returning data to the sender.
The following will work (you will need to send messages to the pid returned by queue/0).
-module(abc).
-export([queue/0,queue/1]).
queue() ->
%% initialize an empty queue,
%% return the Pid to the caller
spawn(abc,queue,[[]]).
queue(E) when is_list(E) ->
receive
%% append the message value to the existing list
{push, Xmpp} ->
io:format("Pushing ~w to ~w~n",[Xmpp,E]),
E1 = lists:append(E,[Xmpp]),
queue(E1);
%% reset the queue
{reset} ->
queue([]);
%% return the value to the caller
%% "Pid" must be a Pid
{pull, Pid} when is_pid(Pid) ->
io:format("pull~n"),
Pid ! E,
queue(E)
end.
That's a problem with a straightforward solution in Erlang. Most of the time every erlang module you will be programming will be like a server, which will expect messages and will answer, you can have 0, 1 or mulitple servers running the same erlang module code. At the same time, you will be programming in the same module a client, which is an easy way to send messages to the server without having to know all the message format the server expect, instead you use functions, so instead of doing stuff like
Server ! {put, Value},
receive
{Server, {ok, Value}} ->
everything_ok;
{Server, {error, Reason}} ->
handle_error
end,
you end doing something like
my_module:put(Server, Value).
So you can create a server process in erlang with the code:
-module(abc).
-export([start/0, put/2, pop/1, reset/1, is_empty/1, loop/1]).
%% Client
start() ->
spawn(?MODULE, loop, [[]]).
put(Pid, Item) ->
Pid ! {self(), {put, Item}},
receive
{Pid, {ok, Item}} ->
Item;
{Pid, {error, Reason}} ->
{error, Reason}
after 500 ->
timeout
end.
pop(Pid) ->
Pid ! {self(), {pop}},
receive
{Pid, {ok, Item}} ->
Item;
{Pid, {error, Reason}} ->
{error, Reason}
after 500 ->
timeout
end.
reset(Pid) ->
Pid ! {self(), {reset}},
receive
{Pid, {ok}} ->
ok;
_ ->
error
after 500 ->
timeout
end.
is_empty(Pid) ->
Pid ! {self(), {is_empty}},
receive
{Pid, {true}} ->
true;
{Pid, {false}} ->
false;
_ ->
error
after 500 ->
timeout
end.
%% Server
loop(Queue) ->
receive
{From, {put, Item}} when is_pid(From) ->
From ! {self(), {ok, Item}},
loop(Queue ++ [Item]);
{From, {pop}} when is_pid(From) ->
case Queue of
[] ->
From ! {self(), {error, empty}},
loop(Queue);
[H|T] ->
From ! {self(), {ok, H}},
loop(T)
end;
{From, {reset}} when is_pid(From) ->
From ! {self(), {ok}},
loop([]);
{From, {is_empty}} when is_pid(From) ->
case Queue of
[] ->
From ! {self(), {true}},
loop(Queue);
_ ->
From ! {self(), {false}},
loop(Queue)
end;
_ ->
loop(Queue)
end.
and the code you end writting to use is simple as well:
(emacs#rorra)1> c("/Users/rorra/abc", [{outdir, "/Users/rorra/"}]).
{ok,abc}
(emacs#rorra)2> Q = abc:start().
<0.44.0>
(emacs#rorra)3> abc:is_empty(Q).
true
(emacs#rorra)4> abc:pop(Q).
{error,empty}
(emacs#rorra)5> abc:put(Q, 23).
23
(emacs#rorra)6> abc:is_empty(Q).
false
(emacs#rorra)7> abc:pop(Q).
23
(emacs#rorra)8> abc:pop(Q).
{error,empty}
(emacs#rorra)9> abc:put(Q, 23).
23
(emacs#rorra)10> abc:put(Q, 50).
50
(emacs#rorra)11> abc:reset(Q).
ok
(emacs#rorra)12> abc:is_empty(Q).
true
At the end to avoid all that repeated code you end using OTP and writting a gen_server for it.
I'm assuming you are building a queue on your own for learning, otherwise Erlang already has a good implementation for a Queue:
http://www.erlang.org/doc/man/queue.html
And the source code:
https://github.com/erlang/otp/blob/master/lib/stdlib/src/queue.erl

Erlang can not receive message from where it begins

There are two files here.
temp_converter() ->
receive
{convertToCelsius, {From, TempF}} ->
io:format("Temp-Server2 > Converter received message from ~w~n", [From]),
ConvertedTempC = (TempF-32)*5/9,
From ! finished,
From ! {converted, ConvertedTempC},
temp_converter();
{From, {convertToCelsius, TempF}} ->
io:format("Temp-Server > Converter received message from ~w~n", [From]),
ConvertedTempC = (TempF-32)*5/9,
From ! {converted, ConvertedTempC},
temp_converter()
end.
The other one is:
sensor(Temp, Temp_Node, Dis_Node) ->
receive
finished ->
io:foramt("finished");
% Receive the clock_tick from clock controller
clock_tick ->
io:format("Sensor_f received tick~n", []),
{temp_converter, Temp_Node} ! {convertToCelsius, {self(), Temp}};
% Receive the temperature which has been converted
{converted, ConvertedTemp} ->
io:format("Sensor_f received converted temperature~n", []),
{display_module, Dis_Node} ! {inCelsius, ConvertedTemp};
{'EXIT', From, Reason} ->
io:foramt("Temperature Server down!~nGot ~p~n", [{'EXIT', From, Reason}])
end.
Basically, the sensor will send message to temp_converter, which is implemented in "clock_tick ->". When temp_converter received message, it will output something and send message back. Here is the problem.
It does output something, but sensor can not receive message from temp_converter. Is there anything wrong with my code? I also tried to send "finished" message back, but it still does not work!!
How can I send message back? How can I change "From ! finished" and "From ! {converted, ConvertedTempC}," to be correct?
In the conversion:
{convertToCelsius, {From, TempF}} ->
io:format("Temp-Server2 > Converter received message from ~w~n", [From]),
ConvertedTempC = (TempF-32)*5/9,
From ! finished, %% Here you send to sensor the message 'finished'
From ! {converted, ConvertedTempC}, %% the conversion will be received %% after the first message (guaranteed by Erlang VM)
temp_converter();
And in the sensor:
finished ->
io:foramt("finished"); %% when you receive the first message, you print it
%% and leave the function. The next message will be lost
%% because you do not loop in the sensor function.
you only have to recall sensor(Temp, Temp_Node, Dis_Node) after the receive bloc and it should be fine.
Note that the message finished is useless in your case and that a simple call to a conversion function should do the job.

Erlang basic general server debugger output interpretation

I'm having some trouble with an Erlang module. Here is the one that I wrote:
-module(basic_gen_server).
-export([start/1, call/2, cast/2]).
start(Module) ->
register(server, spawn(basic_gen_server,gen_server_loop,[Module, Module:init()])), server.
call(Pid,Request) ->
Pid ! {call, self(), Request},
receive
Reply -> Reply
end.
cast(Pid,Request) ->
Pid ! {cast, self(), Request},
receive
_ -> ok
end.
gen_server_loop(Module, CurrentState) ->
io:fwrite("gen_server_loop~n", []),
receive
{call, CallPid, Request} ->
{reply, Reply, NewState} = Module:handle_call(Request,self(),CurrentState),
CallPid ! Reply,
gen_server_loop(Module, NewState);
{cast, CastPid, Request} ->
{noReply, NewState} = Module:handle_cast(Request, CurrentState),
CastPid ! noReply,
gen_server_loop(Module, NewState)
end.
And here is the callback module that was defined:
% Written by Caleb Helbling
% Last updated Oct 10, 2014
-module(name_server).
-export([init/0, add/3, whereis/2, handle_cast/2,
handle_call/3, handle_swap_code/1]).
%% client routines
add(ServerPid, Person, Place) ->
basic_gen_server:cast(ServerPid, {add, Person, Place}).
whereis(ServerPid, Person) ->
basic_gen_server:call(ServerPid, {whereis, Person}).
%% callback routines
init() ->
maps:new().
handle_cast({add, Person, Place}, State) ->
NewState = maps:put(Person, Place, State),
{noreply, NewState}.
handle_call({whereis, Person}, _From, State) ->
Reply = case maps:find(Person, State) of
{ok, Place} -> Place;
error -> error
end,
NewState = State,
{reply, Reply, NewState}.
handle_swap_code(State) ->
{ok, State}.
Upon trying to initialize the server with the following command:
MyServer = basic_gen_server:start(name_server).
I get the following debug output:
=ERROR REPORT==== 29-Oct-2014::12:41:42 ===
Error in process <0.70.0> with exit value: {undef,[{basic_gen_server,gen_server_loop,[name_server,#{}],[]}]}
Conceptually, I understand the notion of making serial code into a basic server system, but I believe that I have a syntax error that I haven't been able to find using either syntax highlighting or Google. Thanks in advance for the help!
Function gen_server_loop is not exported. So you can not call basic_gen_server:gen_server_loop(Module, Module:init()), which is what is happening inside spawn(basic_gen_server,gen_server_loop,[Module, Module:init()]).
If you read your error message it tells you that function you are trying to call in undefined (trougn undef atom). Function being {basic_gen_server,gen_server_loop,[name_server,#{}],[]}, or where you have {Module, Function, ListOfArgs, ...}. You always should check that
there are no types module or function name
function arity match number of arguments in call (List in error message)
function is exported
All local calls (like loop(SomeArgs), without module specified) will not compile if function is not defined. And you can do local call dynamically (FuntionName(SomeArgs) again without module name).
EDIT after comment about need of local calls.
You actually could use lambda for this. There is spawn/1 funciton, which takes lambda (or fun if you like), so you can call spawn( fun local_functino/0).. Only issue with that is fact that your fun can not take any arguments, but there is a way around it, with use of closures.
spawn(fun () ->
gen_server_loop(Module, Module:init())
end).
And gen_serve_loop stays local call.

Erlang missing messages

I'm running the following code with dbg:p(client, r):
-module(client).
-export([start/0, start/2, send/1, net_client/1]).
start() ->
start("localhost", 7000).
start(Host, Port) ->
io:format("Client connecting to ~p:~p.~n", [Host, Port]),
register(ui, spawn(fun() -> gui_control([]) end)),
case gen_tcp:connect(Host, Port, [binary, {packet, 0}]) of
{ok, Socket} ->
Pid = spawn(client, net_client, [Socket]),
register(client, Pid),
gen_tcp:controlling_process(Socket, Pid);
Error ->
io:format("Error connecting to server: ~p~n", [Error]),
erlang:error("Could not connect to server.")
end,
ok.
send(Msg) ->
client!{send, Msg}.
%% Forwards messages to either the GUI controller or the server.
net_client(Socket) ->
receive
{tcp, Socket, Message} ->
Msg = binary_to_term(Message),
io:format("Received TCP message on ~p: ~p~n", [Socket, Msg]),
ui!{server, Msg};
{send, Message} ->
io:format("Sending ~p.~n", [Message]),
gen_tcp:send(Socket, term_to_binary(Message));
close ->
gen_tcp:close(Socket),
exit(normal);
{tcp_closed, Socket} ->
io:format("Server terminated connection.~n"),
exit(normal); %% Reconnect?
timeout -> %% This
io:format("Timed out?~n");
{inet_reply, Socket, Message} -> %% and this never happen.
io:format("inet_reply: ~p~n", Message);
Error ->
io:format("Net client got bad message: ~p.~n", [Error])
after 10000 ->
refresh %% gen_tcp:send(Socket, term_to_binary(keepalive))
end,
?MODULE:net_client(Socket).
gui_control(Data) ->
receive
%% This will hang the gui until the sync is done. Not sure if
%% that's okay.
{server, {sync, Datum}} -> % Resync command from server.
gui_control(resync([Datum]));
{client, refresh} -> % Refresh request from display.
display:update(Data);
{server, Msg} ->
io:format("UI Rx: ~p~n", [Msg])
end,
gui_control(Data).
resync(Data) ->
receive
{server, {sync, Datum}} ->
resync([Datum|Data]);
{server, {done, Num}} ->
case length(Data) of
Num ->
Data;
_ ->
io:format("Got done before all the data were received.~n"),
send({sync})
end
after 5000 ->
io:format("Timed out waiting for data.~n"),
send({sync})
end.
It communicates with a server I wrote with gen_tcp and gen_server, following this. My main problem is that I don't reliably receive all my messages. Sometimes I'll get
(<0.2.0>) << {tcp,#Port<0.517>,
<<131,104,6,100,0,4,99,97,114,100,100,0,7,117,110,107,110,
111,119,110,100,0,7,117,110,107,110,111,119,110,106,106,
104,3,107,0,6,83,101,99,111,110,100,100,0,4,100,114,97,
119,97,2,131,104,6,100,0,4,99,97,114,100,100,0,7,117,110,
107,110,111,119,110,100,0,7,117,110,107,110,111,119,110,
106,106,104,3,107,0,6,83,101,99,111,110,100,100,0,4,100,
114,97,119,97,3,131,104,6,100,0,4,99,97,114,100,100,0,7,
117,110,107,110,111,119,110,100,0,7,117,110,107,110,111,
119,110,106,106,104,3,107,0,5,70,105,114,115,116,100,0,4,
100,114,97,119,97,0>>}
from the debugging output, but no corresponding Received TCP message on #Port<0.517>:... message. I'll also see things like this:
(<0.2.0>) << {io_reply,<0.24.0>,ok}
(<0.2.0>) << timeout
(<0.2.0>) << {io_reply,<0.24.0>,ok}
(<0.2.0>) << timeout
(<0.2.0>) << {io_reply,<0.24.0>,ok}
but nothing from net_client's receive. I've watched the network traffic with wireshark and I know the packets are getting where they're supposed to go and being ACKed. What am I doing wrong?
Edit: I'm invoking this with erl -smp enable -eval "client:start()." in case it matters.
I guess the basic problem is that 'net_client' should be spawned off as a separate process..
In the start method, change
register(client, self()),
net_client(Socket);
to
register(client, fun() -> net_client(Socket) end);
that should solve it..
Also, I recommend using redbug (part of eper) https://github.com/massemanet/eper when tracing. It protects you from drowning your system in trace output and provides a dead simple syntax, eg: redbug:start("mymod:foo -> return", [{msgs,10}]). trace all calls to mymod:foo and what those calls return but give me no more than 10 trace messages.
Turns out {packet, 0} was my problem. Replace that with {packet, 2} and all is well.

Resources