[PATCH 3/4] ntdll: Implement IOCTL_AFD_POLL.

Zebediah Figura z.figura12 at gmail.com
Mon May 24 23:13:41 CDT 2021


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50975
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
As described in a comment, it's possible to implement this entirely on the
server side. I found it to be difficult and fragile due to the difference in
structure size between 32-bit and 64-bit processes, and elected to simplify the
server's job by handling that in ntdll. I'm open to moving the whole ioctl to
the server, though.

 dlls/ntdll/unix/socket.c | 157 ++++++++++++++++++++
 include/wine/afd.h       |  30 ++++
 server/protocol.def      |  24 +++
 server/sock.c            | 306 ++++++++++++++++++++++++++++++++++++++-
 server/trace.c           |  32 ++++
 5 files changed, 546 insertions(+), 3 deletions(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 0e262b26611..0812ec4970f 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -235,6 +235,159 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     return status;
 }
 
+
+struct async_poll_ioctl
+{
+    struct async_fileio io;
+    unsigned int count;
+    struct afd_poll_params *input, *output;
+    struct poll_socket_output sockets[1];
+};
+
+static ULONG_PTR fill_poll_output( struct async_poll_ioctl *async, NTSTATUS status )
+{
+    struct afd_poll_params *input = async->input, *output = async->output;
+    unsigned int i, count = 0;
+
+    memcpy( output, input, offsetof( struct afd_poll_params, sockets[0] ) );
+
+    if (!status)
+    {
+        for (i = 0; i < async->count; ++i)
+        {
+            if (async->sockets[i].flags)
+            {
+                output->sockets[count].socket = input->sockets[i].socket;
+                output->sockets[count].flags = async->sockets[i].flags;
+                output->sockets[count].status = async->sockets[i].status;
+                ++count;
+            }
+        }
+    }
+    output->count = count;
+    return offsetof( struct afd_poll_params, sockets[count] );
+}
+
+static NTSTATUS async_poll_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status )
+{
+    struct async_poll_ioctl *async = user;
+    ULONG_PTR information = 0;
+
+    if (status == STATUS_ALERTED)
+    {
+        SERVER_START_REQ( get_async_result )
+        {
+            req->user_arg = wine_server_client_ptr( async );
+            wine_server_set_reply( req, async->sockets, async->count * sizeof(async->sockets[0]) );
+            status = wine_server_call( req );
+        }
+        SERVER_END_REQ;
+
+        information = fill_poll_output( async, status );
+    }
+
+    if (status != STATUS_PENDING)
+    {
+        io->Status = status;
+        io->Information = information;
+        free( async->input );
+        release_fileio( &async->io );
+    }
+    return status;
+}
+
+
+/* we could handle this ioctl entirely on the server side, but the differing
+ * structure size makes it painful */
+static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
+                           void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size )
+{
+    const struct afd_poll_params *params = in_buffer;
+    struct poll_socket_input *input;
+    struct async_poll_ioctl *async;
+    HANDLE wait_handle;
+    DWORD async_size;
+    NTSTATUS status;
+    unsigned int i;
+    ULONG options;
+
+    if (in_size < sizeof(*params) || out_size < in_size || !params->count
+            || in_size < offsetof( struct afd_poll_params, sockets[params->count] ))
+        return STATUS_INVALID_PARAMETER;
+
+    TRACE( "timeout %s, count %u, unknown %#x, padding (%#x, %#x, %#x), sockets[0] {%04lx, %#x}\n",
+            wine_dbgstr_longlong(params->timeout), params->count, params->unknown,
+            params->padding[0], params->padding[1], params->padding[2],
+            params->sockets[0].socket, params->sockets[0].flags );
+
+    if (params->unknown) FIXME( "unknown boolean is %#x\n", params->unknown );
+    if (params->padding[0]) FIXME( "padding[0] is %#x\n", params->padding[0] );
+    if (params->padding[1]) FIXME( "padding[1] is %#x\n", params->padding[1] );
+    if (params->padding[2]) FIXME( "padding[2] is %#x\n", params->padding[2] );
+    for (i = 0; i < params->count; ++i)
+    {
+        if (params->sockets[i].flags & ~0x1ff)
+            FIXME( "unknown socket flags %#x\n", params->sockets[i].flags );
+    }
+
+    if (!(input = malloc( params->count * sizeof(*input) )))
+        return STATUS_NO_MEMORY;
+
+    async_size = offsetof( struct async_poll_ioctl, sockets[params->count] );
+
+    if (!(async = (struct async_poll_ioctl *)alloc_fileio( async_size, async_poll_proc, handle )))
+    {
+        free( input );
+        return STATUS_NO_MEMORY;
+    }
+
+    if (!(async->input = malloc( in_size )))
+    {
+        release_fileio( &async->io );
+        free( input );
+        return STATUS_NO_MEMORY;
+    }
+    memcpy( async->input, in_buffer, in_size );
+
+    async->count = params->count;
+    async->output = out_buffer;
+
+    for (i = 0; i < params->count; ++i)
+    {
+        input[i].socket = params->sockets[i].socket;
+        input[i].flags = params->sockets[i].flags;
+    }
+
+    SERVER_START_REQ( poll_socket )
+    {
+        req->async = server_async( handle, &async->io, event, apc, apc_user, io );
+        req->timeout = params->timeout;
+        wine_server_add_data( req, input, params->count * sizeof(*input) );
+        wine_server_set_reply( req, async->sockets, params->count * sizeof(async->sockets[0]) );
+        status = wine_server_call( req );
+        wait_handle = wine_server_ptr_handle( reply->wait );
+        options = reply->options;
+        if (wait_handle && status != STATUS_PENDING)
+        {
+            io->Status = status;
+            io->Information = fill_poll_output( async, status );
+        }
+    }
+    SERVER_END_REQ;
+
+    free( input );
+
+    if (status != STATUS_PENDING)
+    {
+        free( async->input );
+        release_fileio( &async->io );
+    }
+
+    if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
+    return status;
+}
+
+
 NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
                      ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size )
 {
@@ -299,6 +452,10 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
             break;
         }
 
