[PATCH 3/6] ws2_32: Reimplement select() on top of IOCTL_AFD_POLL.

Zebediah Figura z.figura12 at gmail.com
Sun Jun 20 11:05:54 CDT 2021


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/ws2_32/socket.c         | 339 ++++++++++++-----------------------
 dlls/ws2_32/tests/sock.c     |  10 +-
 dlls/ws2_32/ws2_32_private.h |   2 -
 3 files changed, 126 insertions(+), 225 deletions(-)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index f5958e86e9f..f8ba7025ec5 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -841,7 +841,6 @@ static void free_per_thread_data(void)
     HeapFree( GetProcessHeap(), 0, ptb->he_buffer );
     HeapFree( GetProcessHeap(), 0, ptb->se_buffer );
     HeapFree( GetProcessHeap(), 0, ptb->pe_buffer );
-    HeapFree( GetProcessHeap(), 0, ptb->fd_cache );
 
     HeapFree( GetProcessHeap(), 0, ptb );
     NtCurrentTeb()->WinSockData = NULL;
@@ -3216,246 +3215,142 @@ int WINAPI WS_recvfrom(SOCKET s, char *buf, INT len, int flags,
         return n;
 }
 
-/* allocate a poll array for the corresponding fd sets */
-static struct pollfd *fd_sets_to_poll( const WS_fd_set *readfds, const WS_fd_set *writefds,
-                                       const WS_fd_set *exceptfds, int *count_ptr )
+
+/* as FD_SET(), but returns 1 if the fd was added, 0 otherwise */
+static int add_fd_to_set( SOCKET fd, struct WS_fd_set *set )
 {
-    unsigned int i, j = 0, count = 0;
-    struct pollfd *fds;
-    struct per_thread_data *ptb = get_per_thread_data();
+    unsigned int i;
 
-    if (readfds) count += readfds->fd_count;
-    if (writefds) count += writefds->fd_count;
-    if (exceptfds) count += exceptfds->fd_count;
-    *count_ptr = count;
-    if (!count)
+    for (i = 0; i < set->fd_count; ++i)
     {
-        SetLastError(WSAEINVAL);
-        return NULL;
+        if (set->fd_array[i] == fd)
+            return 0;
     }
 
-    /* check if the cache can hold all descriptors, if not do the resizing */
-    if (ptb->fd_count < count)
+    if (set->fd_count < WS_FD_SETSIZE)
     {
-        if (!(fds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(fds[0]))))
-        {
-            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
-            return NULL;
-        }
-        HeapFree(GetProcessHeap(), 0, ptb->fd_cache);
-        ptb->fd_cache = fds;
-        ptb->fd_count = count;
+        set->fd_array[set->fd_count++] = fd;
+        return 1;
     }
-    else
-        fds = ptb->fd_cache;
 
-    if (readfds)
-        for (i = 0; i < readfds->fd_count; i++, j++)
-        {
-            fds[j].fd = get_sock_fd( readfds->fd_array[i], FILE_READ_DATA, NULL );
-            if (fds[j].fd == -1) goto failed;
-            fds[j].revents = 0;
-            if (is_fd_bound(fds[j].fd, NULL, NULL) == 1)
-            {
-                fds[j].events = POLLIN;
-            }
-            else
-            {
-                release_sock_fd( readfds->fd_array[i], fds[j].fd );
-                fds[j].fd = -1;
-                fds[j].events = 0;
-            }
-        }
-    if (writefds)
-        for (i = 0; i < writefds->fd_count; i++, j++)
-        {
-            fds[j].fd = get_sock_fd( writefds->fd_array[i], FILE_WRITE_DATA, NULL );
-            if (fds[j].fd == -1) goto failed;
-            fds[j].revents = 0;
-            if (is_fd_bound(fds[j].fd, NULL, NULL) == 1 ||
-                _get_fd_type(fds[j].fd) == SOCK_DGRAM)
-            {
-                fds[j].events = POLLOUT;
-            }
-            else
-            {
-                release_sock_fd( writefds->fd_array[i], fds[j].fd );
-                fds[j].fd = -1;
-                fds[j].events = 0;
-            }
-        }
-    if (exceptfds)
-        for (i = 0; i < exceptfds->fd_count; i++, j++)
-        {
-            fds[j].fd = get_sock_fd( exceptfds->fd_array[i], 0, NULL );
-            if (fds[j].fd == -1) goto failed;
-            fds[j].revents = 0;
-            if (is_fd_bound(fds[j].fd, NULL, NULL) == 1)
-            {
-                int oob_inlined = 0;
-                socklen_t olen = sizeof(oob_inlined);
-
-                fds[j].events = POLLHUP;
-
-                /* Check if we need to test for urgent data or not */
-                getsockopt(fds[j].fd, SOL_SOCKET, SO_OOBINLINE, (char*) &oob_inlined, &olen);
-                if (!oob_inlined)
-                    fds[j].events |= POLLPRI;
-            }
-            else
-            {
-                release_sock_fd( exceptfds->fd_array[i], fds[j].fd );
-                fds[j].fd = -1;
-                fds[j].events = 0;
-            }
-        }
-    return fds;
-
-failed:
-    count = j;
-    j = 0;
-    if (readfds)
-        for (i = 0; i < readfds->fd_count && j < count; i++, j++)
-            if (fds[j].fd != -1) release_sock_fd( readfds->fd_array[i], fds[j].fd );
-    if (writefds)
-        for (i = 0; i < writefds->fd_count && j < count; i++, j++)
-            if (fds[j].fd != -1) release_sock_fd( writefds->fd_array[i], fds[j].fd );
-    if (exceptfds)
-        for (i = 0; i < exceptfds->fd_count && j < count; i++, j++)
-            if (fds[j].fd != -1) release_sock_fd( exceptfds->fd_array[i], fds[j].fd );
-    return NULL;
+    return 0;
 }
 
