[PATCH] Big winsock2 / overlapped patch

Martin Wilck Martin.Wilck at fujitsu-siemens.com
Fri Apr 19 11:40:24 CDT 2002


Hi,

This stuff really approaches a usable state!

I know the patch is much too big but I have no time now to split it into
pieces before next week.

However, I am so excited to see the close events coming at the right time
that I must post it here and encourage people to try it.

Patch summary:
- Implement overlapped IO on sockets and the necessary changes in
  overlapped request handling.
- Fix socket behaviour after FD_CLOSE received, when socket has been taken
  out of the server's main select loop.
- Fix some more aspects of FD_CLOSE.
- Fix WSAAccept() deferred accept.
- Implement WSAGetOverlappedResult and WSAConnect().
- Some improvements for error code handling in Winsock2.

Tested: so far with my test programs only, but they really tries to make
        things hard for wine.

Patch against yesterday's (2002/04/18) CVS version of wine.

Have fun,
Martin

-- 
Martin Wilck                Phone: +49 5251 8 15113
Fujitsu Siemens Computers   Fax:   +49 5251 8 20409
Heinz-Nixdorf-Ring 1	    mailto:Martin.Wilck at Fujitsu-Siemens.com
D-33106 Paderborn           http://www.fujitsu-siemens.com/primergy


diff -ruNX ignore TMP/wine/dlls/winsock/socket.c MW/wine/dlls/winsock/socket.c
--- TMP/wine/dlls/winsock/socket.c	Thu Apr 18 16:26:02 2002
+++ MW/wine/dlls/winsock/socket.c	Fri Apr 19 15:51:52 2002
@@ -104,6 +104,7 @@
 #include "wine/winbase16.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "winerror.h"
 #include "winsock2.h"
 #include "ws2tcpip.h"
 #include "wsipx.h"
@@ -125,6 +126,53 @@
                         inet_ntoa(((struct sockaddr_in *)a)->sin_addr), \
                         ntohs(((struct sockaddr_in *)a)->sin_port))

+/****************************************************************
+ * Async IO declarations
+ ****************************************************************/
+#include "async.h"
+
+static DWORD ws2_async_get_status (const struct async_private *ovp);
+static DWORD ws2_async_get_count  (const struct async_private *ovp);
+static void  ws2_async_set_status (struct async_private *ovp, const DWORD status);
+static void CALLBACK ws2_async_call_completion (ULONG_PTR data);
+static void ws2_async_cleanup ( struct async_private *ovp );
+
+static struct async_ops ws2_async_ops =
+{
+    ws2_async_get_status,
+    ws2_async_set_status,
+    ws2_async_get_count,
+    ws2_async_call_completion,
+    ws2_async_cleanup
+};
+
+static struct async_ops ws2_nocomp_async_ops =
+{
+    ws2_async_get_status,
+    ws2_async_set_status,
+    ws2_async_get_count,
+    NULL,                     /* call_completion */
+    ws2_async_cleanup
+};
+
+typedef struct ws2_async
+{
+    async_private                       async;
+    LPWSAOVERLAPPED                     overlapped;
+    LPWSAOVERLAPPED                     user_overlapped;
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE  completion_func;
+    struct iovec                        *iovec;
+    int                                 n_iovecs;
+    struct WS_sockaddr            *addr;
+    union {
+        int val;     /* for send operations */
+        int *ptr;    /* for recv operations */
+    }                                   addrlen;
+    DWORD                               flags;
+} ws2_async;
+
+/****************************************************************/
+
 /* ----------------------------------- internal data */

 /* ws_... struct conversion flags */
@@ -223,13 +271,39 @@
  */
 static int opentype = 0;

+inline static DWORD NtStatusToWSAError ( const DWORD status )
+{
+    /* We only need to cover the status codes set by server async request handling */
+    DWORD wserr;
+    switch ( status )
+    {
+    case STATUS_SUCCESS:              wserr = 0;                     break;
+    case STATUS_PENDING:              wserr = WSA_IO_PENDING;        break;
+    case STATUS_INVALID_HANDLE:       wserr = WSAENOTSOCK;           break;  /* WSAEBADF ? */
+    case STATUS_INVALID_PARAMETER:    wserr = WSAEINVAL;             break;
+    case STATUS_PIPE_DISCONNECTED:    wserr = WSAESHUTDOWN;          break;
+    case STATUS_CANCELLED:            wserr = WSA_OPERATION_ABORTED; break;
+    case STATUS_TIMEOUT:              wserr = WSAETIMEDOUT;          break;
+    case STATUS_NO_MEMORY:            wserr = WSAEFAULT;             break;
+    default:
+        if ( status >= WSABASEERR && status <= WSABASEERR+1004 )
+            /* It is not a NT status code but a winsock error */
+            wserr = status;
+        else
+        {
+            wserr = RtlNtStatusToDosError( status );
+            FIXME ( "Status code %08lx converted to DOS error code %lx\n", status, wserr );
+        }
+    }
+    return wserr;
+}
+
 /* set last error code from NT status without mapping WSA errors */
 inline static unsigned int set_error( unsigned int err )
 {
     if (err)
     {
-        /* do not map WSA errors */
-        if ((err < WSABASEERR) || (err >= 0x10000000)) err = RtlNtStatusToDosError(err);
+        err = NtStatusToWSAError ( err );
         SetLastError( err );
     }
     return err;
@@ -245,10 +319,17 @@
     return fd;
 }

-inline static int _get_sock_fd_type( SOCKET s, enum fd_type *type, int *flags )
+inline static int _get_sock_fd_type( SOCKET s, DWORD access, enum fd_type *type, int *flags )
 {
     int fd;
-    if (set_error( wine_server_handle_to_fd( s, GENERIC_READ, &fd, type, flags ) )) return -1;
+    if (set_error( wine_server_handle_to_fd( s, access, &fd, type, flags ) )) return -1;
+    if ( ( (access & GENERIC_READ)  && (*flags & FD_FLAG_RECV_SHUTDOWN ) ) ||
+         ( (access & GENERIC_WRITE) && (*flags & FD_FLAG_SEND_SHUTDOWN ) ) )
+    {
+        close (fd);
+        WSASetLastError ( WSAESHUTDOWN );
+        return -1;
+    }
     return fd;
 }

@@ -904,6 +985,399 @@
         free((void*)uaddr);
 }