+        case IOCTL_AFD_POLL:
+            status = sock_poll( handle, event, apc, apc_user, io, in_buffer, in_size, out_buffer, out_size );
+            break;
+
         default:
         {
             FIXME( "Unknown ioctl %#x (device %#x, access %#x, function %#x, method %#x)\n",
diff --git a/include/wine/afd.h b/include/wine/afd.h
index a83ce7c1bac..264e76ef69a 100644
--- a/include/wine/afd.h
+++ b/include/wine/afd.h
@@ -27,6 +27,20 @@
 
 #define IOCTL_AFD_LISTEN                    CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER,  FILE_ANY_ACCESS)
 #define IOCTL_AFD_RECV                      CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER,  FILE_ANY_ACCESS)
+#define IOCTL_AFD_POLL                      CTL_CODE(FILE_DEVICE_BEEP, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define AFD_POLL_READ           0x0001
+#define AFD_POLL_OOB            0x0002
+#define AFD_POLL_WRITE          0x0004
+#define AFD_POLL_HUP            0x0008
+#define AFD_POLL_RESET          0x0010
+#define AFD_POLL_CLOSE          0x0020
+#define AFD_POLL_CONNECT        0x0040
+#define AFD_POLL_ACCEPT         0x0080
+#define AFD_POLL_CONNECT_ERR    0x0100
+/* I have never seen these reported, but StarCraft Remastered polls for them. */
+#define AFD_POLL_UNK1           0x0200
+#define AFD_POLL_UNK2           0x0400
 
 struct afd_listen_params
 {
@@ -50,6 +64,22 @@ struct afd_recv_params
     int msg_flags;
 };
 
+#include <pshpack4.h>
+struct afd_poll_params
+{
+    LONGLONG timeout;
+    unsigned int count;
+    BOOLEAN unknown;
+    BOOLEAN padding[3];
+    struct
+    {
+        SOCKET socket;
+        int flags;
+        NTSTATUS status;
+    } sockets[1];
+};
+#include <poppack.h>
+
 #define IOCTL_AFD_WINE_CREATE               CTL_CODE(FILE_DEVICE_NETWORK, 200, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_AFD_WINE_ACCEPT               CTL_CODE(FILE_DEVICE_NETWORK, 201, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_AFD_WINE_ACCEPT_INTO          CTL_CODE(FILE_DEVICE_NETWORK, 202, METHOD_BUFFERED, FILE_ANY_ACCESS)
diff --git a/server/protocol.def b/server/protocol.def
index 62a584e4cc9..db50e03c691 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1490,6 +1490,30 @@ enum server_fd_type
 @END
 
 
+struct poll_socket_input
+{
+    obj_handle_t socket;        /* socket handle */
+    int flags;                  /* events to poll for */
+};
+
+struct poll_socket_output
+{
+    int flags;                  /* events signaled */
+    unsigned int status;        /* socket status */
+};
+
+/* Perform an async poll on a socket */
+ at REQ(poll_socket)
+    async_data_t async;         /* async I/O parameters */
+    timeout_t    timeout;       /* timeout */
+    VARARG(sockets,poll_socket_input); /* list of sockets to poll */
+ at REPLY
+    obj_handle_t wait;          /* handle to wait on for blocking poll */
+    unsigned int options;       /* file open options */
+    VARARG(sockets,poll_socket_output); /* data returned */
+ at END
+
+
 /* Retrieve the next pending console ioctl request */
 @REQ(get_next_console_request)
     obj_handle_t handle;        /* console server handle */
diff --git a/server/sock.c b/server/sock.c
index ab5aa57a6cd..3925018c7a5 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -96,6 +96,23 @@
 #include "request.h"
 #include "user.h"
 
+static struct list poll_list = LIST_INIT( poll_list );
+
+struct poll_req
+{
+    struct list entry;
+    struct async *async;
+    struct iosb *iosb;
+    struct timeout_user *timeout;
+    unsigned int count;
+    struct poll_socket_output *output;
+    struct
+    {
+        struct sock *sock;
+        int flags;
+    } sockets[1];
+};
+
 struct accept_req
 {
     struct list entry;
@@ -151,6 +168,7 @@ struct sock
     struct async_queue  ifchange_q;  /* queue for interface change notifications */
     struct async_queue  accept_q;    /* queue for asynchronous accepts */
     struct async_queue  connect_q;   /* queue for asynchronous connects */
+    struct async_queue  poll_q;      /* queue for asynchronous polls */
     struct object      *ifchange_obj; /* the interface change notification object */
     struct list         ifchange_entry; /* entry in ifchange notification list */
     struct list         accept_list; /* list of pending accept requests */
@@ -621,6 +639,106 @@ static void complete_async_connect( struct sock *sock )
     }
 }
 
+static void free_poll_req( void *private )
+{
+    struct poll_req *req = private;
+    unsigned int i;
+
+    if (req->timeout) remove_timeout_user( req->timeout );
+
+    for (i = 0; i < req->count; ++i)
+        release_object( req->sockets[i].sock );
+    release_object( req->async );
+    release_object( req->iosb );
+    list_remove( &req->entry );
+    free( req );
+}
+
+static int is_oobinline( struct sock *sock )
+{
+    int oobinline;
+    socklen_t len = sizeof(oobinline);
+    return !getsockopt( get_unix_fd( sock->fd ), SOL_SOCKET, SO_OOBINLINE, (char *)&oobinline, &len ) && oobinline;
+}
+
+static int get_poll_flags( struct sock *sock, int event )
+{
+    int flags = 0;
+
+    /* A connection-mode socket which has never been connected does not return
+     * write or hangup events, but Linux reports POLLOUT | POLLHUP. */
+    if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING)))
+        event &= ~(POLLOUT | POLLHUP);
+
+    if (event & POLLIN)
+    {
+        if (sock->state & FD_WINE_LISTENING)
+            flags |= AFD_POLL_ACCEPT;
+        else
+            flags |= AFD_POLL_READ;
+    }
+    if (event & POLLPRI)
+        flags |= is_oobinline( sock ) ? AFD_POLL_READ : AFD_POLL_OOB;
+    if (event & POLLOUT)
+        flags |= AFD_POLL_WRITE;
+    if (sock->state & FD_WINE_CONNECTED)
+        flags |= AFD_POLL_CONNECT;
+    if (event & POLLHUP)
+        flags |= AFD_POLL_HUP;
+    if (event & POLLERR)
+        flags |= AFD_POLL_CONNECT_ERR;
+
+    return flags;
+}
+
+static void complete_async_polls( struct sock *sock, int event, int error )
+{
+    int flags = get_poll_flags( sock, event );
+    struct poll_req *req, *next;
+
+    LIST_FOR_EACH_ENTRY_SAFE( req, next, &poll_list, struct poll_req, entry )
+    {
+        struct iosb *iosb = req->iosb;
+        unsigned int i;
+
+        if (iosb->status != STATUS_PENDING) continue;
+
+        for (i = 0; i < req->count; ++i)
+        {
+            if (req->sockets[i].sock != sock) continue;
+            if (!(req->sockets[i].flags & flags)) continue;
+
+            if (debug_level)
+                fprintf( stderr, "completing poll for socket %p, wanted %#x got %#x\n",
+                         sock, req->sockets[i].flags, flags );
+
+            req->output[i].flags = req->sockets[i].flags & flags;
+            req->output[i].status = sock_get_ntstatus( error );
+
+            iosb->status = STATUS_SUCCESS;
+            iosb->out_data = req->output;
+            iosb->out_size = req->count * sizeof(*req->output);
+            async_terminate( req->async, STATUS_ALERTED );
+            break;
+        }
+    }
+}
+
+static void async_poll_timeout( void *private )
+{
+    struct poll_req *req = private;
+    struct iosb *iosb = req->iosb;
+
+    req->timeout = NULL;
+
+    if (iosb->status != STATUS_PENDING) return;
+
+    iosb->status = STATUS_TIMEOUT;
+    iosb->out_data = req->output;
+    iosb->out_size = req->count * sizeof(*req->output);
+    async_terminate( req->async, STATUS_ALERTED );
+}
+
 static int sock_dispatch_asyncs( struct sock *sock, int event, int error )
 {
     if (event & (POLLIN | POLLPRI))
@@ -820,6 +938,8 @@ static void sock_poll_event( struct fd *fd, int event )
             event |= POLLHUP;
     }
 