-/* release the file descriptor obtained in fd_sets_to_poll */
-/* must be called with the original fd_set arrays, before calling get_poll_results */
-static void release_poll_fds( const WS_fd_set *readfds, const WS_fd_set *writefds,
-                              const WS_fd_set *exceptfds, struct pollfd *fds )
-{
-    unsigned int i, j = 0;
-
-    if (readfds)
-    {
-        for (i = 0; i < readfds->fd_count; i++, j++)
-            if (fds[j].fd != -1) release_sock_fd( readfds->fd_array[i], fds[j].fd );
-    }
-    if (writefds)
-    {
-        for (i = 0; i < writefds->fd_count; i++, j++)
-            if (fds[j].fd != -1) release_sock_fd( writefds->fd_array[i], fds[j].fd );
-    }
-    if (exceptfds)
-    {
-        for (i = 0; i < exceptfds->fd_count; i++, j++)
-        {
-            if (fds[j].fd == -1) continue;
-            release_sock_fd( exceptfds->fd_array[i], fds[j].fd );
-            if (fds[j].revents & POLLHUP)
-            {
-                int fd = get_sock_fd( exceptfds->fd_array[i], 0, NULL );
-                if (fd != -1)
-                    release_sock_fd( exceptfds->fd_array[i], fd );
-                else
-                    fds[j].revents = 0;
-            }
-        }
-    }
-}
-
-static int do_poll(struct pollfd *pollfds, int count, int timeout)
-{
-    struct timeval tv1, tv2;
-    int ret, torig = timeout;
-
-    if (timeout > 0) gettimeofday( &tv1, 0 );
-
-    while ((ret = poll( pollfds, count, timeout )) < 0)
-    {
-        if (errno != EINTR) break;
-        if (timeout < 0) continue;
-        if (timeout == 0) return 0;
-
-        gettimeofday( &tv2, 0 );
-
-        tv2.tv_sec  -= tv1.tv_sec;
-        tv2.tv_usec -= tv1.tv_usec;
-        if (tv2.tv_usec < 0)
-        {
-            tv2.tv_usec += 1000000;
-            tv2.tv_sec  -= 1;
-        }
-
-        timeout = torig - (tv2.tv_sec * 1000) - (tv2.tv_usec + 999) / 1000;
-        if (timeout <= 0) return 0;
-    }
-    return ret;
-}
-
-/* map the poll results back into the Windows fd sets */
-static int get_poll_results( WS_fd_set *readfds, WS_fd_set *writefds, WS_fd_set *exceptfds,
-                             const struct pollfd *fds )
-{
-    const struct pollfd *poll_writefds  = fds + (readfds ? readfds->fd_count : 0);
-    const struct pollfd *poll_exceptfds = poll_writefds + (writefds ? writefds->fd_count : 0);
-    unsigned int i, k, total = 0;
-
-    if (readfds)
-    {
-        for (i = k = 0; i < readfds->fd_count; i++)
-        {
-            if (fds[i].revents ||
-                    (readfds == writefds && (poll_writefds[i].revents & POLLOUT) && !(poll_writefds[i].revents & POLLHUP)) ||
-                    (readfds == exceptfds && poll_exceptfds[i].revents))
-                readfds->fd_array[k++] = readfds->fd_array[i];
-        }
-        readfds->fd_count = k;
-        total += k;
-    }
-    if (writefds && writefds != readfds)
-    {
-        for (i = k = 0; i < writefds->fd_count; i++)
-        {
-            if (((poll_writefds[i].revents & POLLOUT) && !(poll_writefds[i].revents & POLLHUP)) ||
-                    (writefds == exceptfds && poll_exceptfds[i].revents))
-                writefds->fd_array[k++] = writefds->fd_array[i];
-        }
-        writefds->fd_count = k;
-        total += k;
-    }
-    if (exceptfds && exceptfds != readfds && exceptfds != writefds)
-    {
-        for (i = k = 0; i < exceptfds->fd_count; i++)
-            if (poll_exceptfds[i].revents) exceptfds->fd_array[k++] = exceptfds->fd_array[i];
-        exceptfds->fd_count = k;
-        total += k;
-    }
-    return total;
-}
 
 /***********************************************************************
- *		select			(WS2_32.18)
+ *      select   (ws2_32.18)
  */