+/**************************************************************************
+ * Functions for handling overlapped I/O
+ **************************************************************************/
+
+static DWORD ws2_async_get_status (const struct async_private *ovp)
+{
+    return ((ws2_async*) ovp)->overlapped->Internal;
+}
+
+static VOID ws2_async_set_status (struct async_private *ovp, const DWORD status)
+{
+    ((ws2_async*) ovp)->overlapped->Internal = status;
+}
+
+static DWORD ws2_async_get_count (const struct async_private *ovp)
+{
+    return ((ws2_async*) ovp)->overlapped->InternalHigh;
+}
+
+static void ws2_async_cleanup ( struct async_private *ap )
+{
+    struct ws2_async *as = (struct ws2_async*) ap;
+
+    TRACE ( "as: %p uovl %p ovl %p\n", as, as->user_overlapped, as->overlapped );
+    if ( !as->user_overlapped )
+    {
+        if ( as->overlapped->hEvent != INVALID_HANDLE_VALUE )
+            WSACloseEvent ( as->overlapped->hEvent  );
+        HeapFree ( GetProcessHeap(), 0, as->overlapped );
+    }
+
+    if ( as->iovec )
+        HeapFree ( GetProcessHeap(), 0, as->iovec );
+
+    HeapFree ( GetProcessHeap(), 0, as );
+}
+
+static void CALLBACK ws2_async_call_completion (ULONG_PTR data)
+{
+    ws2_async* as = (ws2_async*) data;
+
+    TRACE ("data: %p\n", as);
+
+    as->completion_func ( NtStatusToWSAError (as->overlapped->Internal),
+                          as->overlapped->InternalHigh,
+                          as->user_overlapped,
+                          as->flags );
+    ws2_async_cleanup ( &as->async );
+}
+
+/***********************************************************************
+ *              WS2_make_async          (INTERNAL)
+ */
+
+static void WS2_async_recv (async_private *as);
+static void WS2_async_send (async_private *as);
+
+inline static struct ws2_async*
+WS2_make_async (SOCKET s, int fd, int type, struct iovec *iovec, DWORD dwBufferCount,
+                LPDWORD lpFlags, struct WS_sockaddr *addr,
+                LPINT addrlen, LPWSAOVERLAPPED lpOverlapped,
+                LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
+{
+    struct ws2_async *wsa = HeapAlloc ( GetProcessHeap(), 0, sizeof ( ws2_async ) );
+
+    TRACE ( "wsa %p\n", wsa );
+
+    if (!wsa)
+        return NULL;
+
+    wsa->async.ops = ( lpCompletionRoutine ? &ws2_async_ops : &ws2_nocomp_async_ops );
+    wsa->async.handle = (HANDLE) s;
+    wsa->async.fd = fd;
+    wsa->async.type = type;
+    switch (type)
+    {
+    case ASYNC_TYPE_READ:
+        wsa->flags = *lpFlags;
+        wsa->async.func = WS2_async_recv;
+        wsa->addrlen.ptr = addrlen;
+        break;
+    case ASYNC_TYPE_WRITE:
+        wsa->flags = 0;
+        wsa->async.func = WS2_async_send;
+        wsa->addrlen.val = *addrlen;
+        break;
+    default:
+        ERR ("Invalid async type: %d\n", type);
+    }
+    wsa->user_overlapped = lpOverlapped;
+    wsa->completion_func = lpCompletionRoutine;
+    wsa->iovec = iovec;
+    wsa->n_iovecs = dwBufferCount;
+    wsa->addr = addr;
+
+    if ( lpOverlapped )
+    {
+        wsa->overlapped = lpOverlapped;
+        wsa->async.event = ( lpCompletionRoutine ? INVALID_HANDLE_VALUE : lpOverlapped->hEvent );
+    }
+    else
+    {
+        wsa->overlapped = HeapAlloc ( GetProcessHeap(), 0,
+                                      sizeof (WSAOVERLAPPED) );
+        if ( !wsa->overlapped )
+            goto error;
+        wsa->async.event = wsa->overlapped->hEvent = INVALID_HANDLE_VALUE;
+    }
+
+    wsa->overlapped->InternalHigh = 0;
+    TRACE ( "wsa %p, ops %p, h %d, ev %d, fd %d, func %p, ov %p, uov %p, cfunc %p\n",
+            wsa, wsa->async.ops, wsa->async.handle, wsa->async.event, wsa->async.fd, wsa->async.func,
+            wsa->overlapped, wsa->user_overlapped, wsa->completion_func );
+
+    return wsa;
+
+error:
+    TRACE ("Error\n");
+    HeapFree ( GetProcessHeap(), 0, wsa );
+    return NULL;
+}
+
+/***********************************************************************
+ *              WS2_recv                (INTERNAL)
+ *
+ * Work horse for both synchronous and asynchronous recv() operations.
+ */
+static int WS2_recv ( int fd, struct iovec* iov, int count,
+                      struct WS_sockaddr *lpFrom, LPINT lpFromlen,
+                      LPDWORD lpFlags )
+{
+    struct msghdr hdr;
+    int n;
+    TRACE ( "fd %d, iovec %p, count %d addr %p, len %p, flags %lx\n",
+            fd, iov, count, lpFrom, lpFromlen, *lpFlags);
+
+    hdr.msg_name = NULL;
+
+    if ( lpFrom )
+    {
+#if DEBUG_SOCKADDR
+        dump_sockaddr (lpFrom);
+#endif
+
+        hdr.msg_namelen = *lpFromlen;
+        hdr.msg_name = ws_sockaddr_alloc ( lpFrom, lpFromlen, &hdr.msg_namelen );
+        if ( !hdr.msg_name )
+        {
+            WSASetLastError ( WSAEFAULT );
+            n = -1;
+            goto out;
+        }
+    }
+    else
+        hdr.msg_namelen = 0;
+
+    hdr.msg_iov = iov;
+    hdr.msg_iovlen = count;
+    hdr.msg_control = NULL;
+    hdr.msg_controllen = 0;
+    hdr.msg_flags = 0;
+
+    if ( (n = recvmsg (fd, &hdr, *lpFlags)) == -1 )
+    {
+        TRACE ( "recvmsg error %d\n", errno);
+        goto out;
+    }
+
+    if ( lpFrom &&
+         ws_sockaddr_u2ws ( hdr.msg_name, hdr.msg_namelen,
+                            lpFrom, lpFromlen ) != 0 )
+    {
+        /* The from buffer was too small, but we read the data
+         * anyway. Is that really bad?
+         */
+        WSASetLastError ( WSAEFAULT );
+        WARN ( "Address buffer too small\n" );
+    }
+
+out:
+
+    ws_sockaddr_free ( hdr.msg_name, lpFrom );
+    TRACE ("-> %d\n", n);
+    return n;
+}
+
+/***********************************************************************
+ *              WS2_async_recv          (INTERNAL)
+ *
+ * Handler for overlapped recv() operations.
+ */
+static void WS2_async_recv ( async_private *as )
+{
+    ws2_async* wsa = (ws2_async*) as;
+    int result, err;
+
+    TRACE ( "async %p\n", wsa );
+
+    if ( wsa->overlapped->Internal != STATUS_PENDING )
+    {
+        TRACE ( "status: %ld\n", wsa->overlapped->Internal );
+        return;
+    }
+
+    result = WS2_recv ( wsa->async.fd, wsa->iovec, wsa->n_iovecs,
+                        wsa->addr, wsa->addrlen.ptr, &wsa->flags );
+
+    if (result >= 0)
+    {
+        wsa->overlapped->Internal = STATUS_SUCCESS;
+        wsa->overlapped->InternalHigh = result;
+        TRACE ( "received %d bytes\n", result );
+        _enable_event ( (SOCKET) wsa->async.handle, FD_READ, 0, 0 );
+        return;
+    }
+
+    err = wsaErrno ();
+    if ( err == WSAEINTR || err == WSAEWOULDBLOCK )  /* errno: EINTR / EAGAIN */
+    {
+        wsa->overlapped->Internal = STATUS_PENDING;
+        _enable_event ( (SOCKET) wsa->async.handle, FD_READ, 0, 0 );
+        TRACE ( "still pending\n" );
+    }
+    else
+    {
+        wsa->overlapped->Internal = err;
+        TRACE ( "Error: %x\n", err );
+    }
+}
+
+/***********************************************************************
+ *              WS2_send                (INTERNAL)
+ *
+ * Work horse for both synchronous and asynchronous send() operations.
+ */
+static int WS2_send ( int fd, struct iovec* iov, int count,
+                      const struct WS_sockaddr *to, INT tolen, DWORD dwFlags )
+{
+    struct msghdr hdr;
+    int n = -1;
+    TRACE ( "fd %d, iovec %p, count %d addr %p, len %d, flags %lx\n",
+            fd, iov, count, to, tolen, dwFlags);
+
+    hdr.msg_name = NULL;
+
+    if ( to )
+    {
+#if DEBUG_SOCKADDR
+        dump_sockaddr (to);
+#endif
+        hdr.msg_name = (struct sockaddr*) ws_sockaddr_ws2u ( to, tolen, &hdr.msg_namelen );
+        if ( !hdr.msg_name )
+        {
+            WSASetLastError ( WSAEFAULT );
+            goto out;
+        }
+    }
+    else
+        hdr.msg_namelen = 0;
+
+    hdr.msg_iov = iov;
+    hdr.msg_iovlen = count;
+    hdr.msg_control = NULL;
+    hdr.msg_controllen = 0;
+    hdr.msg_flags = 0;
+
+    n = sendmsg (fd, &hdr, dwFlags);
+
+out:
+    ws_sockaddr_free ( hdr.msg_name, to );
+    return n;
+}
+
+/***********************************************************************
+ *              WS2_async_send          (INTERNAL)
+ *
+ * Handler for overlapped send() operations.
+ */
+static void WS2_async_send ( async_private *as )
+{
+    ws2_async* wsa = (ws2_async*) as;
+    int result, err;
+
+    TRACE ( "async %p\n", wsa );
+
+    if ( wsa->overlapped->Internal != STATUS_PENDING )
+    {
+        TRACE ( "status: %ld\n", wsa->overlapped->Internal );
+        return;
+    }
+
+    result = WS2_send ( wsa->async.fd, wsa->iovec, wsa->n_iovecs,
+                        wsa->addr, wsa->addrlen.val, wsa->flags );
+
+    if (result >= 0)
+    {
+        wsa->overlapped->Internal = STATUS_SUCCESS;
+        wsa->overlapped->InternalHigh = result;
+        TRACE ( "sent %d bytes\n", result );
+        _enable_event ( (SOCKET) wsa->async.handle, FD_WRITE, 0, 0 );
+        return;
+    }
+
+    err = wsaErrno ();
+    if ( err == WSAEINTR )
+    {
+        wsa->overlapped->Internal = STATUS_PENDING;
+        _enable_event ( (SOCKET) wsa->async.handle, FD_WRITE, 0, 0 );
+        TRACE ( "still pending\n" );
+    }
+    else
+    {
+        /* We set the status to a winsock error code and check for that
+           later in NtStatusToWSAError () */
+        wsa->overlapped->Internal = err;
+        TRACE ( "Error: %x\n", err );
+    }
+}
+
+/***********************************************************************
+ *              WS2_async_shutdown      (INTERNAL)
+ *
+ * Handler for shutdown() operations on overlapped sockets.
+ */
+static void WS2_async_shutdown ( async_private *as )
+{
+    ws2_async* wsa = (ws2_async*) as;
+    int err = 1;
+
+    TRACE ( "async %p %d\n", wsa, wsa->async.type );
+    switch ( wsa->async.type )
+    {
+    case ASYNC_TYPE_READ:
+        err = shutdown ( wsa->async.fd, 0 );
+        break;
+    case ASYNC_TYPE_WRITE:
+        err = shutdown ( wsa->async.fd, 1 );
+        break;
+    default:
+        ERR ("invalid type: %d\n", wsa->async.type );
+    }
+
+    if ( err )
+        wsa->overlapped->Internal = wsaErrno ();
+    else
+        wsa->overlapped->Internal = STATUS_SUCCESS;
+}
+
+/***********************************************************************
+ *  WS2_register_async_shutdown         (INTERNAL)
+ *
+ * Helper function for WS_shutdown() on overlapped sockets.
+ */
+static int WS2_register_async_shutdown ( SOCKET s, int fd, int type )
+{
+    struct ws2_async *wsa;
+    int ret, err = WSAEFAULT;
+    DWORD dwflags = 0;
+    int len = 0;
+    LPWSAOVERLAPPED ovl = HeapAlloc (GetProcessHeap(), 0, sizeof ( WSAOVERLAPPED ));
+
+    TRACE ("s %d fd %d type %d\n", s, fd, type);
+    if (!ovl)
+        goto out;
+
+    ovl->hEvent = WSACreateEvent ();
+    if ( ovl->hEvent == WSA_INVALID_EVENT  )
+        goto out_free;
+
+    wsa = WS2_make_async ( s, fd, type, NULL, 0,
+                           &dwflags, NULL, &len, ovl, NULL );
+    if ( !wsa )
+        goto out_close;
+
+    /* Hack: this will cause ws2_async_cleanup() to free the overlapped structure */
+    wsa->user_overlapped = NULL;
+    wsa->async.func = WS2_async_shutdown;
+    if ( (ret = register_new_async ( &wsa->async )) )
+    {
+        err = NtStatusToWSAError ( ret );
+        ws2_async_cleanup ( &wsa->async );
+        goto out;
+    }
+    return 0;
+
+out_close:
+    WSACloseEvent ( ovl->hEvent );
+out_free:
+    HeapFree ( GetProcessHeap(), 0, ovl );
+out:
+    return err;
+}
+
 /***********************************************************************
  *		accept		(WS2_32.1)
  */
@@ -1139,6 +1613,19 @@
 }

 /***********************************************************************
+ *              WSAConnect             (WS2_32.30)
+ */
+int WINAPI WSAConnect ( SOCKET s, const struct WS_sockaddr* name, int namelen,
+                        LPWSABUF lpCallerData, LPWSABUF lpCalleeData,
+                        LPQOS lpSQOS, LPQOS lpGQOS )
+{
+    if ( lpCallerData || lpCalleeData || lpSQOS || lpGQOS )
+        FIXME ("unsupported parameters!\n");
+    return WS_connect ( s, name, namelen );
+}
+
+
+/***********************************************************************
  *		getpeername		(WS2_32.5)
  */
 int WINAPI WS_getpeername(SOCKET s, struct WS_sockaddr *name, int *namelen)
