[erlang-bugs] R15B03 - Following a send_timeout on a socket, the socket will no longer be closed on process exit.

Paul Cager paul.cager@REDACTED
Thu Feb 7 19:06:51 CET 2013


Erlang R15B03 (V5.9.3.1) on Linux (RHEL 6.3).

Normally if the connected process of a TCP socket exits, the socket is
closed. However if a socket has option send_timeout (*without*
send_timeout_close), and a send does indeed timeout, then the socket
is not closed when the connected process exits. It is also not closed
if you use gen_tcp:close(), erlang:port_close() etc.

This results in a "leaked" socket that becomes impossible to close
(unless the remote end closes, or the whole beam OS process exits).

To reproduce (on Linux)
=======================

  1)  Build the runtime with INET_DRV_DEBUG turned on in inet_drv.c.
  2)  In one window run netstat so you can see which sockets are open
on the test port (1616)
           while true; do netstat -nap | grep :1616 ; sleep 1; done
  3)  Run the Erlang test module below to demonstrate the *working*
case (i.e. not hitting a send_timeout).
  4)  Note from the debug output that tcp_inet_stop of inet_drv is
called. As a result the socket closes (indicated by netstat output).
  5)  Edit the test module to comment out the exit() where indicated
(%% COMMENT OUT FOLLOWING LINES TO DEMONSTRATE PROBLEM). Re-run the
test.
  6)  Note that tcp_inet_stop is *not* called and that the socket
remains established even after termination of the connected process.
  7)  If you replace the exit() following the send_timeout with a call
to prim_inet:close() or erlang:port_close() you get similar results.


============================================================================================
-module(test_tcp_close2).

-compile(export_all).

main() ->
        {Pid, _Ref} = spawn_monitor(fun() -> run_test() end),
        receive
                {'DOWN', _, process, Pid, _} ->
                        io:format("Child process terminated - socket
should definately be closed~n", []),
                        io:format("All ports: ~p~n", [erlang:ports()])
        end.


run_test() ->
        {ok, LSock} = gen_tcp:listen(1616, [binary, {packet,0},
{active, false}, {reuseaddr, true}]),

        spawn(?MODULE, connect_and_idle, [1616]),

        {ok, Sock} = gen_tcp:accept(LSock),
        {ok, FD} = prim_inet:getfd(Sock),

        io:format("All ports: ~p~n", [erlang:ports()]),
        io:format("** Accepted a connection with FD~p~n", [FD]),
        debug_fd(FD),

        ok = inet:setopts(Sock, [
                {send_timeout, 250},
                {active, once}, {nodelay, true}, {sndbuf, 32 * 1024},
{keepalive, true}
        ]),

        fill_write(Sock, FD).

fill_write(Sock, FD) ->
        %% Make sure the output buffer becomes full so we trigger send_timeout.

        Size = 16 * 1024 * 8,        %% 16 KB (k *bytes*).
        case gen_tcp:send(Sock, <<0:Size>>) of
                {error, timeout} ->
                        io:format("** timeout~n", []),
                        debug_fd(FD),
                        io:format("Self=~p, port_info=~p~n", [self(),
erlang:port_info(Sock)]),
                        io:format("** About to exit~n", []),
                        io:format("All ports: ~p~n", [erlang:ports()]),
                        exit(normal),
                        ok;
                {error, X} ->
                        io:format("** Unexpected send error: ~p~n", [X]),
                        debug_fd(FD),
                        io:format("** port_info: ~p~n", [catch
erlang:port_info(Sock, connected)]);
                ok ->
                        timer:sleep(1000),
                        debug_fd(FD),
                        io:format("Self=~p, port_info=~p~n", [self(),
erlang:port_info(Sock)]),
                        %% COMMENT OUT FOLLOWING LINES TO DEMONSTRATE PROBLEM
                                % Exit before send_timeout happens -
this should work
                                exit(normal),
                        %% END COMMENT OUT FOLLOWING LINES TO
DEMONSTRATE PROBLEM
                        fill_write(Sock, FD)
        end.

debug_mbox() ->
        receive X -> io:format("Got message ~p~n", [X]), debug_mbox()
        after 1000 -> ok
        end.

debug_fd(FD) ->
        File = "/proc/" ++ os:getpid() ++ "/fd/" ++ integer_to_list(FD),
        Result = os:cmd("ls -l " ++ File ++ " 2>&1"),
        io:format("FD ~p: ~p~n", [FD, Result]).

connect_and_idle(Port) ->
        {ok, _Sock} = gen_tcp:connect("localhost", Port,
[{active,false}, {mode,binary}, {packet, 0}]),
        timer:sleep(60000),
        io:format("idle reader closing~n", []).

============================================================================================



Normal Exit of Process.

(gdb) bt
#0  tcp_inet_stop (e=0x95e580) at drivers/common/inet_drv.c:
#1  0x0000000000495b87 in terminate_port (prt=0x7ffff679ba98) at beam/io.c:1976
#2  0x0000000000498df8 in erts_do_exit_port (p=0x7ffff679ba98,
from=<value optimized out>, reason=<value optimized out>) at
beam/io.c:2203
#3  0x00000000004aa513 in doit_exit_link (lnk=0x9919f0,
vpcontext=<value optimized out>) at beam/erl_process.c:8611
#4  0x0000000000515057 in erts_sweep_links (root=<value optimized
out>, doit=0x4aa260 <doit_exit_link>, context=0x7ffff4952bf0) at
beam/erl_monitors.c:859
#5  0x00000000004b0a46 in continue_exit_process (p=0x98b8d0,
pix_lock=<value optimized out>) at beam/erl_process.c:9005
#6  0x00000000005399c7 in terminate_proc (c_p=0x98b8d0, pc=<value
optimized out>, reg=<value optimized out>, bf=<value optimized out>)
at beam/beam_emu.c:5524
#7  handle_error (c_p=0x98b8d0, pc=<value optimized out>, reg=<value
optimized out>, bf=<value optimized out>) at beam/beam_emu.c:5399
#8  0x000000000053a6f4 in process_main () at beam/beam_emu.c:2436
#9  0x00000000004b17a9 in sched_thread_func (vesdp=0x7ffff7e5bd40) at
beam/erl_process.c:5217
#10 0x00000000005bbf86 in thr_wrapper (vtwd=0x7fffffffd850) at
pthread/ethread.c:106
#11 0x00000031b4407851 in start_thread () from /lib64/libpthread.so.0
#12 0x00000031b3ce811d in clone () from /lib64/libc.so.6



More information about the erlang-bugs mailing list