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().
This commit is contained in:
Ove Kaaven 2000-03-26 18:21:16 +00:00 committed by Alexandre Julliard
parent f15473f5f3
commit cb98a6ed7a
4 changed files with 151 additions and 73 deletions

View file

@ -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; q<WS_ACCEPT_QUEUE; q++)
if (InterlockedCompareExchange((PVOID*)&pwsi->accept_old[q], (PVOID)s, (PVOID)0) == (PVOID)0)
break;
if (q<WS_ACCEPT_QUEUE)
pwsi->accept_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; q<WS_ACCEPT_QUEUE; q++)
if (pwsi->accept_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; i<FD_MAX_EVENTS; i++)
if (pmask & (1<<i)) {
TRACE("post: event bit %d, error %d\n", i, req->errors[i]);
PostMessageA(info->hWnd, info->uMsg, info->sock,
WSAMAKESELECTREPLY(1<<i, req->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;

View file

@ -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 */

View file

@ -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 );
}

View file

@ -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 )