ws2_32: Only enable FD_WRITE on short sends.

This commit is contained in:
Damjan Jovanovic 2008-09-07 12:41:19 +02:00 committed by Alexandre Julliard
parent 261a39a008
commit b904dd783c
2 changed files with 113 additions and 41 deletions

View File

@ -1250,8 +1250,13 @@ static NTSTATUS WS2_async_send(void* user, IO_STATUS_BLOCK* iosb, NTSTATUS statu
if (result >= 0) if (result >= 0)
{ {
int totalLength = 0;
int i;
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
_enable_event( wsa->hSocket, FD_WRITE, 0, 0 ); for (i = 0; i < wsa->n_iovecs; i++)
totalLength += wsa->iovec[i].iov_len;
if (result < totalLength)
_enable_event( wsa->hSocket, FD_WRITE, 0, 0 );
} }
else else
{ {
@ -2667,6 +2672,7 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
unsigned int i, options; unsigned int i, options;
int n, fd, err; int n, fd, err;
struct iovec iovec[WS_MSG_MAXIOVLEN]; struct iovec iovec[WS_MSG_MAXIOVLEN];
int totalLength = 0;
ULONG_PTR cvalue = (lpOverlapped && ((ULONG_PTR)lpOverlapped->hEvent & 1) == 0) ? (ULONG_PTR)lpOverlapped : 0; ULONG_PTR cvalue = (lpOverlapped && ((ULONG_PTR)lpOverlapped->hEvent & 1) == 0) ? (ULONG_PTR)lpOverlapped : 0;
TRACE("socket %04lx, wsabuf %p, nbufs %d, flags %d, to %p, tolen %d, ovl %p, func %p\n", TRACE("socket %04lx, wsabuf %p, nbufs %d, flags %d, to %p, tolen %d, ovl %p, func %p\n",
@ -2694,6 +2700,7 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
{ {
iovec[i].iov_base = lpBuffers[i].buf; iovec[i].iov_base = lpBuffers[i].buf;
iovec[i].iov_len = lpBuffers[i].len; iovec[i].iov_len = lpBuffers[i].len;
totalLength += lpBuffers[i].len;
} }
for (;;) for (;;)
@ -2819,7 +2826,8 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
} }
else /* non-blocking */ else /* non-blocking */
{ {
_enable_event(SOCKET2HANDLE(s), FD_WRITE, 0, 0); if (n < totalLength)
_enable_event(SOCKET2HANDLE(s), FD_WRITE, 0, 0);
if (n == -1) if (n == -1)
{ {
err = WSAEWOULDBLOCK; err = WSAEWOULDBLOCK;

View File

@ -1993,9 +1993,23 @@ static DWORD WINAPI drain_socket_thread(LPVOID arg)
{ {
char buffer[1024]; char buffer[1024];
SOCKET sock = *(SOCKET*)arg; SOCKET sock = *(SOCKET*)arg;
int ret;
while (recv(sock, buffer, sizeof(buffer), 0) > 0) while ((ret = recv(sock, buffer, sizeof(buffer), 0)) != 0)
; {
if (ret < 0)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
fd_set readset;
FD_ZERO(&readset);
FD_SET(sock, &readset);
select(0, &readset, NULL, NULL, NULL);
}
else
break;
}
}
return 0; return 0;
} }
@ -2051,10 +2065,13 @@ static void test_write_events(void)
SOCKET dst = INVALID_SOCKET; SOCKET dst = INVALID_SOCKET;
HANDLE hThread = NULL; HANDLE hThread = NULL;
HANDLE hEvent = INVALID_HANDLE_VALUE; HANDLE hEvent = INVALID_HANDLE_VALUE;
int len; char *buffer = NULL;
int bufferSize = 1024*1024;
u_long one = 1; u_long one = 1;
int ret; int ret;
DWORD id; DWORD id;
WSANETWORKEVENTS netEvents;
DWORD dwRet;
if (tcp_socketpair(&src, &dst) != 0) if (tcp_socketpair(&src, &dst) != 0)
{ {
@ -2062,6 +2079,29 @@ static void test_write_events(void)
return; return;
} }
/* On Windows it seems when a non-blocking socket sends to a
blocking socket on the same host, the send() is BLOCKING,
so make both sockets non-blocking */
ret = ioctlsocket(src, FIONBIO, &one);
if (ret)
{
ok(0, "ioctlsocket failed, error %d\n", WSAGetLastError());
goto end;
}
ret = ioctlsocket(dst, FIONBIO, &one);
if (ret)
{
ok(0, "ioctlsocket failed, error %d\n", WSAGetLastError());
goto end;
}
buffer = HeapAlloc(GetProcessHeap(), 0, bufferSize);
if (buffer == NULL)
{
ok(0, "could not allocate memory for test\n");
goto end;
}
hThread = CreateThread(NULL, 0, drain_socket_thread, &dst, 0, &id); hThread = CreateThread(NULL, 0, drain_socket_thread, &dst, 0, &id);
if (hThread == NULL) if (hThread == NULL)
{ {
@ -2076,13 +2116,6 @@ static void test_write_events(void)
goto end; goto end;
} }
ret = ioctlsocket(src, FIONBIO, &one);
if (ret)
{
ok(0, "ioctlsocket failed, error %d\n", WSAGetLastError());
goto end;
}
ret = WSAEventSelect(src, hEvent, FD_WRITE | FD_CLOSE); ret = WSAEventSelect(src, hEvent, FD_WRITE | FD_CLOSE);
if (ret) if (ret)
{ {
@ -2090,41 +2123,72 @@ static void test_write_events(void)
goto end; goto end;
} }
for (len = 100; len > 0; --len) /* FD_WRITE should be set initially, and allow us to send at least 1 byte */
dwRet = WaitForSingleObject(hEvent, 5000);
if (dwRet != WAIT_OBJECT_0)
{ {
WSANETWORKEVENTS netEvents; ok(0, "Initial WaitForSingleObject failed, error %d\n", dwRet);
DWORD dwRet = WaitForSingleObject(hEvent, 5000); goto end;
if (dwRet != WAIT_OBJECT_0) }
{ ret = WSAEnumNetworkEvents(src, NULL, &netEvents);
ok(0, "WaitForSingleObject failed, error %d\n", dwRet); if (ret)
goto end; {
} ok(0, "WSAEnumNetworkEvents failed, error %d\n", ret);
goto end;
}
if (netEvents.lNetworkEvents & FD_WRITE)
{
ret = send(src, "a", 1, 0);
ok(ret == 1, "sending 1 byte failed, error %d\n", WSAGetLastError());
if (ret != 1)
goto end;
}
else
{
ok(0, "FD_WRITE not among initial events\n");
goto end;
}
ret = WSAEnumNetworkEvents(src, NULL, &netEvents); /* Now FD_WRITE should not be set, because the socket send buffer isn't full yet */
if (ret) dwRet = WaitForSingleObject(hEvent, 2000);
{ if (dwRet == WAIT_OBJECT_0)
ok(0, "WSAEnumNetworkEvents failed, error %d\n", ret); {
goto end; ok(0, "WaitForSingleObject should have timed out, but succeeded!\n");
} goto end;
}
if (netEvents.lNetworkEvents & FD_WRITE) /* Now if we send a tonne of data, the socket send buffer will only take some of it,
{ and we will get a short write, which will trigger another FD_WRITE event
ret = send(src, "a", 1, 0); as soon as data is sent and more space becomes available, but not any earlier. */
if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) do
{ {
ok(0, "send failed, error %d\n", WSAGetLastError()); ret = send(src, buffer, bufferSize, 0);
goto end; } while (ret == bufferSize);
} if (ret >= 0 || WSAGetLastError() == WSAEWOULDBLOCK)
} {
dwRet = WaitForSingleObject(hEvent, 5000);
if (netEvents.lNetworkEvents & FD_CLOSE) ok(dwRet == WAIT_OBJECT_0, "Waiting failed with %d\n", dwRet);
{ if (dwRet == WAIT_OBJECT_0)
ok(0, "unexpected close\n"); {
goto end; ret = WSAEnumNetworkEvents(src, NULL, &netEvents);
} ok(ret == 0, "WSAEnumNetworkEvents failed, error %d\n", ret);
if (ret == 0)
goto end;
ok(netEvents.lNetworkEvents & FD_WRITE,
"FD_WRITE event not set as expected, events are 0x%x\n", netEvents.lNetworkEvents);
}
else
goto end;
}
else
{
ok(0, "sending a lot of data failed with error %d\n", WSAGetLastError());
goto end;
} }
end: end:
if (buffer != NULL)
HeapFree(GetProcessHeap(), 0, buffer);
if (src != INVALID_SOCKET) if (src != INVALID_SOCKET)
closesocket(src); closesocket(src);
if (dst != INVALID_SOCKET) if (dst != INVALID_SOCKET)