aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn-Egil Dahlberg <[email protected]>2012-03-19 16:25:39 +0100
committerBjörn-Egil Dahlberg <[email protected]>2012-03-19 16:25:39 +0100
commit68ba8d70deb1549dd37a8325eea686c9908f926b (patch)
tree50fbabbf3cb2a52b10fc9a629195baf22b8d17ca
parent662e3ffeed74d9121315d86af46430dd71e01709 (diff)
parent65ba37d1371f36a1666a53c57ac42c73692b8096 (diff)
downloadotp-68ba8d70deb1549dd37a8325eea686c9908f926b.tar.gz
otp-68ba8d70deb1549dd37a8325eea686c9908f926b.tar.bz2
otp-68ba8d70deb1549dd37a8325eea686c9908f926b.zip
Merge branch 'ph/fix-enfile/OTP-9990' into maint
* ph/fix-enfile/OTP-9990: Update preloaded prim_inet.beam gen_tcp: Use correct specs for accept and listen prim_inet: Catch system_limit in open_port Fix returned error from gen_tcp:accept/1,2 when running out of ports
-rw-r--r--erts/emulator/beam/io.c2
-rw-r--r--erts/emulator/drivers/common/inet_drv.c38
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin69980 -> 70100 bytes
-rw-r--r--erts/preloaded/src/prim_inet.erl6
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml4
-rw-r--r--lib/kernel/doc/src/inet.xml2
-rw-r--r--lib/kernel/src/gen_tcp.erl6
-rw-r--r--lib/kernel/src/inet.erl6
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl53
9 files changed, 89 insertions, 28 deletions
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index b23b1f628d..fe1a7ba345 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -743,7 +743,7 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
return (ErlDrvTermData) -1; /* pid does not exist */
}
if ((port_num = get_free_port()) < 0) {
- errno = ENFILE;
+ errno = SYSTEM_LIMIT;
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
erts_smp_mtx_unlock(&erts_driver_list_lock);
return (ErlDrvTermData) -1;
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 47a99fdbe6..060d72199d 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -553,6 +553,12 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
# define VALGRIND_MAKE_MEM_DEFINED(ptr,size)
#endif
+/*
+ Magic errno value used locally for return of {error, system_limit}
+ - the emulator definition of SYSTEM_LIMIT is not available here.
+*/
+#define INET_ERRNO_SYSTEM_LIMIT (15 << 8)
+
/*----------------------------------------------------------------------------
** Interface constants.
**
@@ -1645,6 +1651,17 @@ static struct erl_drv_entry dummy_sctp_driver_entry =
#endif
+/* return lowercase string form of errno value */
+static char *errno_str(int err)
+{
+ switch (err) {
+ case INET_ERRNO_SYSTEM_LIMIT:
+ return "system_limit";
+ default:
+ return erl_errno_id(err);
+ }
+}
+
/* general control reply function */
static ErlDrvSSizeT ctl_reply(int rep, char* buf, ErlDrvSizeT len,
char** rbuf, ErlDrvSizeT rsize)
@@ -1665,13 +1682,9 @@ static ErlDrvSSizeT ctl_reply(int rep, char* buf, ErlDrvSizeT len,
/* general control error reply function */
static ErlDrvSSizeT ctl_error(int err, char** rbuf, ErlDrvSizeT rsize)
{
- char response[256]; /* Response buffer. */
- char* s;
- char* t;
+ char* s = errno_str(err);
- for (s = erl_errno_id(err), t = response; *s; s++, t++)
- *t = tolower(*s);
- return ctl_reply(INET_REP_ERROR, response, t-response, rbuf, rsize);
+ return ctl_reply(INET_REP_ERROR, s, strlen(s), rbuf, rsize);
}
static ErlDrvSSizeT ctl_xerror(char* xerr, char** rbuf, ErlDrvSizeT rsize)
@@ -1683,14 +1696,7 @@ static ErlDrvSSizeT ctl_xerror(char* xerr, char** rbuf, ErlDrvSizeT rsize)
static ErlDrvTermData error_atom(int err)
{
- char errstr[256];
- char* s;
- char* t;
-
- for (s = erl_errno_id(err), t = errstr; *s; s++, t++)
- *t = tolower(*s);
- *t = '\0';
- return driver_mk_atom(errstr);
+ return driver_mk_atom(errno_str(err));
}
@@ -8051,7 +8057,7 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args)
/* Copy a descriptor, by creating a new port with same settings
* as the descriptor desc.
- * return NULL on error (ENFILE no ports avail)
+ * return NULL on error (SYSTEM_LIMIT no ports avail)
*/
static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
ErlDrvTermData owner, int* err)
@@ -8090,7 +8096,7 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
/* The new port will be linked and connected to the original caller */
port = driver_create_port(port, owner, "tcp_inet", (ErlDrvData) copy_desc);
if ((long)port == -1) {
- *err = ENFILE;
+ *err = INET_ERRNO_SYSTEM_LIMIT;
FREE(copy_desc);
return NULL;
}
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index f382236af7..b2f3ab6c5b 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 0cedd284db..14bf3b6c69 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -83,8 +83,10 @@ open(Protocol, Family, Type, Req, Data) ->
end
catch
%% The only (?) way to get here is to try to open
- %% the sctp driver when it does not exist
- error:badarg -> {error,eprotonosupport}
+ %% the sctp driver when it does not exist (badarg)
+ error:badarg -> {error, eprotonosupport};
+ %% system_limit if out of port slots
+ error:system_limit -> {error, system_limit}
end.
enc_family(inet) -> ?INET_AF_INET;
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) ->
<p>Returns <c>{ok, <anno>Socket</anno>}</c> if a connection is established,
or <c>{error, closed}</c> if <c><anno>ListenSocket</anno></c> is closed,
or <c>{error, timeout}</c> if no connection is established
- within the specified time. May also return a POSIX error
+ within the specified time,
+ or <c>{error, system_limit}</c> 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.</p>
<p>Packets can be sent to the returned socket <c><anno>Socket</anno></c>
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
<fsummary>Return a descriptive string for an error reason</fsummary>
<desc>
<p>Returns a diagnostic error string. See the section below
- for possible <c><anno>Posix</anno></c> values and the corresponding
+ for possible Posix values and the corresponding
strings.</p>
</desc>
</func>
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 4d6c7f5f1d..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),
@@ -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
@@ -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
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) ->
[];