[PATCH] Overlapped recv(), WSARecv()

Martin Wilck Martin.Wilck at fujitsu-siemens.com
Wed Jan 16 11:26:13 CST 2002


This patch needs testing yet - I am sending it for people to
review and point out errors right from the start.

Currently I only verified that it compiles.

ChangeLog:
	implement overlapped recv(), much like overlapped IO in
		files/file.c.
	make WS2_32 WSARecvFrom() the "working" function,
		all others are wrappers only.
	implement WSAGetOverlappedResult().

NOTE: My patches submitted yesterday and today (16/01) to wine-patches
are necessary for this one to work. CVS base version is 2002-01-15.

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 -ruX diffignore CVS/wine/dlls/winsock/socket.c MW/wine/dlls/winsock/socket.c
--- CVS/wine/dlls/winsock/socket.c	Wed Jan 16 18:14:19 2002
+++ MW/wine/dlls/winsock/socket.c	Wed Jan 16 18:18:27 2002
@@ -85,6 +85,7 @@
 #include "wine/winbase16.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "winerror.h"
 #include "winsock2.h"
 #include "ws2tcpip.h"
 #include "wsipx.h"
@@ -95,7 +96,6 @@
 #include "file.h"
 #include "debugtools.h"

-
 DEFAULT_DEBUG_CHANNEL(winsock);

 /* critical section to protect some non-rentrant net function */
@@ -167,11 +167,22 @@

     if ( !as->user_overlapped )
         HeapFree ( GetProcessHeap(), 0, as->overlapped );
+    else
+        /* FIXME: is this correct? How else can we communicate the flags
+           to WSAGetOverlappedResult() ? */
+        as->overlapped->Offset = as->flags;

     HeapFree ( GetProcessHeap(), 0, as->iovec );
     HeapFree ( GetProcessHeap(), 0, as );
 }

+static void CALLBACK ws2_default_completion ( DWORD dwError, DWORD cbTransferred,
+                                              LPWSAOVERLAPPED lpOverlapped,
+                                              DWORD dwFlags )
+{
+    NtSetEvent ( lpOverlapped->hEvent, NULL );
+}
+
 /****************************************************************/

 /* ----------------------------------- internal data */
@@ -307,14 +318,19 @@

 static char* check_buffer(int size);

-static int _get_sock_fd(SOCKET s)
+static int _get_sock_fd_type (SOCKET s, enum fd_type *type, DWORD *flags)
 {
-    int fd = FILE_GetUnixHandle( s, GENERIC_READ );
+    int fd = FILE_GetUnixHandleType ( s, GENERIC_READ, type, flags );
     if (fd == -1)
         FIXME("handle %d is not a socket (GLE %ld)\n",s,GetLastError());
     return fd;
 }

+static int _get_sock_fd (SOCKET s)
+{
+    return _get_sock_fd_type (s, NULL, NULL);
+}
+
 static void _enable_event(SOCKET s, unsigned int event,
 			  unsigned int sstate, unsigned int cstate)
 {
@@ -1887,34 +1903,16 @@
  */
 int WINAPI WS_recv(SOCKET s, char *buf, int len, int flags)
 {
-    int fd = _get_sock_fd(s);
+    DWORD n;
+    WSABUF wsabuf = { len, buf };
+    DWORD dwFlags = flags;

     TRACE("socket %04x, buf %8x, len %d, flags %d\n", s, (unsigned)buf, len, flags);

-    if (fd != -1)
-    {
-	INT length;
-
-	if (_is_blocking(s))
-	{
-	    /* block here */
-	    /* FIXME: OOB and exceptfds? */
-	    do_block(fd, 1);
-	}
-	if ((length = recv(fd, buf, len, flags)) >= 0)
-	{
-	    TRACE(" -> %i bytes\n", length);
-
-	    close(fd);
-	    _enable_event(s, FD_READ, 0, 0);
-	    return length;
-	}
-	SetLastError(wsaErrno());
-	close(fd);
-    }
-    else SetLastError(WSAENOTSOCK);
-    WARN(" -> ERROR\n");
-    return SOCKET_ERROR;
+    if ( WSARecv (s, &wsabuf, 1, &n, &dwFlags, NULL, NULL) == SOCKET_ERROR )
+        return SOCKET_ERROR;
+    else
+        return n;
 }

 /***********************************************************************
@@ -1932,60 +1930,14 @@
 int WINAPI WS_recvfrom(SOCKET s, char *buf, INT len, int flags,
                                 struct WS_sockaddr *from, int *fromlen)
 {
-    int fd = _get_sock_fd(s);
-    int res;
-
-    TRACE("socket %04x, ptr %08x, len %d, flags %d\n", s, (unsigned)buf, len, flags);
-#if DEBUG_SOCKADDR
-    if (from)
-        dump_sockaddr(from);
-    else
-        DPRINTF("from = NULL\n");
-#endif
+    DWORD n;
+    WSABUF wsabuf = { len, buf };
+    DWORD dwFlags = flags;

-    res=SOCKET_ERROR;
-    if (fd != -1)
-    {
-        struct sockaddr* uaddr;
-        int uaddrlen;
-        int length;
-
-        if (_is_blocking(s))
-        {
-            /* block here */
-            /* FIXME: OOB and exceptfds */
-            do_block(fd, 1);
-        }
-
-        uaddr=ws_sockaddr_alloc(from,fromlen,&uaddrlen);
-        length=recvfrom(fd, buf, len, flags, uaddr, &uaddrlen);
-        if (length < 0)
-        {
-            SetLastError(wsaErrno());
-            WARN(" -> ERROR\n");
-        }
-        else if (ws_sockaddr_u2ws(uaddr,uaddrlen,from,fromlen) != 0)
-        {
-            /* The from buffer was too small, but we read the data
-             * anyway. Is that really bad?
-             */
-            SetLastError(WSAEFAULT);
-            WARN(" -> WSAEFAULT\n");
-        }
-        else
-        {
-            TRACE(" -> %i bytes\n", length);
-            _enable_event(s, FD_READ, 0, 0);
-            res=length;
-        }
-        close(fd);
-    }
+    if ( WSARecvFrom (s, &wsabuf, 1, &n, &dwFlags, from, fromlen, NULL, NULL) == SOCKET_ERROR )
+        return SOCKET_ERROR;
     else