@@ -1982,25 +2469,29 @@
                       LPWSAOVERLAPPED lpOverlapped,
                       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
 {
-    int i, n, fd, err = WSAENOTSOCK, flags;
+    int i, n, fd, err = WSAENOTSOCK, flags, ret;
     struct iovec* iovec;
-    struct msghdr msghdr;
+    struct ws2_async *wsa;
     enum fd_type type;

     TRACE ("socket %04x, wsabuf %p, nbufs %ld, flags %ld, to %p, tolen %d, ovl %p, func %p\n",
            s, lpBuffers, dwBufferCount, dwFlags,
            to, tolen, lpOverlapped, lpCompletionRoutine);

-    fd = _get_sock_fd_type( s, &type, &flags );
+    fd = _get_sock_fd_type( s, GENERIC_WRITE, &type, &flags );
+    TRACE ( "fd=%d, type=%d, flags=%x\n", fd, type, flags );

     if ( fd == -1 )
+    {
+        err = WSAGetLastError ();
         goto error;
+    }

     iovec = WS_ALLOC ( dwBufferCount * sizeof (struct iovec) );

     if ( !iovec )
     {
-        err = WSAENOBUFS;
+        err = WSAEFAULT;
         goto err_close;
     }

@@ -2010,30 +2501,42 @@
         iovec[i].iov_len  = lpBuffers[i].len;
     }

-    msghdr.msg_name = NULL;
-
-    if (to)
+    if ( (lpOverlapped || lpCompletionRoutine) && flags & FD_FLAG_OVERLAPPED )
     {
-#if DEBUG_SOCKADDR
-        dump_sockaddr (to);
-#endif
-        msghdr.msg_name = (void*) ws_sockaddr_ws2u (to, tolen, &msghdr.msg_namelen);
-        if ( !msghdr.msg_name )
+        wsa = WS2_make_async ( s, fd, ASYNC_TYPE_WRITE, iovec, dwBufferCount,
+                               &dwFlags, (struct WS_sockaddr*) to, &tolen,
+                               lpOverlapped, lpCompletionRoutine );
+        if ( !wsa )
         {
             err = WSAEFAULT;
             goto err_free;
         }
-    }
-    else
-        msghdr.msg_namelen = 0;
+
+        if ( ( ret = register_new_async ( &wsa->async )) )
+        {
+            err = NtStatusToWSAError ( ret );
+
+            if ( !lpOverlapped )
+                HeapFree ( GetProcessHeap(), 0, wsa->overlapped );
+            HeapFree ( GetProcessHeap(), 0, wsa );
+            goto err_free;
+        }

-    msghdr.msg_iov = iovec;
-    msghdr.msg_iovlen = dwBufferCount;
-    msghdr.msg_control = NULL;
-    msghdr.msg_controllen = 0;
-    msghdr.msg_flags = 0;
+        /* Try immediate completion */
+        if ( lpOverlapped )
+        {
+            if  ( WSAGetOverlappedResult ( (HANDLE) s, lpOverlapped,
+                                           lpNumberOfBytesSent, FALSE, &dwFlags) )
+                return 0;
+
+            if ( (err = WSAGetLastError ()) != WSA_IO_INCOMPLETE )
+                goto error;

-    /* FIXME: Treat overlapped IO here */
+        }
+
+        WSASetLastError (WSA_IO_PENDING);
+        return SOCKET_ERROR;
+    }

     if (_is_blocking(s))
     {
@@ -2041,9 +2544,8 @@
         do_block(fd, 2);
     }

-    /* FIXME: can we support MSG_PARTIAL ? How does it relate to sendmsg()'s msg_flags ? */
-
-    if ((n = sendmsg (fd, &msghdr, dwFlags)) == -1)
+    n = WS2_send ( fd, iovec, dwBufferCount, to, tolen, dwFlags );
+    if ( n == -1 )
     {
         err = wsaErrno();
         if ( err == WSAEWOULDBLOCK )
@@ -2051,24 +2553,22 @@
         goto err_free;
     }

+    TRACE(" -> %i bytes\n", n);
     *lpNumberOfBytesSent = n;

-    ws_sockaddr_free ( msghdr.msg_name, to );
-    WS_FREE ( iovec );
+    HeapFree ( GetProcessHeap(), 0, iovec );
     close ( fd );
-
     return 0;

 err_free:
-    ws_sockaddr_free ( msghdr.msg_name, to );
-    WS_FREE ( iovec );
+    HeapFree ( GetProcessHeap(), 0, iovec );

 err_close:
     close ( fd );

 error:
     WARN (" -> ERROR %d\n", err);
-    SetLastError (err);
+    WSASetLastError (err);
     return SOCKET_ERROR;
 }

@@ -2195,55 +2695,88 @@
     return (INT16)WS_setsockopt( s, (UINT16)level, optname, optval, optlen );
 }

