diff options
author | Steve Vinoski <[email protected]> | 2013-08-27 11:42:00 -0400 |
---|---|---|
committer | Lukas Larsson <[email protected]> | 2013-09-23 17:23:14 +0200 |
commit | 7fbf2c26ac063988818230a0e18a9df48c2fbf2d (patch) | |
tree | 8ee574a718eb61d7d99133e47a2a37bc1d94e115 /lib | |
parent | cc3c34fce28062afcaadc288b69939ced8fd8cde (diff) | |
download | otp-7fbf2c26ac063988818230a0e18a9df48c2fbf2d.tar.gz otp-7fbf2c26ac063988818230a0e18a9df48c2fbf2d.tar.bz2 otp-7fbf2c26ac063988818230a0e18a9df48c2fbf2d.zip |
add {active,N} socket option for TCP, UDP, and SCTP
Add the {active,N} socket option, where N is an integer in the range
-32768..32767, to allow a caller to specify the number of data messages to
be delivered to the controlling process. Once the socket's delivered
message count either reaches 0 or is explicitly set to 0 with
inet:setopts/2 or by including {active,0} as an option when the socket is
created, the socket transitions to passive ({active, false}) mode and the
socket's controlling process receives a message to inform it of the
transition. TCP sockets receive {tcp_passive,Socket}, UDP sockets receive
{udp_passive,Socket} and SCTP sockets receive {sctp_passive,Socket}.
The socket's delivered message counter defaults to 0, but it can be set
using {active,N} via any gen_tcp, gen_udp, or gen_sctp function that takes
socket options as arguments, or via inet:setopts/2. New N values are added
to the socket's current counter value, and negative numbers can be used to
reduce the counter value. Specifying a number that would cause the socket's
counter value to go above 32767 causes an einval error. If a negative
number is specified such that the counter value would become negative, the
socket's counter value is set to 0 and the socket transitions to passive
mode. If the counter value is already 0 and inet:setopts(Socket,
[{active,0}]) is specified, the counter value remains at 0 but the
appropriate passive mode transition message is generated for the socket.
This commit contains a modified preloaded prim_inet.beam due to changes in
prim_inet.erl.
Add tests for {active,N} mode for TCP, UDP, and SCTP sockets.
Add documentation for {active,N} mode for inet, gen_tcp, gen_udp, and
gen_sctp.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kernel/doc/src/gen_sctp.xml | 31 | ||||
-rw-r--r-- | lib/kernel/doc/src/gen_tcp.xml | 6 | ||||
-rw-r--r-- | lib/kernel/doc/src/gen_udp.xml | 17 | ||||
-rw-r--r-- | lib/kernel/doc/src/inet.xml | 51 | ||||
-rw-r--r-- | lib/kernel/src/gen_sctp.erl | 2 | ||||
-rw-r--r-- | lib/kernel/src/gen_tcp.erl | 2 | ||||
-rw-r--r-- | lib/kernel/src/gen_udp.erl | 2 | ||||
-rw-r--r-- | lib/kernel/src/inet.erl | 14 | ||||
-rw-r--r-- | lib/kernel/src/inet_int.hrl | 1 | ||||
-rw-r--r-- | lib/kernel/test/gen_sctp_SUITE.erl | 125 | ||||
-rw-r--r-- | lib/kernel/test/gen_tcp_misc_SUITE.erl | 114 | ||||
-rw-r--r-- | lib/kernel/test/gen_udp_SUITE.erl | 106 |
12 files changed, 432 insertions, 39 deletions
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index dacea7a239..986260638d 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -496,9 +496,11 @@ orthogonal to the sets of TCP, UDP and generic INET options: only those options which are explicitly listed below are allowed for SCTP sockets. Options can be set on the socket using - <c>gen_sctp:open/1,2</c> or <c>inet:setopts/2</c>, - retrieved using <c>inet:getopts/2</c>, and when calling - <c>gen_sctp:connect/4,5</c> options can be changed.</p> + <seealso marker="#open/1"><c>gen_sctp:open/1,2</c></seealso> + or <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>, + retrieved using <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>, + and when calling <seealso marker="#connect/4"><c>gen_sctp:connect/4,5</c></seealso> + options can be changed.</p> <marker id="option-binary"></marker> <marker id="option-list"></marker> <taglist> @@ -507,7 +509,7 @@ <p>Determines the type of data returned from <c>gen_sctp:recv/1,2</c>.</p> <marker id="option-active"></marker> </item> - <tag><c>{active, true|false|once}</c></tag> + <tag><c>{active, true|false|once|N}</c></tag> <item> <list type="bulleted"> <item> @@ -524,11 +526,28 @@ </item> <item> <p>If <c>once</c>, only one message is automatically placed - in the message queue, after that the mode is automatically - re-set to passive. This provides flow control as well as + in the message queue, and after that the mode is automatically + reset to passive. This provides flow control as well as the possibility for the receiver to listen for its incoming SCTP data interleaved with other inter-process messages.</p> </item> + <item> + <p>If <c>active</c> is specified as an integer <c>N</c> in the + range -32768 to 32767 (inclusive), then that number is added to + the socket's count of the number of data messages to be + delivered to the controlling process. If the result of the + addition would be negative, the count is set to 0. Once the + count reaches 0, either through the delivery of messages or by + being explicitly set with <seealso + marker="inet#setopts/2">inet:setopts/2</seealso>, the socket's + mode is automatically reset to passive (<c>{active, + false}</c>) mode. When a socket in this active mode transitions to + passive mode, the message <c>{sctp_passive, Socket}</c> is sent + to the controlling process to notify it that if it wants to + receive more data messages from the socket, it must call + <seealso marker="inet#setopts/2">inet:setopts/2</seealso> to set + the socket back into an active mode.</p> + </item> </list> </item> <tag><c>{tos, integer()}</c></tag> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 7afd73f4bf..dbd0d3c815 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -148,6 +148,12 @@ do_recv(Sock, Bs) -> as messages:</p> <code type="none"> {tcp, Socket, Data}</code> + <p>If the socket is in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2"> + inet:setopts/2</seealso> for details) and its message counter + drops to 0, the following message is delivered to indicate that the + socket has transitioned to passive (<c>{active, false}</c>) mode:</p> + <code type="none"> +{tcp_passive, Socket}</code> <p>If the socket is closed, the following message is delivered:</p> <code type="none"> {tcp_closed, Socket}</code> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index faca31e928..503725fe18 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -145,14 +145,23 @@ <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> </item> </taglist> - <p>The returned socket <c><anno>Socket</anno></c> is used to send packets - from this port with <c>send/4</c>. When UDP packets arrive at - the opened port, they are delivered as messages:</p> + <p>The returned socket <c><anno>Socket</anno></c> is used to send + packets from this port with <c>send/4</c>. When UDP packets arrive + at the opened port, if the socket is in an active mode the packets + are delivered as messages to the controlling process:</p> <code type="none"> {udp, Socket, IP, InPortNo, Packet}</code> - <p>Note that arriving UDP packets that are longer than + <p>If the socket is not in an active mode, data can be + retrieved via the <seealso marker="#recv/2">recv/2,3</seealso> calls. + Note that arriving UDP packets that are longer than the receive buffer option specifies, might be truncated without warning.</p> + <p>When a socket in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2"> + inet:setopts/2</seealso> for details) transitions to passive + (<c>{active, false}</c>) mode, the controlling process is notified by a + message of the following form:</p> + <code type="none"> +{udp_passive, Socket}</code> <p><c>IP</c> and <c>InPortNo</c> define the address from which <c>Packet</c> came. <c>Packet</c> is a list of bytes if the option <c>list</c> was specified. <c>Packet</c> is a diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index e1b0b34888..fb8bf32978 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -456,47 +456,66 @@ fe80::204:acff:fe17:bf38 <p>Sets one or more options for a socket. The following options are available:</p> <taglist> - <tag><c>{active, true | false | once}</c></tag> + <tag><c>{active, true | false | once | N}</c></tag> <item> <p>If the value is <c>true</c>, which is the default, everything received from the socket will be sent as messages to the receiving process. If the value is <c>false</c> (passive mode), the process must explicitly - receive incoming data by calling <c>gen_tcp:recv/2,3</c> - or <c>gen_udp:recv/2,3</c> (depending on the type of - socket).</p> + receive incoming data by calling + <seealso marker="gen_tcp#recv/2"><c>gen_tcp:recv/2,3</c></seealso>, + <seealso marker="gen_udp#recv/2"><c>gen_udp:recv/2,3</c></seealso> + or <seealso marker="gen_sctp#recv/1"><c>gen_sctp:recv/1,2</c></seealso> + (depending on the type of socket).</p> <p>If the value is <c>once</c> (<c>{active, once}</c>), <em>one</em> data message from the socket will be sent to the process. To receive one more message, <c>setopts/2</c> must be called again with the <c>{active, once}</c> option.</p> - <p>When using <c>{active, once}</c>, the socket changes - behaviour automatically when data is received. This can - sometimes be confusing in combination with connection - oriented sockets (i.e. <c>gen_tcp</c>) as a socket with - <c>{active, false}</c> behaviour reports closing + <p>If the value is an integer <c>N</c> in the range -32768 to 32767 + (inclusive), the value is added to the socket's count of data + messages sent to the controlling process. A socket's default + message count is 0. If a negative value is specified and its + magnitude is equal to or greater than the socket's current + message count, the socket's message count is set to 0. Once + the socket's message count reaches 0, either due to sending + received data messages to the process or by being explicitly set, + the process is then notified by a special message, specific to + the type of socket, that the socket has entered passive + mode. Once the socket enters passive mode, to receive more + messages <c>setopts/2</c> must be called again to set the + socket back into an active mode.</p> + <p>When using <c>{active, once}</c> or <c>{active, N}</c>, the + socket changes behaviour automatically when data is received. + This can sometimes be confusing in combination with + connection-oriented sockets (i.e. <c>gen_tcp</c>) as a socket + with <c>{active, false}</c> behaviour reports closing differently than a socket with <c>{active, true}</c> behaviour. To make programming easier, a socket where the peer closed and this was detected while in <c>{active, false}</c> mode, will still generate the message - <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c> or <c>{active, true}</c> mode. It is therefore + <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c>, + <c>{active, true}</c> or <c>{active, N}</c> mode. It is therefore safe to assume that the message <c>{tcp_closed,Socket}</c>, possibly followed by socket port termination (depending on the <c>exit_on_close</c> option) will eventually appear when a socket changes back and forth between <c>{active, true}</c> and - <c>{active, false}</c> mode. However, + <c>{active, false}</c> mode. However, <em>when</em> peer closing is detected is all up to the underlying TCP/IP stack and protocol.</p> - <p>Note that <c>{active,true}</c> mode provides no flow + <p>Note that <c>{active, true}</c> mode provides no flow control; a fast sender could easily overflow the - receiver with incoming messages. Use active mode only if + receiver with incoming messages. The same is true of + <c>{active, N}</c> mode while the message count is greater + than zero. Use active mode only if your high-level protocol provides its own flow control (for instance, acknowledging received messages) or the - amount of data exchanged is small. <c>{active,false}</c> - mode or use of the <c>{active, once}</c> mode provides - flow control; the other side will not be able send + amount of data exchanged is small. <c>{active, false}</c> + mode, use of the <c>{active, once}</c> mode or <c>{active, N}</c> + mode with values of <c>N</c> appropriate for the application + provides flow control; the other side will not be able send faster than the receiver can read.</p> </item> diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index 0d005eb651..23a698f9b2 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -36,7 +36,7 @@ -type assoc_id() :: term(). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {dontroute, boolean()} | {high_msgq_watermark, pos_integer()} | diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 96026aa428..bc8ffbe5e3 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -30,7 +30,7 @@ -include("file.hrl"). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {delay_send, boolean()} | {deliver, port | term} | diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index e82b11d2ef..70dceb3679 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -26,7 +26,7 @@ -include("inet_int.hrl"). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {add_membership, {inet:ip_address(), inet:ip_address()}} | {broadcast, boolean()} | {buffer, non_neg_integer()} | diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9cfd3819f0..7261d6486f 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -667,6 +667,9 @@ con_opt([Opt | Opts], R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#connect_opts.opts), + con_opt(Opts, R#connect_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> con_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -733,6 +736,9 @@ list_opt([Opt | Opts], R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#listen_opts.opts), + list_opt(Opts, R#listen_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> list_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -787,6 +793,9 @@ udp_opt([Opt | Opts], R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#udp_opts.opts), + udp_opt(Opts, R#udp_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -805,7 +814,7 @@ udp_add(Name, Val, R, Opts, As) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Currently supported options include: % (*) {mode, list|binary} or just list|binary -% (*) {active, true|false|once} +% (*) {active, true|false|once|N} % (*) {sctp_module, inet_sctp|inet6_sctp} or just inet|inet6 % (*) options set via setsockopt. % The full list is below in sctp_options/0 . @@ -867,6 +876,9 @@ sctp_opt([Opt|Opts], Mod, R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#sctp_opts.opts), + sctp_opt(Opts, Mod, R#sctp_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val); _ -> {error,badarg} end; diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index 18a4a61b2f..024a5fd3b6 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -46,6 +46,7 @@ -define(INET_PASSIVE, 0). -define(INET_ACTIVE, 1). -define(INET_ONCE, 2). % Active once then passive +-define(INET_MULTI, 3). % Active N then passive %% state codes (getstatus, INET_REQ_GETSTATUS) -define(INET_F_OPEN, 16#0001). diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index e89cb44797..6dad52395b 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -35,8 +35,9 @@ open_unihoming_ipv6_socket/1, open_multihoming_ipv6_socket/1, open_multihoming_ipv4_and_ipv6_socket/1, - basic_stream/1, xfer_stream_min/1, peeloff_active_once/1, - peeloff_active_true/1, buffers/1]). + basic_stream/1, xfer_stream_min/1, active_n/1, + peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1, + buffers/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -46,9 +47,9 @@ all() -> open_multihoming_ipv4_socket, open_unihoming_ipv6_socket, open_multihoming_ipv6_socket, - open_multihoming_ipv4_and_ipv6_socket, + open_multihoming_ipv4_and_ipv6_socket, active_n, basic_stream, xfer_stream_min, peeloff_active_once, - peeloff_active_true, buffers]. + peeloff_active_true, peeloff_active_n, buffers]. groups() -> []. @@ -767,6 +768,106 @@ implicit_inet6(S1, Addr) -> end, ?line ok = gen_sctp:close(S2). +active_n(doc) -> + "Verify {active,N} socket management"; +active_n(suite) -> + []; +active_n(Config) when is_list(Config) -> + N = 3, + S1 = ok(gen_sctp:open([{active,N}])), + [{active,N}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,-N}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,0}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + ok = inet:setopts(S1, [{active,32767}]), + {error,einval} = inet:setopts(S1, [{active,1}]), + {error,einval} = inet:setopts(S1, [{active,-32769}]), + ok = inet:setopts(S1, [{active,-32768}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,true}]), + [{active,true}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,once}]), + [{active,once}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(S1, [{active,32768}]), + ok = inet:setopts(S1, [{active,false}]), + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = gen_sctp:listen(S1, true), + S1Port = ok(inet:port(S1)), + S2 = ok(gen_sctp:open(0, [{active,false}])), + Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])), + ok = inet:setopts(S1, [{active,N}]), + [{active,N}] = ok(inet:getopts(S1, [active])), + LoopFun = fun(Count, Count, _Fn) -> + receive + {sctp_passive,S1} -> + ok + after + 5000 -> + exit({error,timeout}) + end; + (I, Count, Fn) -> + Msg = list_to_binary("message "++integer_to_list(I)), + ok = gen_sctp:send(S2, Assoc, 0, Msg), + receive + {sctp,S1,_,_,{[SR],Msg}} when is_record(SR, sctp_sndrcvinfo) -> + Fn(I+1, Count, Fn); + {sctp,S1,_,_,_} -> + %% ignore non-data messages + ok = inet:setopts(S1, [{active,1}]), + Fn(I, Count, Fn); + Other -> + exit({unexpected, Other}) + after + 5000 -> + exit({error,timeout}) + end + end, + ok = LoopFun(1, N, LoopFun), + S3 = ok(gen_sctp:open([{active,0}])), + receive + {sctp_passive,S3} -> + [{active,false}] = ok(inet:getopts(S3, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + ok = gen_sctp:close(S3), + ok = gen_sctp:close(S2), + ok = gen_sctp:close(S1), + ok. + basic_stream(doc) -> "Hello world stream socket"; basic_stream(suite) -> @@ -941,6 +1042,14 @@ peeloff_active_true(suite) -> peeloff_active_true(Config) -> peeloff(Config, [{active,true}]). +peeloff_active_n(doc) -> + "Peel off an SCTP stream socket ({active,N})"; +peeloff_active_n(suite) -> + []; + +peeloff_active_n(Config) -> + peeloff(Config, [{active,1}]). + peeloff(Config, SockOpts) when is_list(Config) -> ?line Addr = {127,0,0,1}, ?line Stream = 0, @@ -1519,7 +1628,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> end. again(Socket) -> - inet:setopts(Socket, [{active,once}]). + receive + {sctp_passive,Socket} -> + [{active, false}] = ok(inet:getopts(Socket, [active])), + ok = inet:setopts(Socket,[{active,1}]) + after 0 -> + ok = inet:setopts(Socket, [{active,once}]) + end. gb_push(Key, Val, GBT) -> case gb_trees:lookup(Key, GBT) of diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index ee271fbdfa..ee8bfcceb1 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -25,7 +25,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, - no_accept/1, close_with_pending_output/1, + no_accept/1, close_with_pending_output/1, active_n/1, data_before_close/1, iter_max_socks/1, get_status/1, passive_sockets/1, accept_closed_by_other_process/1, init_per_testcase/2, end_per_testcase/2, @@ -70,7 +70,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, - iter_max_socks, passive_sockets, + iter_max_socks, passive_sockets, active_n, accept_closed_by_other_process, otp_3924, closed_socket, shutdown_active, shutdown_passive, shutdown_pending, default_options, http_bad_packet, busy_send, @@ -407,6 +407,114 @@ send_loop(Sock, Data, Left) -> ok = gen_tcp:send(Sock, Data), send_loop(Sock, Data, Left-1). +%% Test {active,N} option +active_n(doc) -> + ["Verify operation of the {active,N} option."]; +active_n(suite) -> []; +active_n(Config) when is_list(Config) -> + N = 3, + LS = ok(gen_tcp:listen(0, [{active,N}])), + [{active,N}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,-N}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,0}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + ok = inet:setopts(LS, [{active,32767}]), + {error,einval} = inet:setopts(LS, [{active,1}]), + {error,einval} = inet:setopts(LS, [{active,-32769}]), + ok = inet:setopts(LS, [{active,-32768}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,N}]), + ok = inet:setopts(LS, [{active,true}]), + [{active,true}] = ok(inet:getopts(LS, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(LS, [{active,N}]), + ok = inet:setopts(LS, [{active,once}]), + [{active,once}] = ok(inet:getopts(LS, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(LS, [{active,32768}]), + ok = inet:setopts(LS, [{active,false}]), + [{active,false}] = ok(inet:getopts(LS, [active])), + Port = ok(inet:port(LS)), + C = ok(gen_tcp:connect("localhost", Port, [{active,N}])), + [{active,N}] = ok(inet:getopts(C, [active])), + S = ok(gen_tcp:accept(LS)), + ok = inet:setopts(S, [{active,N}]), + [{active,N}] = ok(inet:getopts(S, [active])), + repeat(3, + fun(I) -> + Msg = "message "++integer_to_list(I), + ok = gen_tcp:send(C, Msg), + receive + {tcp,S,Msg} -> + ok = gen_tcp:send(S, Msg) + after + 5000 -> + exit({error,timeout}) + end, + receive + {tcp,C,Msg} -> + ok + after + 5000 -> + exit({error,timeout}) + end + end), + receive + {tcp_passive,S} -> + [{active,false}] = ok(inet:getopts(S, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + receive + {tcp_passive,C} -> + [{active,false}] = ok(inet:getopts(C, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + LS2 = ok(gen_tcp:listen(0, [{active,0}])), + receive + {tcp_passive,LS2} -> + [{active,false}] = ok(inet:getopts(LS2, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + ok = gen_tcp:close(LS2), + ok = gen_tcp:close(C), + ok = gen_tcp:close(S), + ok = gen_tcp:close(LS), + ok. + -define(OTP_3924_MAX_DELAY, 100). %% Taken out of the blue, but on intra host connections %% I expect propagation of a close to be quite fast @@ -2659,3 +2767,5 @@ oct_aloop(S,X,Times) -> gen_tcp:close(S), closed end. + +ok({ok,V}) -> V. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 4cc6018614..6bb41999c5 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -33,7 +33,7 @@ init_per_group/2,end_per_group/2]). -export([init_per_testcase/2, end_per_testcase/2]). --export([send_to_closed/1, +-export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1]). @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [send_to_closed, buffer_size, binary_passive_recv, bad_address, read_packets, open_fd, connect, - implicit_inet6]. + implicit_inet6, active_n]. groups() -> []. @@ -465,6 +465,108 @@ open_fd(Config) when is_list(Config) -> ?t:fail(io_lib:format("~w", [flush()])) end. +active_n(Config) when is_list(Config) -> + N = 3, + S1 = ok(gen_udp:open(0, [{active,N}])), + [{active,N}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,-N}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,0}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + ok = inet:setopts(S1, [{active,32767}]), + {error,einval} = inet:setopts(S1, [{active,1}]), + {error,einval} = inet:setopts(S1, [{active,-32769}]), + ok = inet:setopts(S1, [{active,-32768}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,true}]), + [{active,true}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,once}]), + [{active,once}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(S1, [{active,32768}]), + ok = inet:setopts(S1, [{active,false}]), + [{active,false}] = ok(inet:getopts(S1, [active])), + S1Port = ok(inet:port(S1)), + S2 = ok(gen_udp:open(0, [{active,N}])), + S2Port = ok(inet:port(S2)), + [{active,N}] = ok(inet:getopts(S2, [active])), + ok = inet:setopts(S1, [{active,N}]), + [{active,N}] = ok(inet:getopts(S1, [active])), + lists:foreach( + fun(I) -> + Msg = "message "++integer_to_list(I), + ok = gen_udp:send(S2, "localhost", S1Port, Msg), + receive + {udp,S1,_,S2Port,Msg} -> + ok = gen_udp:send(S1, "localhost", S2Port, Msg) + after + 5000 -> + exit({error,timeout}) + end, + receive + {udp,S2,_,S1Port,Msg} -> + ok + after + 5000 -> + exit({error,timeout}) + end + end, lists:seq(1,N)), + receive + {udp_passive,S1} -> + [{active,false}] = ok(inet:getopts(S1, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + receive + {udp_passive,S2} -> + [{active,false}] = ok(inet:getopts(S2, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + S3 = ok(gen_udp:open(0, [{active,0}])), + receive + {udp_passive,S3} -> + [{active,false}] = ok(inet:getopts(S3, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + ok = gen_udp:close(S3), + ok = gen_udp:close(S2), + ok = gen_udp:close(S1), + ok. % % Utils |