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 )