+    complete_async_polls( sock, event, error );
+
     event = sock_dispatch_asyncs( sock, event, error );
     sock_dispatch_events( sock, prevstate, event, error );
 
@@ -835,11 +955,34 @@ static void sock_dump( struct object *obj, int verbose )
             sock->mask, sock->pending_events, sock->reported_events );
 }
 
+static int poll_flags_from_afd( struct sock *sock, int flags )
+{
+    int ev = 0;
+
+    /* A connection-mode socket which has never been connected does
+     * not return write or hangup events, but Linux returns
+     * POLLOUT | POLLHUP. */
+    if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING)))
+        return -1;
+
+    if (flags & (AFD_POLL_READ | AFD_POLL_ACCEPT))
+        ev |= POLLIN;
+    if ((flags & AFD_POLL_HUP) && sock->type == WS_SOCK_STREAM)
+        ev |= POLLIN;
+    if (flags & AFD_POLL_OOB)
+        ev |= is_oobinline( sock ) ? POLLIN : POLLPRI;
+    if (flags & AFD_POLL_WRITE)
+        ev |= POLLOUT;
+
+    return ev;
+}
+
 static int sock_get_poll_events( struct fd *fd )
 {
     struct sock *sock = get_fd_user( fd );
     unsigned int mask = sock->mask & ~sock->reported_events;
     unsigned int smask = sock->state & mask;
+    struct poll_req *req;
     int ev = 0;
 
     assert( sock->obj.ops == &sock_ops );
@@ -869,6 +1012,18 @@ static int sock_get_poll_events( struct fd *fd )
     else if (smask & FD_WRITE)
         ev |= POLLOUT;
 
+    LIST_FOR_EACH_ENTRY( req, &poll_list, struct poll_req, entry )
+    {
+        unsigned int i;
+
+        for (i = 0; i < req->count; ++i)
+        {
+            if (req->sockets[i].sock != sock) continue;
+
+            ev |= poll_flags_from_afd( sock, req->sockets[i].flags );
+        }
+    }
+
     return ev;
 }
 