-    {
-        SetLastError(WSAENOTSOCK);
-        WARN(" -> WSAENOTSOCK\n");
-    }
-    return res;
+        return n;
 }

 /***********************************************************************
@@ -2811,6 +2763,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 {
+        TRACE("waiting on %p\n",lpOverlapped);
+        r = WaitForSingleObjectEx (lpOverlapped->hEvent, fWait ? INFINITE : 0, TRUE);
+        TRACE("wait on %p returned %ld\n",lpOverlapped,r);
+    } while (r == STATUS_USER_APC);
+
+    if ( lpcbTransfer )
+        *lpcbTransfer = lpOverlapped->InternalHigh;
+
+    if ( lpdwFlags )
+        *lpdwFlags = lpOverlapped->Offset; /* see ws2_async_call_completion */
+
+    if (lpOverlapped->Internal  == STATUS_PENDING )
+        WSASetLastError ( WSA_IO_INCOMPLETE );
+
+    return (r == WAIT_OBJECT_0);
+}
+
+
 /***********************************************************************
  *      WINSOCK_DoAsyncSelect
  */
@@ -3481,34 +3471,267 @@


 /***********************************************************************
+ *		WSARecv			(WS2_32.67)
+ */
+int WINAPI WSARecv (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+		    LPDWORD NumberOfBytesReceived, LPDWORD lpFlags,
+		    LPWSAOVERLAPPED lpOverlapped,
+		    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
+{
+
+    return WSARecvFrom (s, lpBuffers, dwBufferCount, NumberOfBytesReceived, lpFlags,
+                        NULL, NULL, lpOverlapped, lpCompletionRoutine);
+
+}
+
+/***********************************************************************
+ *              WS2_recv                (INTERNAL)
+ */
+static int WS2_recv (int fd, struct iovec* iov, int count,
+                     struct WS_sockaddr *lpFrom, LPINT lpFromlen, LPDWORD lpFlags)
+{
+    /* Uses recvmsg() in order to provide scatter-gather I/O */
+    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 ( WSAENOBUFS );
+            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 )
+        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:
+    TRACE ( "recvmsg returned %d\n", n );
+
+    ws_sockaddr_free ( hdr.msg_name, lpFrom );
+    return n;
+}
+
+
+/***********************************************************************
+ *              WS2_async_recv          (INTERNAL)
+ */
+static void WS2_async_recv (async_private *as)
+{
+    ws2_async* wsa = (ws2_async*) as;
+    int result, err;
+
+    TRACE ( "async %p\n", wsa );
+
+    result = WS2_recv ( wsa->async.fd, wsa->iovec, wsa->n_iovecs,
+                        wsa->addr, wsa->addrlen, &wsa->flags );
+
+    if (result >= 0)
+    {
+        wsa->overlapped->Internal = STATUS_SUCCESS;
+        wsa->overlapped->InternalHigh = result;
+        TRACE ( "received %d bytes\n", result );
+        return;
+    }
+
+    err = errno;
+    if ( err == EAGAIN || err == EINTR )
+    {
+        wsa->overlapped->Internal = STATUS_PENDING;
+        TRACE ( "still pending\n" );
+    }
+    else
+    {
+        wsa->overlapped->Internal = STATUS_UNSUCCESSFUL;
+        TRACE ( "Error: %d\n", errno );
+    }
+}
+
+/***********************************************************************
  *              WSARecvFrom             (WS2_32.69)
  */
 INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