-
 /***********************************************************************
  *		shutdown		(WS2_32.22)
  */
 int WINAPI WS_shutdown(SOCKET s, int how)
 {
-    int fd = _get_sock_fd(s);
+    int fd, fd0 = -1, fd1 = -1, flags, err = WSAENOTSOCK;
+    enum fd_type type;
+    unsigned int clear_flags = 0;

-    TRACE("socket %04x, how %i\n", s, how );
-    if (fd != -1)
+    fd = _get_sock_fd_type ( s, 0, &type, &flags );
+    TRACE("socket %04x, how %i %d %d \n", s, how, type, flags );
+
+    if (fd == -1)
+        return SOCKET_ERROR;
+
+    switch( how )
     {
-	    switch( how )
-	    {
-		case 0: /* drop receives */
-			_enable_event(s, 0, 0, FD_READ);
-#ifdef SHUT_RD
-			how = SHUT_RD;
-#endif
-			break;
-
-		case 1: /* drop sends */
-			_enable_event(s, 0, 0, FD_WRITE);
-#ifdef SHUT_WR
-			how = SHUT_WR;
-#endif
-			break;
-
-		case 2: /* drop all */
-#ifdef SHUT_RDWR
-			how = SHUT_RDWR;
-#endif
-		default:
-			WSAAsyncSelect( s, 0, 0, 0 );
-			break;
-	    }
+    case 0: /* drop receives */
+        clear_flags |= FD_READ;
+        break;
+    case 1: /* drop sends */
+        clear_flags |= FD_WRITE;
+        break;
+    case 2: /* drop all */
+        clear_flags |= FD_READ|FD_WRITE;
+    default:
+        clear_flags |= FD_WINE_CONNECTED|FD_WINE_LISTENING;
+    }
+
+    if ( flags & FD_FLAG_OVERLAPPED ) {
+
+        switch ( how )
+        {
+        case SD_RECEIVE:
+            fd0 = fd;
+            break;
+        case SD_SEND:
+            fd1 = fd;
+            break;
+        case SD_BOTH:
+        default:
+            fd0 = fd;
+            fd1 = _get_sock_fd ( s );
+        }

-	if (shutdown(fd, how) == 0)
-	{
-	    if( how > 1 )
-	    {
-		_enable_event(s, 0, 0, FD_WINE_CONNECTED|FD_WINE_LISTENING);
-	    }
-	    close(fd);
-	    return 0;
-	}
-	SetLastError(wsaErrno());
-	close(fd);
-    }
-    else SetLastError(WSAENOTSOCK);
+        if ( fd0 != -1 )
+        {
+            err = WS2_register_async_shutdown ( s, fd0, ASYNC_TYPE_READ );
+            if ( err )
+            {
+                close ( fd0 );
+                goto error;
+            }
+        }
+        if ( fd1 != -1 )
+        {
+            err = WS2_register_async_shutdown ( s, fd1, ASYNC_TYPE_WRITE );
+            if ( err )
+            {
+                close ( fd1 );
+                goto error;
+            }
+        }
+    }
+    else /* non-overlapped mode */
+    {
+        if ( shutdown( fd, how ) )
+        {
+            err = wsaErrno ();
+            close ( fd );
+            goto error;
+        }
+        close(fd);
+    }
+
+    _enable_event( s, 0, 0, clear_flags );
+    if ( how > 1) WSAAsyncSelect( s, 0, 0, 0 );
+    return 0;
+
+error:
+    _enable_event( s, 0, 0, clear_flags );
+    WSASetLastError ( err );
     return SOCKET_ERROR;
 }

@@ -2672,6 +3205,44 @@
     return SOCKET_ERROR;
 }

+/**********************************************************************
+ *      WSAGetOverlappedResult (WS2_32.40)
+ */
+BOOL WINAPI WSAGetOverlappedResult ( SOCKET s, LPWSAOVERLAPPED lpOverlapped,
+                                     LPDWORD lpcbTransfer, BOOL fWait,
+                                     LPDWORD lpdwFlags )
+{
+    DWORD r;
+
+    TRACE ( "socket %d ovl %p trans %p, wait %d flags %p\n",
+            s, lpOverlapped, lpcbTransfer, fWait, lpdwFlags );
+
+    if ( !(lpOverlapped && lpOverlapped->hEvent) )
+    {
+        ERR ( "Invalid pointer\n" );
+        WSASetLastError (WSA_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    do {
+        r = WaitForSingleObjectEx (lpOverlapped->hEvent, fWait ? INFINITE : 0, TRUE);
+    } while (r == STATUS_USER_APC);
+
+    if ( lpcbTransfer )
+        *lpcbTransfer = lpOverlapped->InternalHigh;
+
+    if ( lpdwFlags )
+        *lpdwFlags = lpOverlapped->Offset;
+
+    if ( r == WAIT_OBJECT_0 )
+        return TRUE;
+
+    WSASetLastError ( lpOverlapped->Internal == STATUS_PENDING ?
+                      WSA_IO_INCOMPLETE : NtStatusToWSAError ( lpOverlapped->Internal ) );
+    return FALSE;
+}
+
+
 /***********************************************************************
  *      WSAAsyncSelect			(WS2_32.101)
  */
@@ -3250,7 +3821,6 @@
     }
 }

-
 /***********************************************************************
  *		WSARecv			(WS2_32.67)
  */
@@ -3272,11 +3842,9 @@
                         LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )

 {
-    /* Uses recvmsg() in order to provide scatter-gather I/O */
-
+    int i, n, fd, err = WSAENOTSOCK, flags, ret;
     struct iovec* iovec;
-    struct msghdr msghdr;
-    int fd, i, length, err = WSAENOTSOCK, flags;
+    struct ws2_async *wsa;
     enum fd_type type;

     TRACE("socket %04x, wsabuf %p, nbufs %ld, flags %ld, from %p, fromlen %ld, ovl %p, func %p\n",
@@ -3284,19 +3852,19 @@
           (lpFromlen ? *lpFromlen : -1L),
           lpOverlapped, lpCompletionRoutine);

-    fd = _get_sock_fd_type( s, &type, &flags );
+    fd = _get_sock_fd_type( s, GENERIC_READ, &type, &flags );
+    TRACE ( "fd=%d, type=%d, flags=%x\n", fd, type, flags );

     if (fd == -1)
     {
-        err = WSAENOTSOCK;
+        err = WSAGetLastError ();
         goto error;
     }

-    /* FIXME: should this be HeapAlloc() or WS_ALLOC ? */
-    iovec = WS_ALLOC ( dwBufferCount * sizeof (struct iovec) );
+    iovec = HeapAlloc ( GetProcessHeap(), 0, dwBufferCount * sizeof (struct iovec) );
     if ( !iovec )
     {
-        err = WSAENOBUFS;
+        err = WSAEFAULT;
         goto err_close;
     }

@@ -3306,74 +3874,77 @@
         iovec[i].iov_len  = lpBuffers[i].len;
     }

-    msghdr.msg_name = NULL;
-
-    if ( lpFrom )
+    if ( (lpOverlapped || lpCompletionRoutine) && flags & FD_FLAG_OVERLAPPED )
     {
-#if DEBUG_SOCKADDR
-        dump_sockaddr (lpFrom);
-#endif
+        wsa = WS2_make_async ( s, fd, ASYNC_TYPE_READ, iovec, dwBufferCount,
+                               lpFlags, lpFrom, lpFromlen,
+                               lpOverlapped, lpCompletionRoutine );

-        msghdr.msg_namelen = *lpFromlen;
-        msghdr.msg_name = ws_sockaddr_alloc (lpFrom, lpFromlen, &msghdr.msg_namelen);
-    }
-    else
-        msghdr.msg_namelen = 0;
+        if ( !wsa )
+        {
+            err = WSAEFAULT;
+            goto err_free;
+        }
+
+        if ( ( ret = register_new_async ( &wsa->async )) )
+        {
+            err = NtStatusToWSAError ( ret );
+
+            if ( !lpOverlapped )
+                HeapFree ( GetProcessHeap(), 0, wsa->overlapped );
+            HeapFree ( GetProcessHeap(), 0, wsa );
+            goto err_free;
+        }

-    msghdr.msg_iov = iovec;
-    msghdr.msg_iovlen = dwBufferCount;
-    msghdr.msg_control = NULL;
-    msghdr.msg_controllen = 0;
-    msghdr.msg_flags = 0;
+        /* Try immediate completion */
+        if ( lpOverlapped )
+        {
+            if  ( WSAGetOverlappedResult ( (HANDLE) s, lpOverlapped,
+                                           lpNumberOfBytesRecvd, FALSE, lpFlags) )
+                return 0;
+
+            if ( (err = WSAGetLastError ()) != WSA_IO_INCOMPLETE )
+            {
+                TRACE ("err: %d\n", err);
+                goto err_free;
+            }
+        }

-    /* FIXME: Treat overlapped IO here */
+        WSASetLastError ( WSA_IO_PENDING );
+        return SOCKET_ERROR;
+    }

-    if (_is_blocking(s))
+    if ( _is_blocking(s) )
     {
         /* block here */
         /* FIXME: OOB and exceptfds? */
         do_block(fd, 1);
     }
-
-    /* FIXME: can we support MSG_PARTIAL ?
-       How does it relate to recvmsg()'s msg_flags ? */
-
-    if ((length = recvmsg (fd, &msghdr, *lpFlags)) == -1)
+
+    n = WS2_recv ( fd, iovec, dwBufferCount, lpFrom, lpFromlen, lpFlags );
+    if ( n == -1 )
     {
         err = wsaErrno();
         goto err_free;
     }

-    TRACE(" -> %i bytes\n", length);
+    TRACE(" -> %i bytes\n", n);
+    *lpNumberOfBytesRecvd = n;

-    if ( lpFrom && ws_sockaddr_u2ws (msghdr.msg_name, msghdr.msg_namelen, lpFrom, lpFromlen) != 0 )
-    {
-        /* The from buffer was too small, but we read the data
-         * anyway. Is that really bad?
-         */
-        SetLastError ( WSAEFAULT );
-        WARN ( " -> Address buffer too small\n" );
-    }
-
-    *lpNumberOfBytesRecvd = length;
-
-    WS_FREE (iovec);
-    ws_sockaddr_free ( msghdr.msg_name, lpFrom );
+    HeapFree (GetProcessHeap(), 0, iovec);
     close(fd);
     _enable_event(s, FD_READ, 0, 0);
-
     return 0;

 err_free:
-    WS_FREE (iovec);
-    ws_sockaddr_free ( msghdr.msg_name, lpFrom );
+    HeapFree (GetProcessHeap(), 0, iovec);

 err_close:
     close (fd);

 error:
     WARN(" -> ERROR %d\n", err);
-    SetLastError ( err );
+    WSASetLastError ( err );
     return SOCKET_ERROR;
 }

