From 949ee23d72e4412fed3059c04e395f995388a5fb Mon Sep 17 00:00:00 2001 From: Per Hedeland Date: Wed, 4 Jan 2012 10:57:59 +0100 Subject: Fix returned error from gen_tcp:accept/1,2 when running out of ports The {error, enfile} return value is badly misleading and confusing for this case, since the Posix ENFILE errno value has a well-defined meaning that has nothing to do with Erlang ports. The fix changes the return value to {error, system_limit}, which is consistent with e.g. various file(3) functions. inet:format_error/1 has also been updated to support system_limit in the same manner as file:format_error/1. --- lib/kernel/doc/src/gen_tcp.xml | 4 ++- lib/kernel/doc/src/inet.xml | 2 +- lib/kernel/src/gen_tcp.erl | 2 +- lib/kernel/src/inet.erl | 6 ++-- lib/kernel/test/gen_tcp_misc_SUITE.erl | 53 ++++++++++++++++++++++++++++++++-- 5 files changed, 60 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 8a5d40bb16..9f362841ce 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -235,7 +235,9 @@ do_recv(Sock, Bs) ->

Returns {ok, Socket} if a connection is established, or {error, closed} if ListenSocket is closed, or {error, timeout} if no connection is established - within the specified time. May also return a POSIX error + within the specified time, + or {error, system_limit} if all available ports in the + Erlang emulator are in use. May also return a POSIX error value if something else goes wrong, see inet(3) for possible error values.

Packets can be sent to the returned socket Socket diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 1a05b4ba99..86950b3ecc 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -149,7 +149,7 @@ fe80::204:acff:fe17:bf38 Return a descriptive string for an error reason

Returns a diagnostic error string. See the section below - for possible Posix values and the corresponding + for possible Posix values and the corresponding strings.

diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 4d6c7f5f1d..02a6190034 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -194,7 +194,7 @@ listen(Port, Opts) -> -spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when ListenSocket :: socket(), Socket :: socket(), - Reason :: closed | timeout | inet:posix(). + Reason :: closed | timeout | system_limit | inet:posix(). accept(S) -> case inet_db:lookup_socket(S) of diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 49f64a9236..4de3c1c6b1 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1218,11 +1218,13 @@ port_list(Name) -> %% utils %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec format_error(Posix) -> string() when - Posix :: posix(). +-spec format_error(Reason) -> string() when + Reason :: posix() | system_limit. format_error(exbadport) -> "invalid port state"; format_error(exbadseq) -> "bad command sequence"; +format_error(system_limit) -> + "a system limit was hit, probably not enough ports"; format_error(Tag) -> erl_posix_msg:message(Tag). diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 3da4b07c05..d9bba0dbb0 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -39,7 +39,8 @@ accept_timeouts_in_order/1,accept_timeouts_in_order2/1, accept_timeouts_in_order3/1,accept_timeouts_mixed/1, killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1, - several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, send_timeout_active/1, + several_accepts_in_one_go/1, accept_system_limit/1, + active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, otp_9389/1]). @@ -71,7 +72,7 @@ all() -> accept_timeouts_in_order, accept_timeouts_in_order2, accept_timeouts_in_order3, accept_timeouts_mixed, killing_acceptor, killing_multi_acceptors, - killing_multi_acceptors2, several_accepts_in_one_go, + killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit, active_once_closed, send_timeout, send_timeout_active, otp_7731, zombie_sockets, otp_7816, otp_8102, otp_9389]. @@ -1837,6 +1838,54 @@ wait_until_accepting(Proc,N) -> end. +accept_system_limit(suite) -> + []; +accept_system_limit(doc) -> + ["Check that accept returns {error, system_limit} " + "(and not {error, enfile}) when running out of ports"]; +accept_system_limit(Config) when is_list(Config) -> + ?line {ok, LS} = gen_tcp:listen(0, []), + ?line {ok, TcpPort} = inet:port(LS), + ?line Connector = spawn_link(fun () -> connector(TcpPort) end), + ?line ok = acceptor(LS, false, []), + ?line Connector ! stop, + ok. + +acceptor(LS, GotSL, A) -> + case gen_tcp:accept(LS, 1000) of + {ok, S} -> + acceptor(LS, GotSL, [S|A]); + {error, system_limit} -> + acceptor(LS, true, A); + {error, timeout} when GotSL -> + ok; + {error, timeout} -> + error + end. + +connector(TcpPort) -> + ManyPorts = open_ports([]), + ConnF = fun (Port) -> + case catch gen_tcp:connect({127,0,0,1}, TcpPort, []) of + {ok, Sock} -> + Sock; + _Error -> + port_close(Port) + end + end, + R = [ConnF(Port) || Port <- lists:sublist(ManyPorts, 10)], + receive stop -> R end. + +open_ports(L) -> + case catch open_port({spawn_driver, "ram_file_drv"}, []) of + Port when is_port(Port) -> + open_ports([Port|L]); + {'EXIT', {system_limit, _}} -> + {L1, L2} = lists:split(5, L), + [port_close(Port) || Port <- L1], + L2 + end. + active_once_closed(suite) -> []; -- cgit v1.2.3 From 2c1d754fe6aa407f650e6cf6521795fc529a43c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 16 Mar 2012 12:00:02 +0100 Subject: gen_tcp: Use correct specs for accept and listen --- lib/kernel/src/gen_tcp.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 02a6190034..56d0451e44 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -175,7 +175,7 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> Port :: inet:port_number(), Options :: [listen_option()], ListenSocket :: socket(), - Reason :: inet:posix(). + Reason :: system_limit | inet:posix(). listen(Port, Opts) -> Mod = mod(Opts, undefined), @@ -208,7 +208,7 @@ accept(S) -> ListenSocket :: socket(), Timeout :: timeout(), Socket :: socket(), - Reason :: closed | timeout | inet:posix(). + Reason :: closed | timeout | system_limit | inet:posix(). accept(S, Time) when is_port(S) -> case inet_db:lookup_socket(S) of -- cgit v1.2.3