@@ -933,18 +1088,45 @@ static struct fd *sock_get_fd( struct object *obj )
 static int sock_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
 {
     struct sock *sock = (struct sock *)obj;
-    struct accept_req *req, *next;
 
     if (sock->obj.handle_count == 1) /* last handle */
     {
+        struct accept_req *accept_req, *accept_next;
+        struct poll_req *poll_req, *poll_next;
+
         if (sock->accept_recv_req)
             async_terminate( sock->accept_recv_req->async, STATUS_CANCELLED );
 
-        LIST_FOR_EACH_ENTRY_SAFE( req, next, &sock->accept_list, struct accept_req, entry )
-            async_terminate( req->async, STATUS_CANCELLED );
+        LIST_FOR_EACH_ENTRY_SAFE( accept_req, accept_next, &sock->accept_list, struct accept_req, entry )
+            async_terminate( accept_req->async, STATUS_CANCELLED );
 
         if (sock->connect_req)
             async_terminate( sock->connect_req->async, STATUS_CANCELLED );
+
+        LIST_FOR_EACH_ENTRY_SAFE( poll_req, poll_next, &poll_list, struct poll_req, entry )
+        {
+            struct iosb *iosb = poll_req->iosb;
+            unsigned int i;
+
+            if (iosb->status != STATUS_PENDING) continue;
+
+            for (i = 0; i < poll_req->count; ++i)
+            {
+                if (poll_req->sockets[i].sock == sock)
+                {
+                    iosb->status = STATUS_SUCCESS;
+                    poll_req->output[i].flags = AFD_POLL_CLOSE;
+                    poll_req->output[i].status = 0;
+                }
+            }
+
+            if (iosb->status != STATUS_PENDING)
+            {
+                iosb->out_data = poll_req->output;
+                iosb->out_size = poll_req->count * sizeof(*poll_req->output);
+                async_terminate( poll_req->async, STATUS_ALERTED );
+            }
+        }
     }
 
     return 1;
@@ -968,6 +1150,7 @@ static void sock_destroy( struct object *obj )
     free_async_queue( &sock->ifchange_q );
     free_async_queue( &sock->accept_q );
     free_async_queue( &sock->connect_q );
+    free_async_queue( &sock->poll_q );
     if (sock->event) release_object( sock->event );
     if (sock->fd)
     {
@@ -1007,6 +1190,7 @@ static struct sock *create_socket(void)
     init_async_queue( &sock->ifchange_q );
     init_async_queue( &sock->accept_q );
     init_async_queue( &sock->connect_q );
+    init_async_queue( &sock->poll_q );
     memset( sock->errors, 0, sizeof(sock->errors) );
     list_init( &sock->accept_list );
     return sock;
@@ -1710,6 +1894,101 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
     }
 }
 
