[PATCH v4 4/8] server: Defer postprocessing until after setting initial status in send_socket handler.
Jinoh Kang
jinoh.kang.kr at gmail.com
Wed Mar 9 12:36:46 CST 2022
On 3/9/22 00:42, Jinoh Kang wrote:
> On 3/8/22 03:11, Zebediah Figura wrote:
>>
>>
>> On 3/5/22 02:24, Jinoh Kang wrote:
>>> On 3/5/22 09:13, Zebediah Figura wrote:
>>>> On 3/4/22 13:16, Jinoh Kang wrote:
>>>> But assuming that we only really need to clear flags when the kernel send buffer is full, I think the right thing to do would be to clear events if "async_waiting( &sock->write_q )".
>>>
>>> We still need to account for nonblocking I/O (STATUS_DEVICE_NOT_READY), and also is_short_write, which is another indicator for a full send buffer. Otherwise, it breaks ws2_32:sock:test_write_events.
>>> Also, note the implicitly bound address.
>>>
>>> In any case I think the co-routine pattern is inevitable due to the client-server role split. In this case, AFD.SYS can do both pre- and post-processing inside a single function, but wineserver can't block for the client to finish the I/O.
>>>
>>>
>> Right. Maybe we could make use of async_set_completion_callback() instead? I think the only reason that can't work is if the sock name needs to be updated and visible *before* the I/O is necessarily complete.
>
> I'll test if Windows does update it before I/O completion.
Ok, here's the deal: it turns out Windows _does_ update the bound address before I/O completion.
I tested it by flooding loads of UDP packets, each having 1500*20 bytes fragmented into ~ 21 frames (MTU=1500) over a slow NIC.
Soon enough, WSASend() eventually returns with last error WSA_IO_PENDING.
The I/O request was apparently still pending at the moment getsockname() returned.
> OK: getsockname() succeeded while I/O pending.
> Waiting for I/O completion...
> Completion took 23859 milliseconds.
Here's the source code:
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0602
#define _WIN32_WINNT 0x0602
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
void die_error_(int line, DWORD err)
{
fprintf(stderr, "%d: %u (%08x)\n", line, err, err);
exit(1);
}
#define die_error() die_error_(__LINE__, GetLastError())
int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in sa_in, sa_in2;
WSAOVERLAPPED ovl;
WSABUF vecs[1];
int sa_len2;
DWORD result, flags;
static unsigned char buffer[1500*20];
WSAEVENT event;
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) die_error();
event = WSACreateEvent();
if (event == WSA_INVALID_EVENT) die_error();
memset(&sa_in, 0, sizeof(sa_in));
sa_in.sin_family = AF_INET;
if (argc >= 1) inet_pton(AF_INET, argv[1], &sa_in.sin_addr.s_addr);
sa_in.sin_port = htons(1);
for (;;)
{
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == SOCKET_ERROR) die_error();
vecs[0].buf = (CHAR*)buffer;
vecs[0].len = sizeof(buffer);
memset(&ovl, 0, sizeof(ovl));
ovl.hEvent = event;
if (WSASendTo(sock, vecs, 1, NULL, 0, (struct sockaddr *)&sa_in, sizeof(sa_in), &ovl, NULL) == SOCKET_ERROR)
{
DWORD t0 = GetTickCount();
if (WSAGetLastError() != WSA_IO_PENDING) die_error();
sa_len2 = sizeof(sa_in2);
if (getsockname(sock, (struct sockaddr *)&sa_in2, &sa_len2) == SOCKET_ERROR) die_error();
/* still not completed? */
if (!HasOverlappedIoCompleted((LPOVERLAPPED)&ovl) &&
WaitForSingleObject(event, 0) == WAIT_TIMEOUT &&
WaitForSingleObject((HANDLE)sock, 0) == WAIT_TIMEOUT)
{
puts("OK: getsockname() succeeded while I/O pending.\nWaiting for I/O completion...");
fflush(stdout);
WaitForSingleObject(event, INFINITE);
closesocket(sock);
printf("Completion took %lu milliseconds.\n", GetTickCount() - t0);
fflush(stdout);
break; /* success */
}
WSAGetOverlappedResult(sock, &ovl, &result, TRUE, &flags);
}
closesocket(sock);
}
WSACleanup();
return 0;
}
--
Sincerely,
Jinoh Kang
More information about the wine-devel
mailing list