[erlang-questions] Why does adding an io:format call make my accept socket remain valid?
Matthew Shapiro
me@REDACTED
Sun Mar 27 06:16:16 CEST 2016
In my attempts at learning Erlang I am currently writing a generic
tcp_listener application library. One thing I want is for the library to
be decoupled from the main project I am writing (simple IRC server) and
have it be testable.
The way I am handling that is by passing in a MFA for it to pass the
accepted socket to, so the tcp_listener gen_server can go back to handling
more sockets.
I am testing this by creating an EUnit test that connects to the listening
socket passed to the listener server and passing in a controlled MFA that
sends itself a message back. The code is as follows:
-------------------------------------------
handle_cast(accept, State=#state{}) ->
{Module, Function, Arguments} = State#state.on_accept_mfa,
{ok, AcceptSocket} = gen_tcp:accept(State#state.socket),
(State#state.create_acceptor_fun)(),
Pid = spawn(Module, Function, [AcceptSocket | Arguments]),
io:format(user, "Accept socket valid: ~s~n",
[erlang:port_info(AcceptSocket) =/= undefined]),
{noreply, State}.
--------------------------------------------
%% Tests
test_mfa(ListenSocket) ->
Mfa = {?MODULE, send_self_message, [self()]},
State = #state{socket = ListenSocket, on_accept_mfa = Mfa,
create_acceptor_fun = fun do_nothing/0},
spawn_link(fun () -> tcp_listener_server:handle_cast(accept, State) end),
{ok, _} = gen_tcp:connect({127,0,0,1}, ?test_port, []),
receive
success -> ?_assert(true);
X -> ?_assert(X)
after 1000 ->
?_assert(false)
end.
%% Stubs
do_nothing() -> ok.
send_self_message(AcceptSocket, TestPid) ->
case erlang:port_info(AcceptSocket) of
undefined -> TestPid ! bad_accept_socket;
_ -> TestPid ! success
end.
------------------------------------------------
Now I am 100% sure that my handle_cast is wrong, in that I should be
passing ownership of the accepted socket to the spawned process. Adding
that makes everything work honkey dorey.
Before I realized that though, I got stuck for quite a while with a failing
test that I couldn't figure out why. Before I added that io:format call in
handle_cast/2 my tests were failing with erlang:port_info/1 returning
undefined. I was going crazy in the shell trying to figure out why and
adding debug statements all over the place when I added the io:format/3
call you see now. Suddenly everything works and the test passes.
Why does that io:format/3 call (only when after the spawn/3 call) keep the
socket open and active for the spawned process? I wouldn't expect it to be
garbage collected because the spawned process holds a reference to it (and
even adding it to the State in the return doesn't fix it). Only other
thing I could think of was that handle_cast is run in it's own process
which closes the socket when it dies (since it has ownership), but that
doesn't make sense from what I've read about OTP.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20160327/6bb8be56/attachment.htm>
More information about the erlang-questions
mailing list