+static int poll_socket( struct sock *poll_sock, struct async *async, timeout_t timeout,
+                        unsigned int count, const struct poll_socket_input *input )
+{
+    struct poll_socket_output *output;
+    struct poll_req *req;
+    unsigned int i, j;
+
+    if (!(output = mem_alloc( count * sizeof(*output) )))
+        return 0;
+    memset( output, 0, count * sizeof(*output) );
+
+    if (!(req = mem_alloc( offsetof( struct poll_req, sockets[count] ) )))
+    {
+        free( output );
+        return 0;
+    }
+
+    req->timeout = NULL;
+    if (timeout && timeout != TIMEOUT_INFINITE &&
+        !(req->timeout = add_timeout_user( timeout, async_poll_timeout, req )))
+    {
+        free( req );
+        free( output );
+        return 0;
+    }
+
+    for (i = 0; i < count; ++i)
+    {
+        req->sockets[i].sock = (struct sock *)get_handle_obj( current->process, input[i].socket, 0, &sock_ops );
+        if (!req->sockets[i].sock)
+        {
+            for (j = 0; j < i; ++j) release_object( req->sockets[i].sock );
+            if (req->timeout) remove_timeout_user( req->timeout );
+            free( req );
+            free( output );
+            return 0;
+        }
+        req->sockets[i].flags = input[i].flags;
+    }
+
+    req->count = count;
+    req->async = (struct async *)grab_object( async );
+    req->iosb = async_get_iosb( async );
+    req->output = output;
+
+    list_add_tail( &poll_list, &req->entry );
+    async_set_completion_callback( async, free_poll_req, req );
+    queue_async( &poll_sock->poll_q, async );
+
+    if (!timeout) req->iosb->status = STATUS_SUCCESS;
+
+    for (i = 0; i < count; ++i)
+    {
+        struct sock *sock = req->sockets[i].sock;
+        struct pollfd pollfd;
+        int flags;
+
+        pollfd.fd = get_unix_fd( sock->fd );
+        pollfd.events = poll_flags_from_afd( sock, req->sockets[i].flags );
+        if (pollfd.events < 0 || poll( &pollfd, 1, 0 ) < 0) continue;
+
+        if ((req->sockets[i].flags & AFD_POLL_HUP) && (pollfd.revents & POLLIN) &&
+            sock->type == WS_SOCK_STREAM)
+        {
+            char dummy;
+
+            if (!recv( get_unix_fd( sock->fd ), &dummy, 1, MSG_PEEK ))
+            {
+                pollfd.revents &= ~POLLIN;
+                pollfd.revents |= POLLHUP;
+            }
+        }
+
+        flags = get_poll_flags( sock, pollfd.revents ) & req->sockets[i].flags;
+        if (flags)
+        {
+            req->iosb->status = STATUS_SUCCESS;
+            output[i].flags = flags;
+            output[i].status = sock_get_ntstatus( sock_error( sock->fd ) );
+        }
+    }
+
+    if (req->iosb->status != STATUS_PENDING)
+    {
+        req->iosb->out_data = output;
+        req->iosb->out_size = count * sizeof(*output);
+        async_terminate( req->async, STATUS_ALERTED );
+    }
+
+    for (i = 0; i < req->count; ++i)
+        sock_reselect( req->sockets[i].sock );
+    set_error( STATUS_PENDING );
+    return 1;
+}
+
 #ifdef HAVE_LINUX_RTNETLINK_H
 
 /* only keep one ifchange object around, all sockets waiting for wakeups will look to it */
