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; }