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

Commit

Permalink
Work around windows udp bug, allow zero reads
Browse files Browse the repository at this point in the history
  • Loading branch information
piscisaureus committed Oct 20, 2011
1 parent e8a418e commit 36903d3
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 50 deletions.
30 changes: 28 additions & 2 deletions src/win/internal.h
Expand Up @@ -307,14 +307,40 @@ uv_err_code uv_translate_sys_error(int sys_errno);


/*
* Initialization for the windows and winsock api
* Winapi and ntapi utility functions
*/
void uv_winapi_init();


/*
* Winsock utility functions
*/
void uv_winsock_init();

int uv_ntstatus_to_winsock_error(NTSTATUS status);

BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target);
BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target);

int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
int* addr_len, WSAOVERLAPPED *overlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);

/* Threads and synchronization */
/* Whether ipv6 is supported */
extern int uv_allow_ipv6;

/* Ip address used to bind to any port at any interface */
extern struct sockaddr_in uv_addr_ip4_any_;
extern struct sockaddr_in6 uv_addr_ip6_any_;


/*
* Threads and synchronization
*/
typedef struct uv_once_s {
unsigned char ran;
/* The actual event handle must be aligned to sizeof(HANDLE), so in */
Expand Down
111 changes: 70 additions & 41 deletions src/win/udp.c
Expand Up @@ -24,17 +24,15 @@
#include "uv.h"
#include "../uv-common.h"
#include "internal.h"
#include <stdio.h>

#if 0

/*
* Threshold of active udp streams for which to preallocate udp read buffers.
*/
const unsigned int uv_active_udp_streams_threshold = 0;

/* A zero-size buffer for use by uv_udp_read */
static char uv_zero_[] = "";
#endif

/* Counter to keep track of active udp streams */
static unsigned int active_udp_streams = 0;
Expand Down Expand Up @@ -63,6 +61,8 @@ int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name,
static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle,
SOCKET socket) {
DWORD yes = 1;
WSAPROTOCOL_INFOW info;
int opt_len;

assert(handle->socket == INVALID_SOCKET);

Expand All @@ -89,14 +89,31 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle,
}

