[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