From 81f5200ba36593a020f74766827aa25bd2402d75 Mon Sep 17 00:00:00 2001
From: Corinna Vinschen <corinna@vinschen.de>
Date: Mon, 29 Mar 2004 19:41:17 +0000
Subject: [PATCH] 	* fhandler.h (class fhandler_socket): Add
 has_been_closed member. 	* fhandler_socket.cc
 (fhandler_socket::fhandler_socket): Initialize 	has_been_closed to 0. 
 (fhandler_socket::recvfrom): Use new asynchronous I/O driven 	wsock_event
 methods. 	(fhandler_socket::recvmsg): Ditto. 
 (fhandler_socket::sendto): Ditto. 	(fhandler_socket::sendmsg): Ditto. 
 * net.cc (wsock_event::prepare): Reimplement using asynchronous I/O. 
 (wsock_event::wait): Ditto. 	(wsock_event::release): New method. 	*
 wsock_event.h (class wsock_event): Remove ovr member.  Accomodate 	new
 implementation of prepare and wait methods.  Add release method.

---
 winsup/cygwin/ChangeLog          |  16 +++++
 winsup/cygwin/fhandler.h         |   1 +
 winsup/cygwin/fhandler_socket.cc | 106 +++++++++++++++++++++----------
 winsup/cygwin/net.cc             |  79 ++++++++++++++---------
 winsup/cygwin/wsock_event.h      |   6 +-
 5 files changed, 142 insertions(+), 66 deletions(-)

diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index ec81ae8a1..aa0951ee9 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,19 @@
+2004-03-29  Corinna Vinschen  <corinna@vinschen.de>
+
+	* fhandler.h (class fhandler_socket): Add has_been_closed member.
+	* fhandler_socket.cc (fhandler_socket::fhandler_socket): Initialize
+	has_been_closed to 0.
+	(fhandler_socket::recvfrom): Use new asynchronous I/O driven
+	wsock_event methods.
+	(fhandler_socket::recvmsg): Ditto.
+	(fhandler_socket::sendto): Ditto.
+	(fhandler_socket::sendmsg): Ditto.
+	* net.cc (wsock_event::prepare): Reimplement using asynchronous I/O.
+	(wsock_event::wait): Ditto.
+	(wsock_event::release): New method.
+	* wsock_event.h (class wsock_event): Remove ovr member.  Accomodate
+	new implementation of prepare and wait methods.  Add release method.
+
 2004-03-29  Thomas Pfaff  <tpfaff@gmx.net>
 
 	* thread.cc (pthread::atforkprepare): Call
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 0b5c71299..a38b50a55 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -377,6 +377,7 @@ class fhandler_socket: public fhandler_base
   struct _WSAPROTOCOL_INFOA *prot_info_ptr;
   char *sun_path;
   int had_connect_or_listen;
