Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
windows: abort accept requests when a shared tcp server is closed
Just closing the listening socket handle does not cancel AcceptEx
operations when another handle is keeping the underlying socket
open. Thus the AcceptEx operations have to be explicitly canceled
in uv_tcp_close.
  • Loading branch information
piscisaureus committed Jun 20, 2012
1 parent b496c12 commit 6e8eb33
Showing 1 changed file with 73 additions and 17 deletions.
90 changes: 73 additions & 17 deletions src/win/tcp.c
Expand Up @@ -28,7 +28,6 @@
#include "req-inl.h"



/*
* Threshold of active tcp streams for which to preallocate tcp read buffers.
* (Due to node slab allocator performing poorly under this pattern,
Expand Down Expand Up @@ -1292,31 +1291,88 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
}


void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) {
SOCKET socket = tcp->socket;
int non_ifs_lsp;

/* Check if we have any non-IFS LSPs stacked on top of TCP */
non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 :
uv_tcp_non_ifs_lsp_ipv4;

/* If there are non-ifs LSPs then try to obtain a base handle for the */
/* socket. This will always fail on Windows XP/3k. */
if (non_ifs_lsp) {
DWORD bytes;
if (WSAIoctl(socket,
SIO_BASE_HANDLE,
NULL,
0,
&socket,
sizeof socket,
&bytes,
NULL,
NULL) != 0) {
/* Failed. We can't do CancelIo. */
return -1;
}
}

assert(socket != 0 && socket != INVALID_SOCKET);

if (!CancelIo((HANDLE) socket)) {
return -1;
}

/* It worked. */
return 0;
}


void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
int close_socket = 1;

/*
* In order for winsock to do a graceful close there must not be
* any pending reads.
*/
if (tcp->flags & UV_HANDLE_READ_PENDING) {
/* Just do shutdown on non-shared sockets, which ensures graceful close. */
/* In order for winsock to do a graceful close there must not be any */
/* any pending reads, or the socket must be shut down for writing */
if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) {
/* Just do shutdown on non-shared sockets, which ensures graceful close. */
shutdown(tcp->socket, SD_SEND);
tcp->flags |= UV_HANDLE_SHUT;

} else if (uv_tcp_try_cancel_io(tcp) == 0) {
/* In case of a shared socket, we try to cancel all outstanding I/O, */
/* If that works, don't close the socket yet - wait for the read req to */
/* return and close the socket in uv_tcp_endgame. */
close_socket = 0;

} else {
/* Check if we have any non-IFS LSPs stacked on top of TCP */
non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 :
uv_tcp_non_ifs_lsp_ipv4;
/* When cancelling isn't possible - which could happen when an LSP is */
/* present on an old Windows version, we will have to close the socket */
/* with a read pending. That is not nice because trailing sent bytes */
/* may not make it to the other side. */
}

if (!non_ifs_lsp) {
/*
* Shared socket with no non-IFS LSPs, request to cancel pending I/O.
* The socket will be closed inside endgame.
*/
CancelIo((HANDLE)tcp->socket);
close_socket = 0;
} else if ((tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET) &&
tcp->accept_reqs != NULL) {
/* Under normal circumstances closesocket() will ensure that all pending */
/* accept reqs are canceled. However, when the socket is shared the */
/* presence of another reference to the socket in another process will */
/* keep the accept reqs going, so we have to ensure that these are */
/* canceled. */
if (uv_tcp_try_cancel_io(tcp) != 0) {
/* When cancellation is not possible, there is another option: we can */
/* close the incoming sockets, which will also cancel the accept */
/* operations. However this is not cool because we might inadvertedly */
/* close a socket that just accepted a new connection, which will */
/* cause the connection to be aborted. */
unsigned int i;
for (i = 0; i < uv_simultaneous_server_accepts; i++) {
uv_tcp_accept_t* req = &tcp->accept_reqs[i];
if (req->accept_socket != INVALID_SOCKET &&
!HasOverlappedIoCompleted(&req->overlapped)) {
closesocket(req->accept_socket);
req->accept_socket = INVALID_SOCKET;
}
}
}
}
Expand Down

0 comments on commit 6e8eb33

Please sign in to comment.