@@ -3419,7 +3990,7 @@
        SOCKET cs;
        SOCKADDR src_addr, dst_addr;

-       TRACE("Socket  %ui, sockaddr %p, addrlen %p, fnCondition %p, dwCallbackD ata %ld\n",
+       TRACE("Socket  %u, sockaddr %p, addrlen %p, fnCondition %p, dwCallbackData %ld\n",
                s, addr, addrlen, lpfnCondition, dwCallbackData);


@@ -3450,7 +4021,17 @@
                                addr = memcpy(addr, &src_addr, (*addrlen > size) ?  size : *addrlen );
                        return cs;
                case CF_DEFER:
-                       SetLastError(WSATRY_AGAIN);
+                       SERVER_START_REQ ( set_socket_deferred )
+                       {
+                           req->handle = s;
+                           req->deferred = cs;
+                           if ( !wine_server_call_err ( req ) )
+                           {
+                               SetLastError ( WSATRY_AGAIN );
+                               CloseHandle ( cs );
+                           }
+                       }
+                       SERVER_END_REQ;
                        return SOCKET_ERROR;
                case CF_REJECT:
                        WS_closesocket(cs);
diff -ruNX ignore TMP/wine/dlls/winsock/ws2_32.spec MW/wine/dlls/winsock/ws2_32.spec
--- TMP/wine/dlls/winsock/ws2_32.spec	Thu Apr 18 16:26:02 2002
+++ MW/wine/dlls/winsock/ws2_32.spec	Thu Apr 18 16:26:48 2002
@@ -42,7 +42,7 @@
 27  stub     WSAAddressToStringA
 28  stub     WSAAddressToStringW
 29  stdcall  WSACloseEvent(long) WSACloseEvent
-30  stub     WSAConnect
+30  stdcall  WSAConnect(long ptr long ptr ptr ptr ptr) WSAConnect
 31  stdcall  WSACreateEvent ()  WSACreateEvent
 32  stub     WSADuplicateSocketA
 33  stub     WSADuplicateSocketW
@@ -52,7 +52,7 @@
 37  stdcall  WSAEnumProtocolsA(ptr ptr ptr) WSAEnumProtocolsA
 38  stdcall  WSAEnumProtocolsW(ptr ptr ptr) WSAEnumProtocolsW
 39  stdcall  WSAEventSelect(long long long) WSAEventSelect
-40  stub     WSAGetOverlappedResult
+40  stdcall  WSAGetOverlappedResult(long ptr ptr long ptr) WSAGetOverlappedResult
 41  stub     WSAGetQOSByName
 42  stub     WSAGetServiceClassInfoA
 43  stub     WSAGetServiceClassInfoW
diff -ruNX ignore TMP/wine/files/file.c MW/wine/files/file.c
--- TMP/wine/files/file.c	Thu Apr 18 15:41:00 2002
+++ MW/wine/files/file.c	Fri Apr 19 13:52:52 2002
@@ -306,6 +306,13 @@

     ret = wine_server_handle_to_fd( handle, access, &fd, type, flags );
     if (ret) SetLastError( RtlNtStatusToDosError(ret) );
+    if ( ( (access & GENERIC_READ)  && (*flags & FD_FLAG_RECV_SHUTDOWN ) ) ||
+         ( (access & GENERIC_WRITE) && (*flags & FD_FLAG_SEND_SHUTDOWN ) ) )
+    {
+        close (fd);
+        SetLastError ( ERROR_PIPE_NOT_CONNECTED );
+        return -1;
+    }
     return fd;
 }

@@ -1411,7 +1418,7 @@
         *lpTransferred = lpOverlapped->InternalHigh;

     SetLastError ( lpOverlapped->Internal == STATUS_PENDING ?
-                   ERROR_IO_INCOMPLETE : lpOverlapped->Internal );
+                   ERROR_IO_INCOMPLETE : RtlNtStatusToDosError ( lpOverlapped->Internal ) );

     return (r==WAIT_OBJECT_0);
 }
@@ -1511,9 +1518,13 @@
     if ( fd < 0 )
     {
         WARN ( "Couldn't get FD\n" );
-        SetLastError ( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
+    if ( ! (flags & FD_FLAG_OVERLAPPED) ) {
+        WARN ( "fd is not overlapped\n" );
+        SetLastError ( ERROR_INVALID_PARAMETER );
+        goto error;
+    }

     ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
     if(!ovp)
@@ -1730,6 +1741,11 @@
     {
         TRACE( "Couldn't get FD\n" );
         return FALSE;
+    }
+    if ( ! (flags & FD_FLAG_OVERLAPPED) ) {
+        WARN ( "fd is not overlapped\n" );
+        SetLastError ( ERROR_INVALID_PARAMETER );
+        goto error;
     }

     ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
diff -ruNX ignore TMP/wine/include/wine/server_protocol.h MW/wine/include/wine/server_protocol.h
--- TMP/wine/include/wine/server_protocol.h	Thu Apr 18 16:26:02 2002
+++ MW/wine/include/wine/server_protocol.h	Fri Apr 19 13:51:02 2002
@@ -796,6 +796,8 @@
 #define FD_FLAG_OVERLAPPED 0x01
 #define FD_FLAG_TIMEOUT    0x02

+#define FD_FLAG_RECV_SHUTDOWN      0x04
+#define FD_FLAG_SEND_SHUTDOWN      0x08


 struct set_file_pointer_request
@@ -1001,6 +1003,16 @@
     struct reply_header __header;
 };

+struct set_socket_deferred_request
+{
+    struct request_header __header;
+    handle_t     handle;
+    handle_t     deferred;
+};
+struct set_socket_deferred_reply
+{
+    struct reply_header __header;
+};


 struct alloc_console_request
@@ -2758,6 +2770,7 @@
     REQ_set_socket_event,
     REQ_get_socket_event,
     REQ_enable_socket_event,
+    REQ_set_socket_deferred,
     REQ_alloc_console,
     REQ_free_console,
     REQ_get_console_renderer_events,
@@ -2919,6 +2932,7 @@
     struct set_socket_event_request set_socket_event_request;
     struct get_socket_event_request get_socket_event_request;
     struct enable_socket_event_request enable_socket_event_request;
+    struct set_socket_deferred_request set_socket_deferred_request;
     struct alloc_console_request alloc_console_request;
     struct free_console_request free_console_request;
     struct get_console_renderer_events_request get_console_renderer_events_request;
@@ -3078,6 +3092,7 @@
     struct set_socket_event_reply set_socket_event_reply;
     struct get_socket_event_reply get_socket_event_reply;
     struct enable_socket_event_reply enable_socket_event_reply;
+    struct set_socket_deferred_reply set_socket_deferred_reply;
     struct alloc_console_reply alloc_console_reply;
     struct free_console_reply free_console_reply;
     struct get_console_renderer_events_reply get_console_renderer_events_reply;
@@ -3183,6 +3198,6 @@
     struct get_window_properties_reply get_window_properties_reply;
 };

-#define SERVER_PROTOCOL_VERSION 78
+#define SERVER_PROTOCOL_VERSION 80

 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff -ruNX ignore TMP/wine/include/winsock.h MW/wine/include/winsock.h
--- TMP/wine/include/winsock.h	Thu Apr 18 15:41:05 2002
+++ MW/wine/include/winsock.h	Thu Apr 18 16:26:48 2002
@@ -728,6 +728,7 @@
 #define FD_CLOSE                   0x00000020

 /* internal per-socket flags */
+/* CAUTION: FD_FLAG_RECV_SHUTDOWN / FD_FLAG_SEND_SHUTDOWN (server/protocol.def) must fit into this mask */
 #ifdef __WINE__
 #define FD_WINE_LISTENING          0x10000000
 #define FD_WINE_NONBLOCKING        0x20000000
diff -ruNX ignore TMP/wine/include/winsock2.h MW/wine/include/winsock2.h
--- TMP/wine/include/winsock2.h	Thu Apr 18 15:41:09 2002
+++ MW/wine/include/winsock2.h	Thu Apr 18 16:26:48 2002
@@ -221,8 +221,7 @@

 #define WSAEVENT      HANDLE
 #define LPWSAEVENT    LPHANDLE
-#define WSAOVERLAPPED OVERLAPPED
-typedef struct _OVERLAPPED* LPWSAOVERLAPPED;
+typedef OVERLAPPED    WSAOVERLAPPED, *LPWSAOVERLAPPED;

 #define WSA_IO_PENDING             (ERROR_IO_PENDING)
 #define WSA_IO_INCOMPLETE          (ERROR_IO_INCOMPLETE)
