diff options
author | Rory Byrne <[email protected]> | 2015-04-15 17:28:09 +0100 |
---|---|---|
committer | Rory Byrne <[email protected]> | 2015-05-12 19:55:35 +0100 |
commit | 25bd6312491fc9153a16f74b5d1d39609426ae60 (patch) | |
tree | 0cfd7f0227302868e2b77b04b803843b87f5eb9f /erts/preloaded/src | |
parent | ce96ab6d64768cd6536011ccdecc08191c238220 (diff) | |
download | otp-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 'erts/preloaded/src')
-rw-r--r-- | erts/preloaded/src/prim_inet.erl | 21 |
1 files changed, 1 insertions, 20 deletions
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 79ff013c77..622e1be869 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -127,37 +127,18 @@ drv2protocol(_) -> undefined. %% TODO: shutdown equivalent for SCTP %% shutdown(S, read) when is_port(S) -> - shutdown_2(S, 0); + shutdown_1(S, 0); shutdown(S, write) when is_port(S) -> shutdown_1(S, 1); shutdown(S, read_write) when is_port(S) -> shutdown_1(S, 2). shutdown_1(S, How) -> - case subscribe(S, [subs_empty_out_q]) of - {ok,[{subs_empty_out_q,N}]} when N > 0 -> - shutdown_pend_loop(S, N); %% wait for pending output to be sent - _Other -> ok - end, - shutdown_2(S, How). - -shutdown_2(S, How) -> case ctl_cmd(S, ?TCP_REQ_SHUTDOWN, [How]) of {ok, []} -> ok; {error,_}=Error -> Error end. -shutdown_pend_loop(S, N0) -> - receive - {empty_out_q,S} -> ok - after ?INET_CLOSE_TIMEOUT -> - case getstat(S, [send_pend]) of - {ok,[{send_pend,N0}]} -> ok; - {ok,[{send_pend,N}]} -> shutdown_pend_loop(S, N); - _ -> ok - end - end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% CLOSE(insock()) -> ok |