From cb98a6ed7a9999533e27ff877179633f88465c3a Mon Sep 17 00:00:00 2001 From: Ove Kaaven Date: Sun, 26 Mar 2000 18:21:16 +0000 Subject: [PATCH] Handle POLLHUP better (delay FD_CLOSE notification until all data has been read). Made WSAEnumNetworkEvents atomic. Convert socket event error codes properly. Made accept()-ed sockets inherit the listening socket's WSAAsyncSelect(). --- dlls/winsock/socket.c | 87 ++++++++++++++++++++++++++------- include/server.h | 3 +- server/sock.c | 131 ++++++++++++++++++++++++++++++-------------------- server/trace.c | 3 +- 4 files changed, 151 insertions(+), 73 deletions(-) diff --git a/dlls/winsock/socket.c b/dlls/winsock/socket.c index e867126b92a..c166cc15a80 100644 --- a/dlls/winsock/socket.c +++ b/dlls/winsock/socket.c @@ -107,11 +107,15 @@ typedef struct /* WSAAsyncSelect() control struct */ HANDLE service, event, sock; HWND hWnd; UINT uMsg; + LONG lEvent; + struct _WSINFO *pwsi; } ws_select_info; #define WS_MAX_SOCKETS_PER_PROCESS 128 /* reasonable guess */ #define WS_MAX_UDP_DATAGRAM 1024 +#define WS_ACCEPT_QUEUE 6 + #define WSI_BLOCKINGCALL 0x00000001 /* per-thread info flags */ #define WSI_BLOCKINGHOOK 0x00000002 /* 32-bit callback */ @@ -135,6 +139,8 @@ typedef struct _WSINFO char* dbuffer; /* buffer for dummies (32 bytes) */ DWORD blocking_hook; + + volatile HANDLE accept_old[WS_ACCEPT_QUEUE], accept_new[WS_ACCEPT_QUEUE]; } WSINFO, *LPWSINFO; /* function prototypes */ @@ -229,6 +235,7 @@ static int _is_blocking(SOCKET s) req->handle = s; req->service = FALSE; req->s_event = 0; + req->c_event = 0; sock_server_call( REQ_GET_SOCKET_EVENT ); return (req->state & WS_FD_NONBLOCKING) == 0; } @@ -240,6 +247,7 @@ static unsigned int _get_sock_mask(SOCKET s) req->handle = s; req->service = FALSE; req->s_event = 0; + req->c_event = 0; sock_server_call( REQ_GET_SOCKET_EVENT ); return req->mask; } @@ -258,6 +266,7 @@ static int _get_sock_error(SOCKET s, unsigned int bit) req->handle = s; req->service = FALSE; req->s_event = 0; + req->c_event = 0; sock_server_call( REQ_GET_SOCKET_EVENT ); return req->errors[bit]; } @@ -694,6 +703,21 @@ struct ws_protoent* _check_buffer_pe(LPWSINFO pwsi, int size) /*********************************************************************** * accept() (WSOCK32.1) */ +static void WSOCK32_async_accept(LPWSINFO pwsi, SOCKET s, SOCKET as) +{ + int q; + /* queue socket for WSAAsyncSelect */ + for (q=0; qaccept_old[q], (PVOID)s, (PVOID)0) == (PVOID)0) + break; + if (qaccept_new[q] = as; + else + ERR("accept queue too small\n"); + /* now signal our AsyncSelect handler */ + _enable_event(s, WS_FD_SERVEVENT, 0, 0); +} + SOCKET WINAPI WSOCK32_accept(SOCKET s, struct sockaddr *addr, INT *addrlen32) { @@ -724,7 +748,9 @@ SOCKET WINAPI WSOCK32_accept(SOCKET s, struct sockaddr *addr, sock_server_call( REQ_ACCEPT_SOCKET ); if( req->handle >= 0 ) { - int fd = _get_sock_fd( s = req->handle ); + unsigned omask = _get_sock_mask( s ); + SOCKET as = req->handle; + int fd = _get_sock_fd( as ); if( getpeername(fd, addr, addrlen32) != -1 ) { #ifdef HAVE_IPX @@ -743,7 +769,9 @@ SOCKET WINAPI WSOCK32_accept(SOCKET s, struct sockaddr *addr, #endif } else SetLastError(wsaErrno()); close(fd); - return s; + if (omask & WS_FD_SERVEVENT) + WSOCK32_async_accept(pwsi, s, as); + return as; } } return INVALID_SOCKET; @@ -2119,11 +2147,10 @@ int WINAPI WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEvent, LPWSANETWORKEVENTS lp req->handle = s; req->service = TRUE; req->s_event = 0; + req->c_event = hEvent; sock_server_call( REQ_GET_SOCKET_EVENT ); lpEvent->lNetworkEvents = req->pmask; memcpy(lpEvent->iErrorCode, req->errors, sizeof(lpEvent->iErrorCode)); - if (hEvent) - ResetEvent(hEvent); return 0; } else SetLastError(WSAEINVAL); @@ -2155,34 +2182,55 @@ int WINAPI WSAEventSelect(SOCKET s, WSAEVENT hEvent, LONG lEvent) VOID CALLBACK WINSOCK_DoAsyncEvent( ULONG_PTR ptr ) { - /* FIXME: accepted socket uses same event object as listening socket by default - * (at least before a new WSAAsyncSelect is issued), must handle it somehow */ ws_select_info *info = (ws_select_info*)ptr; + LPWSINFO pwsi = info->pwsi; struct get_socket_event_request *req = get_req_buffer(); - unsigned int i, pmask; + unsigned int i, pmask, orphan = FALSE; TRACE("socket %08x, event %08x\n", info->sock, info->event); SetLastError(0); req->handle = info->sock; req->service = TRUE; req->s_event = info->event; /* <== avoid race conditions */ + req->c_event = info->event; sock_server_call( REQ_GET_SOCKET_EVENT ); - if ( GetLastError() == WSAEINVAL ) + if ( (GetLastError() == WSAENOTSOCK) || (GetLastError() == WSAEINVAL) ) { /* orphaned event (socket closed or something) */ - TRACE("orphaned event, self-destructing\n"); - SERVICE_Delete( info->service ); - WS_FREE(info); - return; + pmask = WS_FD_SERVEVENT; + orphan = TRUE; + } else + pmask = req->pmask; + /* check for accepted sockets that needs to inherit WSAAsyncSelect */ + if (pmask & WS_FD_SERVEVENT) { + int q; + for (q=0; qaccept_old[q] == info->sock) { + /* there's only one service thread per pwsi, no lock necessary */ + HANDLE as = pwsi->accept_new[q]; + if (as) { + pwsi->accept_new[q] = 0; + pwsi->accept_old[q] = 0; + WSAAsyncSelect(as, info->hWnd, info->uMsg, info->lEvent); + } + } + pmask &= ~WS_FD_SERVEVENT; } /* dispatch network events */ - pmask = req->pmask; for (i=0; ierrors[i]); PostMessageA(info->hWnd, info->uMsg, info->sock, WSAMAKESELECTREPLY(1<errors[i])); } + /* cleanup */ + if (orphan) + { + TRACE("orphaned event, self-destructing\n"); + /* SERVICE_Delete closes the event object */ + SERVICE_Delete( info->service ); + WS_FREE(info); + } } INT WINAPI WSAAsyncSelect(SOCKET s, HWND hWnd, UINT uMsg, LONG lEvent) @@ -2198,17 +2246,20 @@ INT WINAPI WSAAsyncSelect(SOCKET s, HWND hWnd, UINT uMsg, LONG lEvent) ws_select_info *info = (ws_select_info*)WS_ALLOC(sizeof(ws_select_info)); if( info ) { - HANDLE hObj = CreateEventA( NULL, FALSE, FALSE, NULL ); + HANDLE hObj = CreateEventA( NULL, TRUE, FALSE, NULL ); INT err; - info->sock = s; - info->event = hObj; - info->hWnd = hWnd; - info->uMsg = uMsg; + info->sock = s; + info->event = hObj; + info->hWnd = hWnd; + info->uMsg = uMsg; + info->lEvent = lEvent; + info->pwsi = pwsi; info->service = SERVICE_AddObject( hObj, WINSOCK_DoAsyncEvent, (ULONG_PTR)info ); err = WSAEventSelect( s, hObj, lEvent | WS_FD_SERVEVENT ); if (err) { + /* SERVICE_Delete closes the event object */ SERVICE_Delete( info->service ); WS_FREE(info); return err; diff --git a/include/server.h b/include/server.h index fd040e4341b..15fa78064ec 100644 --- a/include/server.h +++ b/include/server.h @@ -611,6 +611,7 @@ struct get_socket_event_request IN int handle; /* handle to the socket */ IN int service; /* clear pending? */ IN int s_event; /* "expected" event object */ + IN int c_event; /* event to clear */ OUT unsigned int mask; /* event mask */ OUT unsigned int pmask; /* pending events */ OUT unsigned int state; /* status bits */ @@ -1208,7 +1209,7 @@ enum request REQ_NB_REQUESTS }; -#define SERVER_PROTOCOL_VERSION 5 +#define SERVER_PROTOCOL_VERSION 6 /* ### make_requests end ### */ /* Everything above this line is generated automatically by tools/make_requests */ diff --git a/server/sock.c b/server/sock.c index 5abe4cad05a..86248e7ad4d 100644 --- a/server/sock.c +++ b/server/sock.c @@ -56,6 +56,7 @@ static int sock_get_poll_events( struct object *obj ); static void sock_poll_event( struct object *obj, int event ); static int sock_get_fd( struct object *obj ); static void sock_destroy( struct object *obj ); +static int sock_get_error( int err ); static void sock_set_error(void); static const struct object_ops sock_ops = @@ -89,17 +90,17 @@ static void sock_reselect( struct sock *sock ) pfd.events = ev; pfd.revents = 0; poll( &pfd, 1, 0 ); - if (pfd.revents & (POLLIN|POLLOUT|POLLPRI)) + if (pfd.revents) sock_poll_event( &sock->obj, pfd.revents); } inline static int sock_error(int s) { - unsigned int optval, optlen; + unsigned int optval = 0, optlen; optlen = sizeof(optval); getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen); - return optval; + return optval ? sock_get_error(optval) : 0; } static void sock_poll_event( struct object *obj, int event ) @@ -177,10 +178,10 @@ static void sock_poll_event( struct object *obj, int event ) if (debug_level) fprintf(stderr, "socket %d got OOB data\n", sock->obj.fd); } - if (event & (POLLERR|POLLHUP)) - { + if (((event & POLLERR) || ((event & (POLLIN|POLLHUP)) == POLLHUP)) + && (sock->state & (WS_FD_READ|WS_FD_WRITE))) { + /* socket closing */ sock->errors[FD_CLOSE_BIT] = sock_error( sock->obj.fd ); - /* we got an error, socket closing? */ sock->state &= ~(WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE); sock->pmask |= FD_CLOSE; if (debug_level) @@ -333,7 +334,8 @@ static struct object *accept_socket( int handle ) acceptsock->hmask = 0; acceptsock->pmask = 0; acceptsock->event = NULL; - if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event ); + if (sock->event && !(sock->mask & WS_FD_SERVEVENT)) + acceptsock->event = (struct event *)grab_object( sock->event ); sock_reselect( acceptsock ); clear_error(); @@ -344,69 +346,75 @@ static struct object *accept_socket( int handle ) } /* set the last error depending on errno */ -static void sock_set_error(void) +static int sock_get_error( int err ) { - switch (errno) + switch (err) { - case EINTR: set_error(WSAEINTR);break; - case EBADF: set_error(WSAEBADF);break; + case EINTR: return WSAEINTR; break; + case EBADF: return WSAEBADF; break; case EPERM: - case EACCES: set_error(WSAEACCES);break; - case EFAULT: set_error(WSAEFAULT);break; - case EINVAL: set_error(WSAEINVAL);break; - case EMFILE: set_error(WSAEMFILE);break; - case EWOULDBLOCK: set_error(WSAEWOULDBLOCK);break; - case EINPROGRESS: set_error(WSAEINPROGRESS);break; - case EALREADY: set_error(WSAEALREADY);break; - case ENOTSOCK: set_error(WSAENOTSOCK);break; - case EDESTADDRREQ: set_error(WSAEDESTADDRREQ);break; - case EMSGSIZE: set_error(WSAEMSGSIZE);break; - case EPROTOTYPE: set_error(WSAEPROTOTYPE);break; - case ENOPROTOOPT: set_error(WSAENOPROTOOPT);break; - case EPROTONOSUPPORT: set_error(WSAEPROTONOSUPPORT);break; - case ESOCKTNOSUPPORT: set_error(WSAESOCKTNOSUPPORT);break; - case EOPNOTSUPP: set_error(WSAEOPNOTSUPP);break; - case EPFNOSUPPORT: set_error(WSAEPFNOSUPPORT);break; - case EAFNOSUPPORT: set_error(WSAEAFNOSUPPORT);break; - case EADDRINUSE: set_error(WSAEADDRINUSE);break; - case EADDRNOTAVAIL: set_error(WSAEADDRNOTAVAIL);break; - case ENETDOWN: set_error(WSAENETDOWN);break; - case ENETUNREACH: set_error(WSAENETUNREACH);break; - case ENETRESET: set_error(WSAENETRESET);break; - case ECONNABORTED: set_error(WSAECONNABORTED);break; + case EACCES: return WSAEACCES; break; + case EFAULT: return WSAEFAULT; break; + case EINVAL: return WSAEINVAL; break; + case EMFILE: return WSAEMFILE; break; + case EWOULDBLOCK: return WSAEWOULDBLOCK; break; + case EINPROGRESS: return WSAEINPROGRESS; break; + case EALREADY: return WSAEALREADY; break; + case ENOTSOCK: return WSAENOTSOCK; break; + case EDESTADDRREQ: return WSAEDESTADDRREQ; break; + case EMSGSIZE: return WSAEMSGSIZE; break; + case EPROTOTYPE: return WSAEPROTOTYPE; break; + case ENOPROTOOPT: return WSAENOPROTOOPT; break; + case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT; break; + case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT; break; + case EOPNOTSUPP: return WSAEOPNOTSUPP; break; + case EPFNOSUPPORT: return WSAEPFNOSUPPORT; break; + case EAFNOSUPPORT: return WSAEAFNOSUPPORT; break; + case EADDRINUSE: return WSAEADDRINUSE; break; + case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL; break; + case ENETDOWN: return WSAENETDOWN; break; + case ENETUNREACH: return WSAENETUNREACH; break; + case ENETRESET: return WSAENETRESET; break; + case ECONNABORTED: return WSAECONNABORTED; break; case EPIPE: - case ECONNRESET: set_error(WSAECONNRESET);break; - case ENOBUFS: set_error(WSAENOBUFS);break; - case EISCONN: set_error(WSAEISCONN);break; - case ENOTCONN: set_error(WSAENOTCONN);break; - case ESHUTDOWN: set_error(WSAESHUTDOWN);break; - case ETOOMANYREFS: set_error(WSAETOOMANYREFS);break; - case ETIMEDOUT: set_error(WSAETIMEDOUT);break; - case ECONNREFUSED: set_error(WSAECONNREFUSED);break; - case ELOOP: set_error(WSAELOOP);break; - case ENAMETOOLONG: set_error(WSAENAMETOOLONG);break; - case EHOSTDOWN: set_error(WSAEHOSTDOWN);break; - case EHOSTUNREACH: set_error(WSAEHOSTUNREACH);break; - case ENOTEMPTY: set_error(WSAENOTEMPTY);break; + case ECONNRESET: return WSAECONNRESET; break; + case ENOBUFS: return WSAENOBUFS; break; + case EISCONN: return WSAEISCONN; break; + case ENOTCONN: return WSAENOTCONN; break; + case ESHUTDOWN: return WSAESHUTDOWN; break; + case ETOOMANYREFS: return WSAETOOMANYREFS; break; + case ETIMEDOUT: return WSAETIMEDOUT; break; + case ECONNREFUSED: return WSAECONNREFUSED; break; + case ELOOP: return WSAELOOP; break; + case ENAMETOOLONG: return WSAENAMETOOLONG; break; + case EHOSTDOWN: return WSAEHOSTDOWN; break; + case EHOSTUNREACH: return WSAEHOSTUNREACH; break; + case ENOTEMPTY: return WSAENOTEMPTY; break; #ifdef EPROCLIM - case EPROCLIM: set_error(WSAEPROCLIM);break; + case EPROCLIM: return WSAEPROCLIM; break; #endif #ifdef EUSERS - case EUSERS: set_error(WSAEUSERS);break; + case EUSERS: return WSAEUSERS; break; #endif #ifdef EDQUOT - case EDQUOT: set_error(WSAEDQUOT);break; + case EDQUOT: return WSAEDQUOT; break; #endif #ifdef ESTALE - case ESTALE: set_error(WSAESTALE);break; + case ESTALE: return WSAESTALE; break; #endif #ifdef EREMOTE - case EREMOTE: set_error(WSAEREMOTE);break; + case EREMOTE: return WSAEREMOTE; break; #endif - default: perror("sock_set_error"); set_error( ERROR_UNKNOWN ); break; + default: errno=err; perror("sock_set_error"); return ERROR_UNKNOWN; break; } } +/* set the last error depending on errno */ +static void sock_set_error(void) +{ + set_error( sock_get_error( errno ) ); +} + /* create a socket */ DECL_HANDLER(create_socket) { @@ -496,6 +504,12 @@ DECL_HANDLER(get_socket_event) } if (!req->s_event) { + if (req->c_event) + { + struct event *cevent = get_event_obj(current->process, req->c_event, EVENT_MODIFY_STATE); + reset_event( cevent ); + release_object( cevent ); + } sock->pmask = 0; sock_reselect( sock ); } @@ -517,5 +531,16 @@ DECL_HANDLER(enable_socket_event) sock->state |= req->sstate; sock->state &= ~req->cstate; sock_reselect( sock ); + + /* service trigger */ + if (req->mask & WS_FD_SERVEVENT) + { + sock->pmask |= WS_FD_SERVEVENT; + if (sock->event) { + if (debug_level) fprintf(stderr, "signalling service event ptr %p\n", sock->event); + set_event(sock->event); + } + } + release_object( &sock->obj ); } diff --git a/server/trace.c b/server/trace.c index 0ef5f73aa83..04c8172d4a9 100644 --- a/server/trace.c +++ b/server/trace.c @@ -728,7 +728,8 @@ static void dump_get_socket_event_request( const struct get_socket_event_request { fprintf( stderr, " handle=%d,", req->handle ); fprintf( stderr, " service=%d,", req->service ); - fprintf( stderr, " s_event=%d", req->s_event ); + fprintf( stderr, " s_event=%d,", req->s_event ); + fprintf( stderr, " c_event=%d", req->c_event ); } static void dump_get_socket_event_reply( const struct get_socket_event_request *req ) -- 2.11.4.GIT