diff -ruNX ignore TMP/wine/server/async.c MW/wine/server/async.c
--- TMP/wine/server/async.c	Tue Apr 16 11:18:11 2002
+++ MW/wine/server/async.c	Fri Apr 19 13:50:30 2002
@@ -150,42 +150,32 @@

 DECL_HANDLER(register_async)
 {
-    struct object *obj;
+    struct object *obj = get_handle_obj( current->process, req->handle, 0, NULL);

-    if (!(obj = get_handle_obj( current->process, req->handle, 0, NULL)) )
-        return;
-
-    if(obj->ops->queue_async)
+    if ( !(obj) || !obj->ops->queue_async )
     {
-        struct async_queue *q = obj->ops->queue_async(obj, NULL, req->type, 0);
-        struct async *async;
-
-        async = find_async(q, current, req->overlapped);
-        if(req->status==STATUS_PENDING)
-        {
-            if(!async)
-                async = create_async(obj, current, req->overlapped);
-
-            if(async)
-            {
-                async->status = req->status;
-                if(!obj->ops->queue_async(obj, async, req->type, req->count))
-                    destroy_async(async);
-            }
-        }
-        else
-        {
-            if(async)
-                destroy_async(async);
-            else
-                set_error(STATUS_INVALID_PARAMETER);
-        }
-
-        set_select_events(obj,obj->ops->get_poll_events(obj));
-    }
-    else
         set_error(STATUS_INVALID_HANDLE);
+        return;
+    }
+
+/*
+ * The queue_async method must do the following:
+ *
+ * 1. Get the async_queue for the request of given type.
+ * 2. Call find_async() to look for the specific client request in the queue (=> NULL if not found).
+ * 3. If status is STATUS_PENDING:
+ *      a) If no async request found in step 2 (new request): call create_async() to initialize one.
+ *      b) Set request's status to STATUS_PENDING.
+ *      c) If the "queue" field of the async request is NULL: call async_insert() to put it into the queue.
+ *    Otherwise:
+ *      If the async request was found in step 2, destroy it by calling destroy_async().
+ * 4. Carry out any operations necessary to adjust the object's poll events
+ *    Usually: set_elect_events (obj, obj->ops->get_poll_events()).
+ *
+ * See also the implementations in file.c, serial.c, and sock.c.
+*/

+    obj->ops->queue_async (obj, req->overlapped, req->status, req->type, req->count);
     release_object(obj);
 }

diff -ruNX ignore TMP/wine/server/file.c MW/wine/server/file.c
--- TMP/wine/server/file.c	Tue Apr  2 16:51:50 2002
+++ MW/wine/server/file.c	Thu Apr 18 19:11:13 2002
@@ -69,7 +69,7 @@
 static int file_flush( struct object *obj );
 static int file_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 static void file_destroy( struct object *obj );
-static struct async_queue * file_queue_async(struct object *obj, struct async* async, int type, int count);
+static void file_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count);

 static const struct object_ops file_ops =
 {
@@ -358,9 +358,10 @@
     return FD_TYPE_DEFAULT;
 }

-static struct async_queue *file_queue_async(struct object *obj, struct async *async, int type, int count)
+static void file_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count)
 {
     struct file *file = (struct file *)obj;
+    struct async *async;
     struct async_queue *q;

     assert( obj->ops == &file_ops );
@@ -368,7 +369,7 @@
     if ( !(file->flags & FILE_FLAG_OVERLAPPED) )
     {
         set_error ( STATUS_INVALID_HANDLE );
-        return NULL;
+        return;
     }

     switch(type)
@@ -381,13 +382,26 @@
         break;
     default:
         set_error( STATUS_INVALID_PARAMETER );
-        return NULL;
+        return;
     }

-    if(async && !async->q)
-        async_insert(q, async);
+    async = find_async ( q, current, ptr );
+
+    if ( status == STATUS_PENDING )
+    {
+        if ( !async )
+            async = create_async ( obj, current, ptr );
+        if ( !async )
+            return;
+
+        async->status = STATUS_PENDING;
+        if ( !async->q )
+            async_insert( q, async );
+    }
+    else if ( async ) destroy_async ( async );
+    else set_error ( STATUS_INVALID_PARAMETER );

-    return q;
+    set_select_events ( obj, file_get_poll_events ( obj ));
 }

 static void file_destroy( struct object *obj )
diff -ruNX ignore TMP/wine/server/object.h MW/wine/server/object.h
--- TMP/wine/server/object.h	Thu Apr  4 09:37:39 2002
+++ MW/wine/server/object.h	Fri Apr 19 13:29:37 2002
@@ -63,8 +63,8 @@
     int  (*flush)(struct object *);
     /* get file information */
     int  (*get_file_info)(struct object *,struct get_file_info_reply *, int *flags);
-    /* queue an async operation */
-    struct async_queue* (*queue_async)(struct object *, struct async *async, int type, int count);
+    /* queue an async operation - see register_async handler in async.c*/
+    void (*queue_async)(struct object *, void* ptr, unsigned int status, int type, int count);
     /* destroy on refcount == 0 */
     void (*destroy)(struct object *);
 };
diff -ruNX ignore TMP/wine/server/protocol.def MW/wine/server/protocol.def
--- TMP/wine/server/protocol.def	Thu Apr 18 16:26:02 2002
+++ MW/wine/server/protocol.def	Fri Apr 19 10:49:29 2002
@@ -612,7 +612,9 @@
 };
 #define FD_FLAG_OVERLAPPED 0x01
 #define FD_FLAG_TIMEOUT    0x02
-
+/* Socket flags: see server/sock.c */
+#define FD_FLAG_RECV_SHUTDOWN      0x04
+#define FD_FLAG_SEND_SHUTDOWN      0x08

 /* Set a file current position */
 @REQ(set_file_pointer)
@@ -746,6 +748,10 @@
     unsigned int cstate;        /* status bits to clear */
 @END

+ at REQ(set_socket_deferred)
+    handle_t     handle;        /* handle to the socket */
+    handle_t     deferred;      /* handle to the socket for which accept() is deferred */
+ at END

 /* Allocate a console (only used by a console renderer) */
 @REQ(alloc_console)
diff -ruNX ignore TMP/wine/server/request.h MW/wine/server/request.h
--- TMP/wine/server/request.h	Thu Apr 18 16:26:02 2002
+++ MW/wine/server/request.h	Fri Apr 19 13:51:02 2002
@@ -152,6 +152,7 @@
 DECL_HANDLER(set_socket_event);
 DECL_HANDLER(get_socket_event);
 DECL_HANDLER(enable_socket_event);
+DECL_HANDLER(set_socket_deferred);
 DECL_HANDLER(alloc_console);
 DECL_HANDLER(free_console);
 DECL_HANDLER(get_console_renderer_events);
@@ -312,6 +313,7 @@
     (req_handler)req_set_socket_event,
     (req_handler)req_get_socket_event,
     (req_handler)req_enable_socket_event,
+    (req_handler)req_set_socket_deferred,
     (req_handler)req_alloc_console,
     (req_handler)req_free_console,
     (req_handler)req_get_console_renderer_events,
diff -ruNX ignore TMP/wine/server/serial.c MW/wine/server/serial.c
--- TMP/wine/server/serial.c	Tue Apr  2 16:51:51 2002
+++ MW/wine/server/serial.c	Fri Apr 19 13:36:47 2002
@@ -51,7 +51,7 @@
 static int serial_get_fd( struct object *obj );
 static int serial_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
 static int serial_get_poll_events( struct object *obj );
-static struct async_queue * serial_queue_async(struct object *obj, struct async* async, int type, int count);
+static void serial_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count);
 static void destroy_serial(struct object *obj);
 static void serial_poll_event( struct object *obj, int event );

@@ -250,24 +250,11 @@
     set_select_events(obj,obj->ops->get_poll_events(obj));
 }

-/*
- * This function is an abuse of overloading that deserves some explanation.
- *
- * It has three purposes:
- *
- * 1. get the queue for a type of async operation
- * 2. requeue an async operation
- * 3. queue a new async operation
- *
- * It is overloaded so that these three functions only take one function pointer
- *  in the object operations list.
- *
- * In all cases, it returns the async queue.
- */
-static struct async_queue *serial_queue_async(struct object *obj, struct async *async, int type, int count)
+static void serial_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count)
 {
     struct serial *serial = (struct serial *)obj;
     struct async_queue *q;
+    struct async *async;
     int timeout;

     assert(obj->ops == &serial_ops);
@@ -288,19 +275,29 @@
         break;
     default:
         set_error(STATUS_INVALID_PARAMETER);
-        return NULL;
+        return;
     }

-    if(async)
+    async = find_async ( q, current, ptr );
+
+    if ( status == STATUS_PENDING )
     {
+        if ( !async )
+            async = create_async ( obj, current, ptr );
+        if ( !async )
+            return;
+
+        async->status = STATUS_PENDING;
         if(!async->q)
         {
-            async_add_timeout(async,timeout);
-            async_insert(q, async);
+            async_add_timeout ( async, timeout );
+            async_insert ( q, async );
+        }
     }
-}
+    else if ( async ) destroy_async ( async );
+    else set_error ( STATUS_INVALID_PARAMETER );

-    return q;
+    set_select_events ( obj, serial_get_poll_events ( obj ));
 }

 /* create a serial */
diff -ruNX ignore TMP/wine/server/sock.c MW/wine/server/sock.c
--- TMP/wine/server/sock.c	Thu Apr 18 16:26:02 2002
+++ MW/wine/server/sock.c	Fri Apr 19 16:25:54 2002
@@ -72,6 +72,7 @@
     unsigned int        message;     /* message to send */
     unsigned int        wparam;      /* message wparam (socket handle) */
     int                 errors[FD_MAX_EVENTS]; /* event errors */
