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:
parent
f15473f5f3
commit
cb98a6ed7a
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
131
server/sock.c
131
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 );
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
|
|
Loading…
Reference in New Issue