aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/gen_tcp_api_SUITE.erl
diff options
context:
space:
mode:
authorRory Byrne <[email protected]>2015-04-15 17:28:09 +0100
committerRory Byrne <[email protected]>2015-05-12 19:55:35 +0100
commit25bd6312491fc9153a16f74b5d1d39609426ae60 (patch)
tree0cfd7f0227302868e2b77b04b803843b87f5eb9f /lib/kernel/test/gen_tcp_api_SUITE.erl
parentce96ab6d64768cd6536011ccdecc08191c238220 (diff)
downloadotp-25bd6312491fc9153a16f74b5d1d39609426ae60.tar.gz
otp-25bd6312491fc9153a16f74b5d1d39609426ae60.tar.bz2
otp-25bd6312491fc9153a16f74b5d1d39609426ae60.zip
Fix gen_tcp:shutdown/2 by making it asynchronous
If the driver queue is empty, or the user is requesting a 'read' shutdown, then the shutdown() syscall is performed synchronously, as per the old version of shutdown/2. However, if the user is requesting a 'write' or 'read_write' shutdown, and there is data in the driver queue for the socket, then the shutdown() syscall is delayed and handled asynchronously when the driver queue is written out. This version of shutdown solves a number of issues with the old version. The two main solutions it offers are: * It doesn't block when the TCP peer is idle or slow. This is the expected behaviour when shutdown() is called: the caller needs to be able to continue reading from the socket, not be prevented from doing so. * It doesn't truncate the output. The current version of gen_tcp:shutdown/2 will truncate any outbound data in the driver queue after about 10 seconds if the TCP peer is idle of slow. Worse yet, it doesn't even inform anyone that the data has been truncated: 'ok' is returned to the caller; and a FIN rather than an RST is sent to the TCP peer. For a detailed description of all the problems with the old version of shutdown, please see the EEP Light that was written to justify this patch.
Diffstat (limited to 'lib/kernel/test/gen_tcp_api_SUITE.erl')
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl32
1 files changed, 30 insertions, 2 deletions
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index c27d265550..4a527e2f51 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -32,6 +32,7 @@
t_connect_bad/1,
t_recv_timeout/1, t_recv_eof/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
+ t_shutdown_async/1,
t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]).
-export([getsockfd/0,closesockfd/1]).
@@ -41,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
- t_fdopen, t_fdconnect, t_implicit_inet6].
+ t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6].
groups() ->
[{t_accept, [], [t_accept_timeout]},
@@ -155,7 +156,34 @@ t_shutdown_error(Config) when is_list(Config) ->
?line ok = gen_tcp:close(L),
?line {error, closed} = gen_tcp:shutdown(L, read_write),
ok.
-
+
+t_shutdown_async(Config) when is_list(Config) ->
+ ?line {OS, _} = os:type(),
+ ?line {ok, L} = gen_tcp:listen(0, [{sndbuf, 4096}]),
+ ?line {ok, Port} = inet:port(L),
+ ?line {ok, Client} = gen_tcp:connect(localhost, Port,
+ [{recbuf, 4096},
+ {active, false}]),
+ ?line {ok, S} = gen_tcp:accept(L),
+ ?line PayloadSize = 1024 * 1024,
+ ?line Payload = lists:duplicate(PayloadSize, $.),
+ ?line ok = gen_tcp:send(S, Payload),
+ ?line case erlang:port_info(S, queue_size) of
+ {queue_size, N} when N > 0 -> ok;
+ {queue_size, 0} when OS =:= win32 -> ok;
+ {queue_size, 0} = T -> ?t:fail({unexpected, T})
+ end,
+
+ ?line ok = gen_tcp:shutdown(S, write),
+ ?line {ok, Buf} = gen_tcp:recv(Client, PayloadSize),
+ ?line {error, closed} = gen_tcp:recv(Client, 0),
+ ?line case length(Buf) of
+ PayloadSize -> ok;
+ Sz -> ?t:fail({payload_size,
+ {expected, PayloadSize},
+ {received, Sz}})
+ end.
+
%%% gen_tcp:fdopen/2