+    struct sock*        deferred;    /* socket that waits for a deferred accept */
     struct async_queue  read_q;      /* Queue for asynchronous reads */
     struct async_queue  write_q;     /* Queue for asynchronous writes */
 };
@@ -85,6 +86,7 @@
 static void sock_destroy( struct object *obj );
 static int sock_get_error( int err );
 static void sock_set_error(void);
+static void sock_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count);

 static const struct object_ops sock_ops =
 {
@@ -99,7 +101,7 @@
     sock_get_fd,                  /* get_fd */
     no_flush,                     /* flush */
     sock_get_info,                /* get_file_info */
-    NULL,                         /* queue_async */
+    sock_queue_async,             /* queue_async */
     sock_destroy                  /* destroy */
 };

@@ -123,7 +125,25 @@
 };


-static void sock_reselect( struct sock *sock )
+/* After POLLHUP is received, the socket will no longer be in the main select loop.
+   This function is used to signal pending events nevertheless */
+static void sock_try_event ( struct sock *sock, int event )
+{
+    struct pollfd pfd;
+
+    pfd.fd = sock->obj.fd;
+    pfd.events = event;
+    pfd.revents = 0;
+    poll (&pfd, 1, 0);
+
+    if ( pfd.revents )
+    {
+        if ( debug_level ) fprintf ( stderr, "sock_try_event: %x\n", pfd.revents );
+        sock_poll_event ( &sock->obj, pfd.revents );
+    }
+}
+
+static int sock_reselect( struct sock *sock )
 {
     int ev = sock_get_poll_events( &sock->obj );

@@ -132,21 +152,41 @@

     if (sock->obj.select == -1) {
         /* previously unconnected socket, is this reselect supposed to connect it? */
-        if (!(sock->state & ~FD_WINE_NONBLOCKING)) return;
+        if (!(sock->state & ~FD_WINE_NONBLOCKING)) return 0;
         /* ok, it is, attach it to the wineserver's main poll loop */
         add_select_user( &sock->obj );
     }
     /* update condition mask */
     set_select_events( &sock->obj, ev );
+    return ev;
 }

 /* wake anybody waiting on the socket event or send the associated message */
-static void sock_wake_up( struct sock *sock )
+static void sock_wake_up( struct sock *sock, int pollev )
 {
     unsigned int events = sock->pmask & sock->mask;
     int i;
+    int async_active = 0;

-    if (!events) return;
+    if ( sock->flags & FD_FLAG_OVERLAPPED )
+    {
+        if( pollev & (POLLIN|POLLPRI) && IS_READY( sock->read_q ) )
+        {
+            if (debug_level) fprintf ( stderr, "activating read queue for socket %p\n", sock );
+            async_notify( sock->read_q.head, STATUS_ALERTED );
+            async_active = 1;
+        }
+        if( pollev & POLLOUT && IS_READY( sock->write_q ) )
+        {
+            if (debug_level) fprintf ( stderr, "activating write queue for socket %p\n", sock );
+            async_notify( sock->write_q.head, STATUS_ALERTED );
+            async_active = 1;
+        }
+    }
+
+    /* Do not signal events if there are still pending asynchronous IO requests */
+    /* We need this to delay FD_CLOSE events until all pending overlapped requests are processed */
+    if ( !events || async_active ) return;

     if (sock->event)
     {
@@ -182,6 +222,7 @@
 static void sock_poll_event( struct object *obj, int event )
 {
     struct sock *sock = (struct sock *)obj;
+    int empty_recv = 0;

     assert( sock->obj.ops == &sock_ops );
     if (debug_level)
@@ -232,19 +273,37 @@
         if (event & POLLIN)
         {
             char dummy;
+            int nr;

             /* Linux 2.4 doesn't report POLLHUP if only one side of the socket
              * has been closed, so we need to check for it explicitly here */
-            if (!recv( sock->obj.fd, &dummy, 1, MSG_PEEK )) event = POLLHUP;
-            else
+            nr  = recv( sock->obj.fd, &dummy, 1, MSG_PEEK );
+
+            if ( nr > 0 )
             {
                 /* incoming data */
                 sock->pmask |= FD_READ;
                 sock->hmask |= FD_READ;
                 sock->errors[FD_READ_BIT] = 0;
                 if (debug_level)
-                    fprintf(stderr, "socket %d is readable\n", sock->obj.fd );
+                    fprintf( stderr, "socket %d is readable\n", sock->obj.fd );
             }
+            else if ( nr == 0 )
+                empty_recv = 1;
+            else
+            {
+                /* EAGAIN can happen if an async recv() falls between the server's poll()
+                   call and the invocation of this routine */
+                if ( errno == EAGAIN )
+                    event &= ~POLLIN;
+                else
+                {
+                    if ( debug_level )
+                        fprintf ( stderr, "recv error on socket %d: %d\n", sock->obj.fd, errno );
+                    event = POLLERR;
+                }
+            }
+
         }
         if (event & POLLOUT)
         {
@@ -262,25 +321,37 @@
             if (debug_level)
                 fprintf(stderr, "socket %d got OOB data\n", sock->obj.fd);
         }
-        if (((event & POLLERR) || ((event & (POLLIN|POLLHUP)) == POLLHUP))
-            && (sock->state & (FD_READ|FD_WRITE))) {
+        if ( (sock->state & (FD_READ|FD_WRITE)) && (event & (POLLERR|POLLHUP)) )
+        {
             /* socket closing */
             sock->errors[FD_CLOSE_BIT] = sock_error( sock->obj.fd );
-            sock->state &= ~(FD_WINE_CONNECTED|FD_READ|FD_WRITE);
+            sock->state &= ~(FD_WINE_CONNECTED|FD_WRITE);
             sock->pmask |= FD_CLOSE;
             if (debug_level)
                 fprintf(stderr, "socket %d aborted by error %d\n",
                         sock->obj.fd, sock->errors[FD_CLOSE_BIT]);
         }
+        else if ( empty_recv && (sock->state & FD_READ) )
+        {
+            /* incoming stream closing - do not alter sock->state, reading is still allowed */
+            sock->errors[FD_CLOSE_BIT] = 0;
+            sock->pmask |= FD_CLOSE;
+            if (debug_level)
+                fprintf(stderr, "socket %d incoming stream closed\n", sock->obj.fd);
+        }
     }

     if (event & (POLLERR|POLLHUP))
+    {
+        if ( debug_level )
+            fprintf ( stderr, "HUP/ERR - removing socket %d from select loop\n", sock->obj.fd );
         set_select_events( &sock->obj, -1 );
+    }
     else
         sock_reselect( sock );

     /* wake up anyone waiting for whatever just happened */
-    if (sock->pmask & sock->mask) sock_wake_up( sock );
+    if ( sock->pmask & sock->mask || sock->flags & FD_FLAG_OVERLAPPED ) sock_wake_up( sock, event );

     /* if anyone is stupid enough to wait on the socket object itself,
      * maybe we should wake them up too, just in case? */
@@ -319,8 +390,11 @@
         /* listening, wait for readable */
         return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN;

-    if (mask & FD_READ)  ev |= POLLIN | POLLPRI;
-    if (mask & FD_WRITE) ev |= POLLOUT;
+    if (mask & FD_READ || (sock->flags & WSA_FLAG_OVERLAPPED && IS_READY (sock->read_q)))
+        ev |= POLLIN | POLLPRI;
+    if (mask & FD_WRITE || (sock->flags & WSA_FLAG_OVERLAPPED && IS_READY (sock->write_q)))
+        ev |= POLLOUT;
+
     return ev;
 }

@@ -350,10 +424,70 @@
         reply->serial      = 0;
     }
     *flags = 0;
-    if (sock->flags & WSA_FLAG_OVERLAPPED) *flags |= FD_FLAG_OVERLAPPED;
+    if ( !(sock->state & FD_READ ) ) *flags |= FD_FLAG_RECV_SHUTDOWN;
+    if ( !(sock->state & FD_WRITE ) ) *flags |= FD_FLAG_SEND_SHUTDOWN;
+    if ( sock->flags & WSA_FLAG_OVERLAPPED ) *flags |= FD_FLAG_OVERLAPPED;
     return FD_TYPE_DEFAULT;
 }