-int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds,
-                     WS_fd_set *ws_writefds, WS_fd_set *ws_exceptfds,
-                     const struct WS_timeval* ws_timeout)
+int WINAPI WS_select( int count, WS_fd_set *read_ptr, WS_fd_set *write_ptr,
+                      WS_fd_set *except_ptr, const struct WS_timeval *timeout)
 {
-    struct pollfd *pollfds;
-    int count, ret, timeout = -1;
+    char buffer[offsetof( struct afd_poll_params, sockets[WS_FD_SETSIZE * 3] )] = {0};
+    struct afd_poll_params *params = (struct afd_poll_params *)buffer;
+    struct WS_fd_set read, write, except;
+    ULONG params_size, i, j;
+    SOCKET poll_socket = 0;
+    IO_STATUS_BLOCK io;
+    HANDLE sync_event;
+    int ret_count = 0;
+    NTSTATUS status;
 
-    TRACE("read %p, write %p, excp %p timeout %p\n",
-          ws_readfds, ws_writefds, ws_exceptfds, ws_timeout);
+    TRACE( "read %p, write %p, except %p, timeout %p\n", read_ptr, write_ptr, except_ptr, timeout );
 
-    if (!(pollfds = fd_sets_to_poll( ws_readfds, ws_writefds, ws_exceptfds, &count )))
-        return SOCKET_ERROR;
+    WS_FD_ZERO( &read );
+    WS_FD_ZERO( &write );
+    WS_FD_ZERO( &except );
+    if (read_ptr) read = *read_ptr;
+    if (write_ptr) write = *write_ptr;
+    if (except_ptr) except = *except_ptr;
 
-    if (ws_timeout)
-        timeout = (ws_timeout->tv_sec * 1000) + (ws_timeout->tv_usec + 999) / 1000;
+    if (!(sync_event = get_sync_event())) return -1;
 
-    ret = do_poll(pollfds, count, timeout);
-    release_poll_fds( ws_readfds, ws_writefds, ws_exceptfds, pollfds );
+    if (timeout)
+        params->timeout = timeout->tv_sec * -10000000 + timeout->tv_usec * -10;
+    else
+        params->timeout = TIMEOUT_INFINITE;
 
-    if (ret == -1) SetLastError(wsaErrno());
-    else ret = get_poll_results( ws_readfds, ws_writefds, ws_exceptfds, pollfds );
-    return ret;
+    for (i = 0; i < read.fd_count; ++i)
+    {
+        params->sockets[params->count].socket = read.fd_array[i];
+        params->sockets[params->count].flags = AFD_POLL_READ | AFD_POLL_ACCEPT | AFD_POLL_HUP;
+        ++params->count;
+        poll_socket = read.fd_array[i];
+    }
+
+    for (i = 0; i < write.fd_count; ++i)
+    {
+        params->sockets[params->count].socket = write.fd_array[i];
+        params->sockets[params->count].flags = AFD_POLL_WRITE;
+        ++params->count;
+        poll_socket = write.fd_array[i];
+    }
+
+    for (i = 0; i < except.fd_count; ++i)
+    {
+        params->sockets[params->count].socket = except.fd_array[i];
+        params->sockets[params->count].flags = AFD_POLL_OOB | AFD_POLL_CONNECT_ERR;
+        ++params->count;
+        poll_socket = except.fd_array[i];
+    }
+
+    if (!params->count)
+    {
+        SetLastError( WSAEINVAL );
+        return -1;
+    }
+
+    params_size = offsetof( struct afd_poll_params, sockets[params->count] );
+
+    status = NtDeviceIoControlFile( (HANDLE)poll_socket, sync_event, NULL, NULL, &io,
+                                    IOCTL_AFD_POLL, params, params_size, params, params_size );
+    if (status == STATUS_PENDING)
+    {
+        if (WaitForSingleObject( sync_event, INFINITE ) == WAIT_FAILED)
+            return -1;
+        status = io.u.Status;
+    }
+    if (status == STATUS_TIMEOUT) status = STATUS_SUCCESS;
+    if (!status)
+    {
+        /* pointers may alias, so clear them all first */
+        if (read_ptr) WS_FD_ZERO( read_ptr );
+        if (write_ptr) WS_FD_ZERO( write_ptr );
+        if (except_ptr) WS_FD_ZERO( except_ptr );
+
+        for (i = 0; i < params->count; ++i)
+        {
+            unsigned int flags = params->sockets[i].flags;
+            SOCKET s = params->sockets[i].socket;
+
+            for (j = 0; j < read.fd_count; ++j)
+            {
+                if (read.fd_array[j] == s
+                        && (flags & (AFD_POLL_READ | AFD_POLL_ACCEPT | AFD_POLL_HUP | AFD_POLL_CLOSE)))
+                {
+                    ret_count += add_fd_to_set( s, read_ptr );
+                    flags &= ~AFD_POLL_CLOSE;
+                }
+            }
+
+            if (flags & AFD_POLL_CLOSE)
+                status = STATUS_INVALID_HANDLE;
+
+            for (j = 0; j < write.fd_count; ++j)
+            {
+                if (write.fd_array[j] == s && (flags & AFD_POLL_WRITE))
+                    ret_count += add_fd_to_set( s, write_ptr );
+            }
+
+            for (j = 0; j < except.fd_count; ++j)
+            {
+                if (except.fd_array[j] == s && (flags & (AFD_POLL_OOB | AFD_POLL_CONNECT_ERR)))
+                    ret_count += add_fd_to_set( s, except_ptr );
+            }
+        }
+    }
+
+    SetLastError( NtStatusToWSAError( status ) );
+    return status ? -1 : ret_count;
 }
 
 
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 7b92d0b4e84..01c14382e84 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -3127,6 +3127,14 @@ static void test_select(void)
     ret = select(0, &readfds, &writefds, &exceptfds, &select_timeout);
     ok(ret == 1, "expected 1, got %d\n", ret);
     ok(FD_ISSET(fdRead, &readfds), "fdRead socket is not in the set\n");
