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 Wilck Phone: +49 5251 8 15113
Fujitsu Siemens Computers Fax: +49 5251 8 20409
Heinz-Nixdorf-Ring 1 mailto:Martin.Wilck@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 );