if (pSetFileCompletionNotificationModes) {
if (pSetFileCompletionNotificationModes((HANDLE)socket,
FILE_SKIP_SET_EVENT_ON_HANDLE |
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
} else if (GetLastError() != ERROR_INVALID_FUNCTION) {
/* All know windowses that support SetFileCompletionNotificationModes */
/* have a bug that makes it impossible to use this function in */
/* conjunction with datagram sockets. We can work around that but only */
/* if the user is using the default UDP driver (AFD) and has no other */
/* LSPs stacked on top. Here we check whether that is the case. */
opt_len = (int) sizeof info;
if (!getsockopt(socket,
SOL_SOCKET,
SO_PROTOCOL_INFOW,
(char*) &info,
&opt_len) == SOCKET_ERROR) {
uv__set_sys_error(loop, GetLastError());
return -1;
}

if (info.ProtocolChain.ChainLen == 1) {
if (pSetFileCompletionNotificationModes((HANDLE)socket,
FILE_SKIP_SET_EVENT_ON_HANDLE |
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
} else if (GetLastError() != ERROR_INVALID_FUNCTION) {
uv__set_sys_error(loop, GetLastError());
return -1;
}
}
}

handle->socket = socket;
Expand Down Expand Up @@ -237,6 +254,11 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
uv_buf_t buf;
DWORD bytes, flags;
int result;
int (WSAAPI *recvfrom)(SOCKET, WSABUF*, DWORD, DWORD*, DWORD*,

This comment has been minimized.

Copy link
@igorzi

igorzi Oct 20, 2011

Maybe these should be fields in uv_udp_t? so that we wouldn't have to do the handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP check every time.

struct sockaddr*, int*, WSAOVERLAPPED*,
LPWSAOVERLAPPED_COMPLETION_ROUTINE);
int (WSAAPI *recv)(SOCKET, WSABUF*, DWORD, DWORD*, DWORD*,
WSAOVERLAPPED*, LPWSAOVERLAPPED_COMPLETION_ROUTINE);

assert(handle->flags & UV_HANDLE_READING);
assert(!(handle->flags & UV_HANDLE_READ_PENDING));
Expand All @@ -248,10 +270,9 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
* Preallocate a read buffer if the number of active streams is below
* the threshold.
*/
#if 0
if (active_udp_streams < uv_active_udp_streams_threshold) {
handle->flags &= ~UV_HANDLE_ZERO_READ;
#endif

handle->recv_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536);
assert(handle->recv_buffer.len > 0);

Expand All @@ -260,7 +281,13 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
handle->recv_from_len = sizeof handle->recv_from;
flags = 0;

result = WSARecvFrom(handle->socket,
if (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP) {
recvfrom = uv_wsarecvfrom_workaround;
} else {
recvfrom = WSARecvFrom;
}

result = recvfrom(handle->socket,
(WSABUF*) &buf,
1,
&bytes,
Expand All @@ -286,21 +313,27 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
uv_insert_pending_req(loop, req);
handle->reqs_pending++;
}
#if 0

} else {
handle->flags |= UV_HANDLE_ZERO_READ;

buf.base = (char*) uv_zero_;
buf.len = 0;
flags = MSG_PARTIAL;
flags = MSG_PEEK;

result = WSARecv(handle->socket,
(WSABUF*) &buf,
1,
&bytes,
&flags,
&req->overlapped,
NULL);
if (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP) {
recv = uv_wsarecv_workaround;
} else {
recv = WSARecv;
}

result = recv(handle->socket,
(WSABUF*) &buf,
1,
&bytes,
&flags,
&req->overlapped,
NULL);

if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
/* Process the req without IOCP. */
Expand All @@ -319,7 +352,6 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
handle->reqs_pending++;
}
}
#endif
}


Expand Down Expand Up @@ -448,34 +480,27 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
handle->flags &= ~UV_HANDLE_READ_PENDING;

if (!REQ_SUCCESS(req) &&
GET_REQ_STATUS(req) != STATUS_RECEIVE_EXPEDITED) {
GET_REQ_SOCK_ERROR(req) != WSAEMSGSIZE) {
/* An error occurred doing the read. */
if ((handle->flags & UV_HANDLE_READING)) {
uv__set_sys_error(loop, GET_REQ_SOCK_ERROR(req));
if (handle->flags & UV_HANDLE_READING) {
uv__set_sys_error(loop, GET_REQ_SOCK_ERROR(req));
uv_udp_recv_stop(handle);
#if 0
buf = (handle->flags & UV_HANDLE_ZERO_READ) ?
uv_buf_init(NULL, 0) : handle->recv_buffer;
#else
buf = handle->recv_buffer;
#endif
handle->recv_cb(handle, -1, buf, NULL, 0);
}
goto done;
}

#if 0
if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
#endif
/* Successful read */
partial = (GET_REQ_STATUS(req) == STATUS_RECEIVE_EXPEDITED);
partial = !REQ_SUCCESS(req);
handle->recv_cb(handle,
req->overlapped.InternalHigh,
handle->recv_buffer,
(struct sockaddr*) &handle->recv_from,
partial ? UV_UDP_PARTIAL : 0);
#if 0
} else {
} else if (handle->flags & UV_HANDLE_READING) {
DWORD bytes, err, flags;
struct sockaddr_storage from;
int from_len;
Expand All @@ -487,7 +512,8 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,

memset(&from, 0, sizeof from);
from_len = sizeof from;
flags = MSG_PARTIAL;

flags = 0;

if (WSARecvFrom(handle->socket,
(WSABUF*)&buf,
Expand All @@ -500,14 +526,18 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
NULL) != SOCKET_ERROR) {

/* Message received */
handle->recv_cb(handle,
bytes,
buf,
(struct sockaddr*) &from,
(flags & MSG_PARTIAL) ? UV_UDP_PARTIAL : 0);
handle->recv_cb(handle, bytes, buf, (struct sockaddr*) &from, 0);
} else {
err = WSAGetLastError();
if (err == WSAEWOULDBLOCK) {
if (err == WSAEMSGSIZE) {
/* Message truncated */
handle->recv_cb(handle,
bytes,
buf,
(struct sockaddr*) &from,
UV_UDP_PARTIAL);
} if (err == WSAEWOULDBLOCK) {
/* Kernel buffer empty */
uv__set_sys_error(loop, WSAEWOULDBLOCK);
handle->recv_cb(handle, 0, buf, NULL, 0);
} else {
Expand All @@ -517,7 +547,6 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
}
}
}
#endif

done:
/* Post another read if still reading and not closing. */
Expand Down
8 changes: 8 additions & 0 deletions src/win/winapi.c
Expand Up @@ -27,6 +27,7 @@


sRtlNtStatusToDosError pRtlNtStatusToDosError;
sNtDeviceIoControlFile pNtDeviceIoControlFile;
sNtQueryInformationFile pNtQueryInformationFile;
sNtSetInformationFile pNtSetInformationFile;
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
Expand Down Expand Up @@ -57,6 +58,13 @@ void uv_winapi_init() {
uv_fatal_error(GetLastError(), "GetProcAddress");
}

pNtDeviceIoControlFile = (sNtDeviceIoControlFile) GetProcAddress(
ntdll_module,
"NtDeviceIoControlFile");

This comment has been minimized.

Copy link
@igorzi

igorzi Oct 20, 2011

Should be "GetProcAddress".

if (pNtDeviceIoControlFile == NULL) {
uv_fatal_error(GetLastError(), "NtDeviceIoControlFile");
}

pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress(
ntdll_module,
"NtSetInformationFile");
Expand Down
19 changes: 19 additions & 0 deletions src/win/winapi.h
Expand Up @@ -4270,9 +4270,26 @@ typedef enum _FILE_INFORMATION_CLASS {
FILE_SPECIAL_ACCESS)
#endif

typedef VOID (NTAPI *PIO_APC_ROUTINE)
(PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG Reserved);

typedef ULONG (NTAPI *sRtlNtStatusToDosError)
(NTSTATUS Status);

typedef NTSTATUS (NTAPI *sNtDeviceIoControlFile)
(HANDLE FileHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG IoControlCode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength);

typedef NTSTATUS (NTAPI *sNtQueryInformationFile)
(HANDLE FileHandle,
PIO_STATUS_BLOCK IoStatusBlock,
Expand Down Expand Up @@ -4325,6 +4342,8 @@ typedef BOOLEAN (WINAPI* sCreateSymbolicLinkW)

/* Ntapi function pointers */
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
extern sNtDeviceIoControlFile pNtDeviceIoControlFile;
extern sNtDeviceIoControlFile pNtDeviceIoControlFile;

This comment has been minimized.

Copy link
@igorzi

igorzi Oct 20, 2011

Duplicates?

extern sNtQueryInformationFile pNtQueryInformationFile;
extern sNtSetInformationFile pNtSetInformationFile;

Expand Down

0 comments on commit 36903d3

Please sign in to comment.