@@ -2201,3 +2480,24 @@ DECL_HANDLER(recv_socket)
     }
     release_object( sock );
 }
+
+DECL_HANDLER(poll_socket)
+{
+    struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
+    const struct poll_socket_input *input = get_req_data();
+    struct async *async;
+    unsigned int count;
+
+    if (!sock) return;
+
+    count = get_req_data_size() / sizeof(*input);
+
+    if ((async = create_request_async( sock->fd, get_fd_comp_flags( sock->fd ), &req->async )))
+    {
+        reply->wait = async_handoff( async, poll_socket( sock, async, req->timeout, count, input ), NULL, 0 );
+        reply->options = get_fd_options( sock->fd );
+        release_object( async );
+    }
+
+    release_object( sock );
+}
diff --git a/server/trace.c b/server/trace.c
index ab805a05234..d8082d54cdb 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1372,6 +1372,38 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size )
     fputc( '}', stderr );
 }
 
+static void dump_varargs_poll_socket_input( const char *prefix, data_size_t size )
+{
+    const struct poll_socket_input *input;
+
+    fprintf( stderr, "%s{", prefix );
+    while (size >= sizeof(*input))
+    {
+        input = cur_data;
+        fprintf( stderr, "{socket=%04x,flags=%08x}", input->socket, input->flags );
+        size -= sizeof(*input);
+        remove_data( sizeof(*input) );
+        if (size) fputc( ',', stderr );
+    }
+    fputc( '}', stderr );
+}
+
+static void dump_varargs_poll_socket_output( const char *prefix, data_size_t size )
+{
+    const struct poll_socket_output *output;
+
+    fprintf( stderr, "%s{", prefix );
+    while (size >= sizeof(*output))
+    {
+        output = cur_data;
+        fprintf( stderr, "{flags=%08x,status=%s}", output->flags, get_status_name( output->status ) );
+        size -= sizeof(*output);
+        remove_data( sizeof(*output) );
+        if (size) fputc( ',', stderr );
+    }
+    fputc( '}', stderr );
+}
+
 typedef void (*dump_func)( const void *req );
 
 /* Everything below this line is generated automatically by tools/make_requests */
-- 
2.30.2




More information about the wine-devel mailing list