+static void sock_queue_async(struct object *obj, void *ptr, unsigned int status, int type, int count)
+{
+    struct sock *sock = (struct sock *)obj;
+    struct async_queue *q;
+    struct async *async;
+    int pollev;
+
+    assert( obj->ops == &sock_ops );
+
+    if ( !(sock->flags & WSA_FLAG_OVERLAPPED) )
+    {
+        set_error ( STATUS_INVALID_HANDLE );
+        return;
+    }
+
+    switch( type )
+    {
+    case ASYNC_TYPE_READ:
+        q = &sock->read_q;
+        break;
+    case ASYNC_TYPE_WRITE:
+        q = &sock->write_q;
+        break;
+    default:
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+
+    async = find_async ( q, current, ptr );
+
+    if ( status == STATUS_PENDING )
+    {
+        if ( ( !( sock->state & FD_READ ) && type == ASYNC_TYPE_READ  ) ||
+             ( !( sock->state & FD_WRITE ) && type == ASYNC_TYPE_WRITE ) )
+        {
+            set_error ( STATUS_PIPE_DISCONNECTED );
+            if ( async ) destroy_async ( async );
+        }
+        else
+        {
+            if ( !async )
+                async = create_async ( obj, current, ptr );
+            if ( !async )
+                return;
+
+            async->status = STATUS_PENDING;
+            if ( !async->q )
+                async_insert ( q, async );
+        }
+    }
+    else if ( async ) destroy_async ( async );
+    else set_error ( STATUS_INVALID_PARAMETER );
+
+    pollev = sock_reselect ( sock );
+    if ( pollev ) sock_try_event ( sock, pollev );
+    else if ( sock->pmask & sock->mask ) sock_wake_up ( sock, 0 );
+}
+
 static void sock_destroy( struct object *obj )
 {
     struct sock *sock = (struct sock *)obj;
@@ -361,6 +495,9 @@

     /* FIXME: special socket shutdown stuff? */

+    if ( sock->deferred )
+        release_object ( sock->deferred );
+
     if ( sock->flags & WSA_FLAG_OVERLAPPED )
     {
         destroy_async_queue ( &sock->read_q );
@@ -394,13 +531,14 @@
     sock->window  = 0;
     sock->message = 0;
     sock->wparam  = 0;
-    sock_reselect( sock );
-    clear_error();
+    sock->deferred = NULL;
     if (sock->flags & WSA_FLAG_OVERLAPPED)
     {
         init_async_queue (&sock->read_q);
         init_async_queue (&sock->write_q);
     }
+    sock_reselect( sock );
+    clear_error();
     return &sock->obj;
 }

@@ -417,44 +555,51 @@
                                       GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops);
     if (!sock)
     	return NULL;
-    /* Try to accept(2). We can't be safe that this an already connected socket
-     * or that accept() is allowed on it. In those cases we will get -1/errno
-     * return.
-     */
-    slen = sizeof(saddr);
-    acceptfd = accept(sock->obj.fd,&saddr,&slen);
-    if (acceptfd==-1) {
-    	sock_set_error();
-        release_object( sock );
-	return NULL;
-    }
-    if (!(acceptsock = alloc_object( &sock_ops, -1 )))
-    {
-        release_object( sock );
-        return NULL;
-    }

-    /* newly created socket gets the same properties of the listening socket */
-    fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */
-    acceptsock->obj.fd = acceptfd;
-    acceptsock->state  = FD_WINE_CONNECTED|FD_READ|FD_WRITE;
-    if (sock->state & FD_WINE_NONBLOCKING)
-        acceptsock->state |= FD_WINE_NONBLOCKING;
-    acceptsock->mask    = sock->mask;
-    acceptsock->hmask   = 0;
-    acceptsock->pmask   = 0;
-    acceptsock->event   = NULL;
-    acceptsock->window  = sock->window;
-    acceptsock->message = sock->message;
-    acceptsock->wparam  = 0;
-    if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event );
-    acceptsock->flags = sock->flags;
-    if ( acceptsock->flags & WSA_FLAG_OVERLAPPED )
-    {
-	init_async_queue ( &acceptsock->read_q );
-	init_async_queue ( &acceptsock->write_q );
-    }
+    if ( sock->deferred ) {
+        acceptsock = sock->deferred;
+        sock->deferred = NULL;
+    } else {
+
+        /* Try to accept(2). We can't be safe that this an already connected socket
+         * or that accept() is allowed on it. In those cases we will get -1/errno
+         * return.
+         */
+        slen = sizeof(saddr);
+        acceptfd = accept(sock->obj.fd,&saddr,&slen);
+        if (acceptfd==-1) {
+            sock_set_error();
+            release_object( sock );
+            return NULL;
+        }
+        if (!(acceptsock = alloc_object( &sock_ops, -1 )))
+        {
+            release_object( sock );
+            return NULL;
+        }

+        /* newly created socket gets the same properties of the listening socket */
+        fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */
+        acceptsock->obj.fd = acceptfd;
+        acceptsock->state  = FD_WINE_CONNECTED|FD_READ|FD_WRITE;
+        if (sock->state & FD_WINE_NONBLOCKING)
+            acceptsock->state |= FD_WINE_NONBLOCKING;
+        acceptsock->mask    = sock->mask;
+        acceptsock->hmask   = 0;
+        acceptsock->pmask   = 0;
+        acceptsock->event   = NULL;
+        acceptsock->window  = sock->window;
+        acceptsock->message = sock->message;
+        acceptsock->wparam  = 0;
+        if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event );
+        acceptsock->flags = sock->flags;
+        acceptsock->deferred = 0;
+        if ( acceptsock->flags & WSA_FLAG_OVERLAPPED )
+        {
+            init_async_queue ( &acceptsock->read_q );
+            init_async_queue ( &acceptsock->write_q );
+        }
+    }
     clear_error();
     sock->pmask &= ~FD_ACCEPT;
     sock->hmask &= ~FD_ACCEPT;
@@ -566,6 +711,7 @@
 {
     struct sock *sock;
     struct event *old_event;
+    int pollev;

     if (!(sock = (struct sock*)get_handle_obj( current->process, req->handle,
                                                GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, &sock_ops)))
@@ -579,7 +725,11 @@
     if (req->event) sock->event = get_event_obj( current->process, req->event, EVENT_MODIFY_STATE );

     if (debug_level && sock->event) fprintf(stderr, "event ptr: %p\n", sock->event);
-    sock_reselect( sock );
+
+    pollev = sock_reselect( sock );
+    if ( pollev ) sock_try_event ( sock, pollev );
+    else if ( sock->pmask & sock->mask ) sock_wake_up ( sock, 0 );
+
     if (sock->mask)
         sock->state |= FD_WINE_NONBLOCKING;

@@ -587,7 +737,7 @@
        it is possible that FD_CONNECT or FD_ACCEPT network events has happened
        before a WSAEventSelect() was done on it.
        (when dealing with Asynchronous socket)  */
-    if (sock->pmask & sock->mask) sock_wake_up( sock );
+    if (sock->pmask & sock->mask) sock_wake_up( sock, pollev );

     if (old_event) release_object( old_event ); /* we're through with it */
     release_object( &sock->obj );
@@ -634,6 +784,7 @@
 DECL_HANDLER(enable_socket_event)
 {
     struct sock *sock;
+    int pollev;

     if (!(sock = (struct sock*)get_handle_obj( current->process, req->handle,
                                                GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, &sock_ops)))
@@ -643,6 +794,33 @@
     sock->hmask &= ~req->mask;
     sock->state |= req->sstate;
     sock->state &= ~req->cstate;
-    sock_reselect( sock );
+
+    pollev = sock_reselect( sock );
+    if ( pollev ) sock_try_event ( sock, pollev );
+    else if ( sock->pmask & sock->mask ) sock_wake_up ( sock, 0 );
+
     release_object( &sock->obj );
+}
+
+DECL_HANDLER(set_socket_deferred)
+{
+    struct sock *sock, *acceptsock;
+
+    sock=(struct sock*)get_handle_obj( current->process,req->handle,
+                                       GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops );
+    if ( !sock )
+    {
+        set_error ( WSAENOTSOCK );
+        return;
+    }
+    acceptsock = (struct sock*)get_handle_obj( current->process,req->deferred,
+                                               GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops );
+    if ( !acceptsock )
+    {
+        release_object ( sock );
+        set_error ( WSAENOTSOCK );
+        return;
+    }
+    sock->deferred = acceptsock;
+    release_object ( sock );
 }
diff -ruNX ignore TMP/wine/server/trace.c MW/wine/server/trace.c
--- TMP/wine/server/trace.c	Thu Apr 18 16:26:02 2002
+++ MW/wine/server/trace.c	Fri Apr 19 13:51:02 2002
@@ -943,6 +943,12 @@
     fprintf( stderr, " cstate=%08x", req->cstate );
 }

+static void dump_set_socket_deferred_request( const struct set_socket_deferred_request *req )
+{
+    fprintf( stderr, " handle=%d,", req->handle );
+    fprintf( stderr, " deferred=%d", req->deferred );
+}
+
 static void dump_alloc_console_request( const struct alloc_console_request *req )
 {
     fprintf( stderr, " access=%08x,", req->access );
@@ -2215,6 +2221,7 @@
     (dump_func)dump_set_socket_event_request,
     (dump_func)dump_get_socket_event_request,
     (dump_func)dump_enable_socket_event_request,
+    (dump_func)dump_set_socket_deferred_request,
     (dump_func)dump_alloc_console_request,
     (dump_func)dump_free_console_request,
     (dump_func)dump_get_console_renderer_events_request,
@@ -2372,6 +2379,7 @@
     (dump_func)0,
     (dump_func)dump_get_socket_event_reply,
     (dump_func)0,
+    (dump_func)0,
     (dump_func)dump_alloc_console_reply,
     (dump_func)0,
     (dump_func)dump_get_console_renderer_events_reply,
@@ -2529,6 +2537,7 @@
     "set_socket_event",
     "get_socket_event",
     "enable_socket_event",
+    "set_socket_deferred",
     "alloc_console",
     "free_console",
     "get_console_renderer_events",






More information about the wine-devel mailing list