+  int has_been_closed;
 
  public:
   fhandler_socket ();
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
index 63f28fb27..0c3058867 100644
--- a/winsup/cygwin/fhandler_socket.cc
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -122,7 +122,7 @@ get_inet_addr (const struct sockaddr *in, int inlen,
 /* fhandler_socket */
 
 fhandler_socket::fhandler_socket ()
-  : fhandler_base (), sun_path (NULL)
+  : fhandler_base (), sun_path (NULL), has_been_closed (0)
 {
   set_need_fork_fixup ();
   prot_info_ptr = (LPWSAPROTOCOL_INFOA) cmalloc (HEAP_BUF,
@@ -726,19 +726,28 @@ fhandler_socket::recvfrom (void *ptr, size_t len, int flags,
     {
       WSABUF wsabuf = { len, (char *) ptr };
 
-      if (is_nonblocking ())
+      if (is_nonblocking () || has_been_closed)
 	res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, (DWORD *) &flags,
 			   from, fromlen,
 			   NULL, NULL);
       else
 	{
 	  wsock_event wsock_evt;
-	  res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, (DWORD *) &flags,
-			     from, fromlen,
-			     wsock_evt.prepare (), NULL);
-
-	  if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
-	    ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+	  long evt = (FD_CLOSE | ((flags & MSG_OOB) ? FD_OOB : FD_READ));
+	  if (wsock_evt.prepare (get_socket (), evt))
+	    {
+              do
+                {
+                  if (!(res = wsock_evt.wait (get_socket (), has_been_closed)))
+		    res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret,
+				       (DWORD *) &flags, from, fromlen,
+				       NULL, NULL);
+                }
+              while (res == SOCKET_ERROR
+                     && WSAGetLastError () == WSAEWOULDBLOCK
+                     && !has_been_closed);
+	      wsock_evt.release (get_socket ());
+	    }
 	}
     }
 
@@ -844,7 +853,7 @@ fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
 
       DWORD ret;
 
-      if (is_nonblocking ())
+      if (is_nonblocking () || has_been_closed)
 	res = WSARecvFrom (get_socket (),
 			   wsabuf, iovcnt, &ret, (DWORD *) &flags,
 			   from, fromlen,
@@ -852,13 +861,21 @@ fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
       else
 	{
 	  wsock_event wsock_evt;
-	  res = WSARecvFrom (get_socket (),
-			     wsabuf, iovcnt, &ret, (DWORD *) &flags,
-			     from, fromlen,
-			     wsock_evt.prepare (), NULL);
-
-	  if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
-	    ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+	  long evt = (FD_CLOSE | ((flags & MSG_OOB) ? FD_OOB : FD_READ));
+	  if (wsock_evt.prepare (get_socket (), evt))
+	    {
+              do
+                {
+                  if (!(res = wsock_evt.wait (get_socket (), has_been_closed)))
+		    res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret,
+				       (DWORD *) &flags, from, fromlen,
+				       NULL, NULL);
+                }
+              while (res == SOCKET_ERROR
+                     && WSAGetLastError () == WSAEWOULDBLOCK
+                     && !has_been_closed);
+	      wsock_evt.release (get_socket ());
+	    }
 	}
 
       if (res == SOCKET_ERROR)
@@ -915,7 +932,7 @@ fhandler_socket::sendto (const void *ptr, size_t len, int flags,
     {
       WSABUF wsabuf = { len, (char *) ptr };
 
-      if (is_nonblocking ())
+      if (is_nonblocking () || has_been_closed)
 	res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
 			 flags & MSG_WINMASK,
 			 (to ? (const struct sockaddr *) &sin : NULL), tolen,
@@ -923,13 +940,26 @@ fhandler_socket::sendto (const void *ptr, size_t len, int flags,
       else
 	{
 	  wsock_event wsock_evt;
-	  res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
-			   flags & MSG_WINMASK,
-			   (to ? (const struct sockaddr *) &sin : NULL), tolen,
-			   wsock_evt.prepare (), NULL);
-
-	  if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
-	    ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+	  if (wsock_evt.prepare (get_socket (), FD_CLOSE | FD_WRITE))
+	    {
+	      do 
+		{
+		  res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+				   flags & MSG_WINMASK,
+				   (to ? (const struct sockaddr *) &sin : NULL),
+				   tolen, NULL, NULL);
+		  if (res != SOCKET_ERROR
+		      || WSAGetLastError () != WSAEWOULDBLOCK)
+		    break;
+		  if (ret > 0)
+		    {
+		      res = 0;
+		      break;
+		    }
+		}
+	      while (!(res = wsock_evt.wait (get_socket (), has_been_closed)));
+	      wsock_evt.release (get_socket ());
+	    }
 	}
     }
 
@@ -1041,7 +1071,7 @@ fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot)
 
       DWORD ret;
 
-      if (is_nonblocking ())
+      if (is_nonblocking () || has_been_closed)
 	res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, flags,
 			 (struct sockaddr *) msg->msg_name,
 			 msg->msg_namelen,
@@ -1049,13 +1079,25 @@ fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot)
       else
 	{
 	  wsock_event wsock_evt;
-	  res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, flags,
-			   (struct sockaddr *) msg->msg_name,
-			   msg->msg_namelen,
-			   wsock_evt.prepare (), NULL);
-
-	  if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
-	    ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+	  if (wsock_evt.prepare (get_socket (), FD_CLOSE | FD_WRITE))
+	    {
+              do
+                {
+		  res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret,
+				   flags, (struct sockaddr *) msg->msg_name,
+				   msg->msg_namelen, NULL, NULL);
+                  if (res != SOCKET_ERROR
+                      || WSAGetLastError () != WSAEWOULDBLOCK)
+                    break;
+                  if (ret > 0)
+                    {
+                      res = 0;
+                      break;
+                    }
+                }
+              while (!(res = wsock_evt.wait (get_socket (), has_been_closed)));
+	      wsock_evt.release (get_socket ());
+	    }
 	}
 
       if (res == SOCKET_ERROR)
diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc
index 8c4090b28..3d76a433b 100644
--- a/winsup/cygwin/net.cc
+++ b/winsup/cygwin/net.cc
@@ -50,58 +50,75 @@ extern "C"
   int sscanf (const char *, const char *, ...);
 }				/* End of "C" section */
 