+    ok(!FD_ISSET(fdRead, &exceptfds), "fdRead socket is in the set\n");
+    FD_ZERO_ALL();
+    FD_SET_ALL(fdRead);
+    ret = select(0, &readfds, &writefds, &exceptfds, &select_timeout);
+    ok(ret == 2, "expected 1, got %d\n", ret);
+    ok(FD_ISSET(fdRead, &readfds), "fdRead socket is not in the set\n");
+    ok(FD_ISSET(fdRead, &writefds), "fdRead socket is not in the set\n");
+    ok(!FD_ISSET(fdRead, &exceptfds), "fdRead socket is in the set\n");
     ret = recv(fdRead, tmp_buf, sizeof(tmp_buf), 0);
     ok(ret == 4, "expected 4, got %d\n", ret);
     ok(!strcmp(tmp_buf, "1234"), "data received differs from sent\n");
@@ -3239,9 +3247,9 @@ static void test_select(void)
     FD_SET(fdWrite, &exceptfds);
     SetLastError(0xdeadbeef);
     ret = select(0, NULL, NULL, &exceptfds, &select_timeout);
-todo_wine
     ok(ret == SOCKET_ERROR, "expected -1, got %d\n", ret);
     ok(GetLastError() == WSAENOTSOCK, "got %d\n", GetLastError());
+    ok(!FD_ISSET(fdWrite, &exceptfds), "fdWrite socket is in the set\n");
     WaitForSingleObject (thread_handle, 1000);
     closesocket(fdRead);
 
diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h
index 8baa62908dd..b70f4a1595d 100644
--- a/dlls/ws2_32/ws2_32_private.h
+++ b/dlls/ws2_32/ws2_32_private.h
@@ -190,8 +190,6 @@ struct per_thread_data
     struct WS_hostent *he_buffer;
     struct WS_servent *se_buffer;
     struct WS_protoent *pe_buffer;
-    struct pollfd *fd_cache;
-    unsigned int fd_count;
     int he_len;
     int se_len;
     int pe_len;
-- 
2.30.2




More information about the wine-devel mailing list