-                        LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct WS_sockaddr *lpFrom,
-                        LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
+                        LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,
+                        struct WS_sockaddr *lpFrom, LPINT lpFromlen,
+                        LPWSAOVERLAPPED lpOverlapped,
                         LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
 {
-  DWORD dwCount;
-  INT   rc;
+    struct iovec* iovec;
+    int fd, i, length, err = WSAENOTSOCK;
+    enum fd_type type;
+    DWORD flags;
+    ws2_async *wsa;
+
+    fd = _get_sock_fd_type (s, &type, &flags);
+
+    TRACE("socket %04x, wsabuf %p, nbufs %ld, flags %ld, from %p, "
+          "fromlen %ld, ovl %p, func %p, fd %d, type %d, flags %ld\n",
+          s, lpBuffers, dwBufferCount, *lpFlags, lpFrom,
+          (lpFromlen ? *lpFromlen : -1L),
+          lpOverlapped, lpCompletionRoutine,
+          fd, (int) type, flags);

-  FIXME( "(%i,%p,%lu,%p,%p,%p,%p,%p,%p: stub\n",
-         s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags,
-         lpFrom, lpFromlen, lpOverlapped, lpCompletionRoutine );
+    if (fd == -1)
+    {
+        err = WSAENOTSOCK;
+        goto error;
+    }

-  for( dwCount = 0, rc = 0; dwCount < dwBufferCount; dwCount++ )
-  {
+    iovec = HeapAlloc (GetProcessHeap(), 0,
+                       dwBufferCount * sizeof (struct iovec) );
+    if ( !iovec )
+    {
+        err = WSAENOBUFS;
+        goto err_close;
+    }

-    if( ( rc = WS_recvfrom(s, lpBuffers[ dwCount ].buf, (INT)lpBuffers[ dwCount ].len,
-                                (INT)*lpFlags, lpFrom, lpFromlen ) ) != 0 )
+    for (i = 0; i < dwBufferCount; i++)
     {
-       break;
+        iovec[i].iov_base = lpBuffers[i].buf;
+        iovec[i].iov_len  = lpBuffers[i].len;
     }

-  }
+    if ( (lpOverlapped || lpCompletionRoutine) && flags & FD_FLAG_OVERLAPPED )
+    {
+        /* Asynchronous recv  */

-  return rc;
-}
+        wsa = HeapAlloc ( GetProcessHeap(), 0, sizeof (ws2_async) );
+        if ( !wsa )
+        {
+            err = WSAENOBUFS;
+            goto err_free;
+        }
+
+        wsa->async.ops = &ws2_async_ops;
+        wsa->async.handle = (HANDLE) s;
+        wsa->async.fd = fd;
+        wsa->async.type = ASYNC_TYPE_READ;
+        wsa->async.func = WS2_async_recv;
+        wsa->user_overlapped = lpOverlapped;
+        if ( lpOverlapped )
+            wsa->overlapped = lpOverlapped;
+        else
+        {
+            wsa->overlapped = HeapAlloc ( GetProcessHeap(), 0,
+                                          sizeof (WSAOVERLAPPED) );
+            if ( !wsa->overlapped )
+            {
+                err = WSAENOBUFS;
+                goto err_free_as;
+            }
+        }
+        wsa->completion_func = ( lpCompletionRoutine ?
+                                 lpCompletionRoutine : ws2_default_completion );
+        wsa->iovec = iovec;
+        wsa->n_iovecs = dwBufferCount;
+        wsa->addr = lpFrom;
+        wsa->addrlen = lpFromlen;
+        wsa->flags = *lpFlags;
+
+        wsa->overlapped->Internal = STATUS_USER_APC;
+        wsa->overlapped->InternalHigh = 0;

+        TRACE ( "async %p, overlapped: %p, user overlapped: %p, completion: %p\n",
+                wsa, wsa->overlapped, wsa->user_overlapped, wsa->completion_func );
+
+        if ( !register_async ( &wsa->async ) )
+        {
+            err = wsaErrno ();
+            goto err_free_ov;
+        }
+
+        /* Check for immediate completion
+           (only possible if lpOverlapped is non-NULL) */
+        if ( !lpOverlapped )
+        {
+            WSASetLastError ( WSA_IO_PENDING );
+            return SOCKET_ERROR;
+        }
+        else if ( WSAGetOverlappedResult ( (HANDLE) s, lpOverlapped,
+                                           lpNumberOfBytesRecvd, FALSE, lpFlags) )
+        {
+            return 0;
+        }
+        else if ( WSAGetLastError () == WSA_IO_INCOMPLETE )
+        {
+            WSASetLastError ( WSA_IO_PENDING );
+            return SOCKET_ERROR;
+        }
+        else
+            goto err_free_ov;
+    }
+
+    /* Synchronous recv */
+
+    if (_is_blocking(s))
+    {
+        /* block here */
+        /* FIXME: OOB and exceptfds? */
+        do_block(fd, 1);
+    }
+
+    length = WS2_recv ( fd, iovec, dwBufferCount, lpFrom, lpFromlen, lpFlags );
+    if ( length == -1 )
+    {
+        err = wsaErrno();
+        goto err_free;
+    }
+
+    TRACE(" -> %i bytes\n", length);
+
+    *lpNumberOfBytesRecvd = length;
+
+    WS_FREE (iovec);
+    close(fd);
+    _enable_event(s, FD_READ, 0, 0);
+
+    return 0;
+
+err_free_ov:
+    if ( !wsa->user_overlapped )
+        HeapFree ( GetProcessHeap(), 0, wsa->overlapped );
+err_free_as:
+    HeapFree ( GetProcessHeap(), 0, wsa );
+err_free:
+    WS_FREE (iovec);
+err_close:
+    close (fd);
+error:
+    WARN(" -> ERROR %d\n", err);
+    SetLastError ( err );
+    return SOCKET_ERROR;
+}

 /***********************************************************************
  *              WSCInstallProvider             (WS2_32.88)
diff -ruX diffignore CVS/wine/dlls/winsock/ws2_32.spec MW/wine/dlls/winsock/ws2_32.spec
--- CVS/wine/dlls/winsock/ws2_32.spec	Tue Jan 15 20:17:38 2002
+++ MW/wine/dlls/winsock/ws2_32.spec	Wed Jan 16 18:13:36 2002
@@ -52,7 +52,7 @@
 37  stub     WSAEnumProtocolsA
 38  stub     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
@@ -79,7 +79,7 @@
 64  stub     WSANtohl
 65  stub     WSANtohs
 66  stub     WSAProviderConfigChange
-67  stub     WSARecv
+67  stdcall  WSARecv(long ptr long ptr ptr ptr ptr) WSARecv
 68  stub     WSARecvDisconnect
 69  stdcall  WSARecvFrom(long ptr long ptr ptr ptr ptr ptr ptr ) WSARecvFrom
 70  stub     WSARemoveServiceClass
diff -ruX diffignore CVS/wine/files/file.c MW/wine/files/file.c
--- CVS/wine/files/file.c	Wed Jan 16 18:14:09 2002
+++ MW/wine/files/file.c	Wed Jan 16 17:34:34 2002
@@ -224,7 +224,7 @@
  * Retrieve the Unix handle corresponding to a file handle.
  * Returns -1 on failure.
  */
