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(a)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 );
On Wed, 16 Jan 2002, Martin Wilck wrote:
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
These 2 +winsock traces are from juno 4 (socks.gz) and juno 2 (winsock.gz). Seems rather sluggish. I wish I'd run traces without the patch also. Connection is rather tenuous anyway - ppp to a broken-ring net to a router to an ethernet LAN to a proxy server to I'm not sure what kind of link to the internet, but it usually works faster than that. The app reported it failed to get my mail on the first session, but sent 1 letter. At that point I started tracing it and it reported success, but was very sluggish. Maybe you can see something in the traces. I'll try backing out the patches and make another trace or two, I guess. Lawson ---oof---
participants (2)
-
lawson_whitney@juno.com -
Martin Wilck