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().
diff --git a/dlls/winsock/socket.c b/dlls/winsock/socket.c
index e867126..c166cc1 100644
--- a/dlls/winsock/socket.c
+++ b/dlls/winsock/socket.c
@@ -107,11 +107,15 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/***********************************************************************
* 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 @@
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 @@
#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 @@
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 @@
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 @@
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 fd040e4..15fa780 100644
--- a/include/server.h
+++ b/include/server.h
@@ -611,6 +611,7 @@
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 @@
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 5abe4ca..86248e7 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -56,6 +56,7 @@
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 @@
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 @@
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 @@
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 @@
}
/* 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 @@
}
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 @@
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 0ef5f73..04c8172 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -728,7 +728,8 @@
{
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 )