-static int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, DWORD *flags )
+int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, DWORD *flags )
 {
     int ret, fd = -1;

diff -ruX diffignore CVS/wine/include/file.h MW/wine/include/file.h
--- CVS/wine/include/file.h	Wed Jan 16 18:14:04 2002
+++ MW/wine/include/file.h	Wed Jan 16 18:15:12 2002
@@ -12,6 +12,9 @@
 #include "winbase.h"
 #include "wine/windef16.h"  /* HFILE16 */

+/* FIXME: including server/protocol.h here leads to a handle_t type conflict elsewhere */
+enum fd_type; /* declared in server_protocol.h */
+
 #define MAX_PATHNAME_LEN   1024

 /* Definition of a full DOS file name */
@@ -97,6 +100,7 @@
 extern int FILE_strncasecmp( const char *str1, const char *str2, int len );
 extern void FILE_SetDosError(void);
 extern HANDLE FILE_DupUnixHandle( int fd, DWORD access, BOOL inherit );
+extern int FILE_GetUnixHandleType ( HANDLE handle, DWORD access, enum fd_type *type, DWORD *flags );
 extern int FILE_GetUnixHandle( HANDLE handle, DWORD access );
 extern BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info );
 extern HFILE16 FILE_Dup2( HFILE16 hFile1, HFILE16 hFile2 );








More information about the wine-devel mailing list