Patch: async-winsock.diff
Basic implementation of asynchronous WSARecvFrom()/WSASendTo()/WSAGetOverlappedResult(), client side.
Patch against: CVS 2002-04-12, with my sock-fd-type and sock-accept-deferred patches applied. Also requires the server side patch (async-sock-server).
Modified files: dlls/winsock: socket.c, ws2_32.spec include: winsock2.h
diff -ruNX ignore TMP/wine/dlls/winsock/socket.c MW/wine/dlls/winsock/socket.c --- TMP/wine/dlls/winsock/socket.c Fri Apr 12 14:55:42 2002 +++ MW/wine/dlls/winsock/socket.c Fri Apr 12 15:13:28 2002 @@ -105,6 +105,7 @@ #include "wine/winbase16.h" #include "wingdi.h" #include "winuser.h" +#include "winerror.h" #include "winsock2.h" #include "ws2tcpip.h" #include "wsipx.h" @@ -126,6 +127,170 @@ inet_ntoa(((struct sockaddr_in *)a)->sin_addr), \ ntohs(((struct sockaddr_in *)a)->sin_port))
+/**************************************************************** + * Async IO declarations and operations + ****************************************************************/ +#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; + +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 ( RtlNtStatusToDosError (as->overlapped->Internal), + as->overlapped->InternalHigh, + as->user_overlapped, + as->flags ); + ws2_async_cleanup ( &as->async ); +} + +static void WS2_async_recv (async_private *as); +static void WS2_async_send (async_private *as); + +/*********************************************************************** + * WS2_make_async (WS2_32.69) + */ +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; +} + +/****************************************************************/ + /* ----------------------------------- internal data */
/* ws_... struct conversion flags */ @@ -1988,6 +2153,88 @@ }
/*********************************************************************** + * WS2_send (INTERNAL) + */ +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) + */ +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 = 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 ); + } +} + +/*********************************************************************** * WSASendTo (WS2_32.74) */ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, @@ -1996,9 +2243,9 @@ 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", @@ -2006,6 +2253,7 @@ to, tolen, lpOverlapped, lpCompletionRoutine);
fd = _get_sock_fd_type( s, &type, &flags ); + TRACE ( "fd=%d, type=%d, flags=%x\n", fd, type, flags );
if ( fd == -1 ) goto error; @@ -2014,7 +2262,7 @@
if ( !iovec ) { - err = WSAENOBUFS; + err = WSAEFAULT; goto err_close; }
@@ -2024,30 +2272,46 @@ 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 = RtlNtStatusToDosError ( ret );
- msghdr.msg_iov = iovec; - msghdr.msg_iovlen = dwBufferCount; - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; - msghdr.msg_flags = 0; + if ( !lpOverlapped ) + HeapFree ( GetProcessHeap(), 0, wsa->overlapped ); + HeapFree ( GetProcessHeap(), 0, wsa ); + goto err_free; + }
- /* FIXME: Treat overlapped IO here */ + _enable_event ( s, FD_WRITE, 0, 0 ); + + /* Try immediate completion */ + if ( lpOverlapped ) + { + if ( WSAGetOverlappedResult ( (HANDLE) s, lpOverlapped, + lpNumberOfBytesSent, FALSE, &dwFlags) ) + return 0; + + if ( (err = WSAGetLastError ()) != WSA_IO_INCOMPLETE ) + { + WARN ("err=%d\n", err); + goto err_free; + } + } + + WSASetLastError (WSA_IO_PENDING); + return SOCKET_ERROR; + }
if (_is_blocking(s)) { @@ -2055,9 +2319,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 ) @@ -2065,24 +2328,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; }
@@ -2686,6 +2947,53 @@ 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; + + TRACE ("r=%lx, int=%lx inth%ld\n", r, lpOverlapped->Internal, lpOverlapped->InternalHigh); + + if ( r == WAIT_OBJECT_0 ) + return TRUE; + + if ( lpOverlapped->Internal == STATUS_PENDING ) + WSASetLastError ( WSA_IO_INCOMPLETE ); + else + { + DWORD err = RtlNtStatusToDosError ( lpOverlapped->Internal ); + WSASetLastError ( err ); + TRACE ( "Error: %lx\n", err ); + } + + return FALSE; +} + + /*********************************************************************** * WSAAsyncSelect (WS2_32.101) */ @@ -3278,6 +3586,105 @@ }
/*********************************************************************** + * WS2_recv (INTERNAL) + */ +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 ) + 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 ); + 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 ); + + 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 = 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, @@ -3286,11 +3693,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", @@ -3299,6 +3704,7 @@ lpOverlapped, lpCompletionRoutine);
fd = _get_sock_fd_type( s, &type, &flags ); + TRACE ( "fd=%d, type=%d, flags=%x\n", fd, type, flags );
if (fd == -1) { @@ -3306,11 +3712,10 @@ 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; }
@@ -3320,67 +3725,69 @@ 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 = RtlNtStatusToDosError ( ret );
- msghdr.msg_iov = iovec; - msghdr.msg_iovlen = dwBufferCount; - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; - msghdr.msg_flags = 0; + if ( !lpOverlapped ) + HeapFree ( GetProcessHeap(), 0, wsa->overlapped ); + HeapFree ( GetProcessHeap(), 0, wsa ); + goto err_free; + }
- /* FIXME: Treat overlapped IO here */ + _enable_event ( s, FD_READ, 0, 0 );
- if (_is_blocking(s)) + /* Try immediate completion */ + if ( lpOverlapped ) + { + if ( WSAGetOverlappedResult ( (HANDLE) s, lpOverlapped, + lpNumberOfBytesRecvd, FALSE, lpFlags) ) + return 0; + + if ( (err = WSAGetLastError ()) != WSA_IO_INCOMPLETE ) + goto err_free; + } + + WSASetLastError ( WSA_IO_PENDING ); + return SOCKET_ERROR; + } + + 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); diff -ruNX ignore TMP/wine/dlls/winsock/ws2_32.spec MW/wine/dlls/winsock/ws2_32.spec --- TMP/wine/dlls/winsock/ws2_32.spec Fri Apr 12 14:55:42 2002 +++ MW/wine/dlls/winsock/ws2_32.spec Fri Apr 12 15:05:29 2002 @@ -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/include/winsock2.h MW/wine/include/winsock2.h --- TMP/wine/include/winsock2.h Tue Apr 2 16:51:40 2002 +++ MW/wine/include/winsock2.h Fri Apr 12 15:05:29 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)