-LPWSAOVERLAPPED
-wsock_event::prepare ()
+bool
+wsock_event::prepare (int sock, long event_mask)
 {
-  LPWSAOVERLAPPED ret = NULL;
-
   SetLastError (0);
-  if ((event = WSACreateEvent ()) != WSA_INVALID_EVENT)
+  if ((event = WSACreateEvent ()) != WSA_INVALID_EVENT
+      && WSAEventSelect (sock, event, event_mask) == SOCKET_ERROR)
     {
-      memset (&ovr, 0, sizeof ovr);
-      ovr.hEvent = event;
-      ret = &ovr;
+      debug_printf ("WSAEventSelect: %E");
+      WSACloseEvent (event);
+      event = WSA_INVALID_EVENT;
     }
-  else if (GetLastError () == ERROR_PROC_NOT_FOUND) /* winsock2 not available */
-    WSASetLastError (0);
-
-  debug_printf ("%d = wsock_event::prepare ()", ret);
-  return ret;
+  return event != WSA_INVALID_EVENT;
 }
 
 int
-wsock_event::wait (int socket, LPDWORD flags)
+wsock_event::wait (int sock, int &closed)
 {
   int ret = -1;
+  DWORD wsa_err = 0;
   WSAEVENT ev[2] = { event, signal_arrived };
-  DWORD len;
-
   switch (WSAWaitForMultipleEvents (2, ev, FALSE, WSA_INFINITE, FALSE))
     {
       case WSA_WAIT_EVENT_0:
-	if (WSAGetOverlappedResult (socket, &ovr, &len, FALSE, flags))
-	  ret = (int) len;
+        WSANETWORKEVENTS evts;
+	memset (&evts, 0, sizeof evts);
+	WSAEnumNetworkEvents (sock, event, &evts);
+	if (evts.lNetworkEvents & FD_READ)
+	  {
+	    if (evts.iErrorCode[FD_READ_BIT])
+	      wsa_err = evts.iErrorCode[FD_READ_BIT];
+	    else
+	      ret = 0;
+	  }
+	else if (evts.lNetworkEvents & FD_WRITE)
+	  {
+	    if (evts.iErrorCode[FD_WRITE_BIT])
+	      wsa_err = evts.iErrorCode[FD_WRITE_BIT];
+	    else
+	      ret = 0;
+	  }
+	if (evts.lNetworkEvents & FD_CLOSE)
+	  {
+	    closed = 1;
+	    if (!wsa_err && evts.iErrorCode[FD_CLOSE_BIT])
+	      wsa_err = evts.iErrorCode[FD_CLOSE_BIT];
+	    else
+	      ret = 0;
+	  }
+	if (wsa_err)
+	  WSASetLastError (wsa_err);
 	break;
       case WSA_WAIT_EVENT_0 + 1:
-	if (!CancelIo ((HANDLE) socket))
-	  {
-	    debug_printf ("CancelIo() %E, fallback to blocking io");
-	    WSAGetOverlappedResult (socket, &ovr, &len, TRUE, flags);
-	  }
-	else
-	  WSASetLastError (WSAEINTR);
+        WSASetLastError (WSAEINTR);
 	break;
-      case WSA_WAIT_FAILED:
-	break;
-      default:			/* Should be impossible. *LOL* */
+      default:
 	WSASetLastError (WSAEFAULT);
-	break;
     }
-  WSACloseEvent (event);
-  event = NULL;
   return ret;
 }
 
+void
+wsock_event::release (int sock)
+{
+  WSAEventSelect (sock, event, 0);
+  WSACloseEvent (event);
+  unsigned long non_block = 0;
+  ioctlsocket (sock, FIONBIO, &non_block);
+}
+
 WSADATA wsadata;
 
 static fhandler_socket *
diff --git a/winsup/cygwin/wsock_event.h b/winsup/cygwin/wsock_event.h
index 3f8638134..9a1f07ac1 100644
--- a/winsup/cygwin/wsock_event.h
+++ b/winsup/cygwin/wsock_event.h
@@ -14,7 +14,6 @@ details. */
 class wsock_event
 {
   WSAEVENT		event;
-  WSAOVERLAPPED		ovr;
 public:
   wsock_event () : event (NULL) {};
   ~wsock_event ()
@@ -25,8 +24,9 @@ public:
     };
 
   /* The methods are implemented in net.cc */
-  LPWSAOVERLAPPED prepare ();
-  int wait (int socket, LPDWORD flags);
+  bool prepare (int sock, long event_mask);
+  int wait (int sock, int &closed);
+  void release (int sock);
 };
 
 #endif /* __WSOCK_EVENT_H__ */