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:[EMAIL PROTECTED] 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 +@REQ(set_socket_deferred) + handle_t handle; /* handle to the socket */ + handle_t deferred; /* handle to the socket for which accept() is +deferred */ +@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",