bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2] implement full-blown select(2) for winsock


From: Paolo Bonzini
Subject: [PATCH v2] implement full-blown select(2) for winsock
Date: Tue, 23 Sep 2008 16:51:23 +0200

This revised patch includes the select(2) wrapper in sys_select,
not sys_socket.  This makes sense given the very purpose of the
wrapper -- which is to let the client use select for other descriptor
types than socket handles.

I do believe that the code belongs in the generic "header file"
modules, since without it the provided sys/select.h file would be
totally useless.

The actual code is the same as the previous patch.

Paolo

2008-09-23  Paolo Bonzini  <address@hidden>

        * lib/sys_select.in.h: Install select wrapper.
        * lib/sys_socket.in.h: Use a new descriptive name when there is no
        select wrapper.
        * lib/winsock-select.c: New.
        * m4/sys_select_h.m4: Compile lib/winsock-select.c if WinSock is used.
        Require gl_HEADER_SYS_SOCKET.
        * modules/sys_select: Depend on alloca, add lib/winsock-select.c.
        Replace HAVE_WINSOCK2_H.

---
 lib/sys_select.in.h  |    7 +
 lib/sys_socket.in.h  |    3 +-
 lib/winsock-select.c |  407 ++++++++++++++++++++++++++++++++++++++++++++++++++
 m4/sys_select_h.m4   |    4 +
 modules/sys_select   |    3 +
 6 files changed, 422 insertions(+), 2 deletions(-)
 create mode 100644 lib/winsock-select.c

diff --git a/lib/sys_select.in.h b/lib/sys_select.in.h
index 86ae539..341543b 100644
--- a/lib/sys_select.in.h
+++ b/lib/sys_select.in.h
@@ -39,6 +39,13 @@
 
 # include <sys/socket.h>
 
+# if @HAVE_WINSOCK2_H@
+#  undef select
+#  define select               rpl_select
+
+extern int rpl_select (int, fd_set *, fd_set *, fd_set *, struct timeval *);
+# endif
+
 #endif
 
 #endif /* _GL_SYS_SELECT_H */
diff --git a/lib/sys_socket.in.h b/lib/sys_socket.in.h
index 53c7195..c7cd57f 100644
--- a/lib/sys_socket.in.h
+++ b/lib/sys_socket.in.h
@@ -156,9 +156,8 @@ rpl_fd_isset (int fd, fd_set * set)
 #  define sendto               rpl_sendto
 #  undef setsockopt
 #  define setsockopt           rpl_setsockopt
-
 #  undef select
-#  define select               select_not_supported_under_win32_use_poll
+#  define select               select_used_without_including_sys_select_h
 
 extern int rpl_close(int);
 extern int rpl_socket (int, int, int protocol);
diff --git a/lib/winsock-select.c b/lib/winsock-select.c
new file mode 100644
index 0000000..ceceb1b
--- /dev/null
+++ b/lib/winsock-select.c
@@ -0,0 +1,407 @@
+/* Emulation for select(2)
+   Contributed by Paolo Bonzini.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This file is part of gnulib.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <config.h>
+#include <alloca.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+#include <sys/types.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <winsock2.h>
+#include <windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <conio.h>
+#include <time.h>
+
+struct bitset {
+  unsigned char in[FD_SETSIZE / CHAR_BIT];
+  unsigned char out[FD_SETSIZE / CHAR_BIT];
+};
+
+/* Declare data structures for ntdll functions.  */
+typedef struct _FILE_PIPE_LOCAL_INFORMATION {
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
+typedef struct _IO_STATUS_BLOCK
+{
+  union {
+    DWORD Status;
+    PVOID Pointer;
+  } u;
+  ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+  FilePipeLocalInformation = 24
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef DWORD (WINAPI *PNtQueryInformationFile)
+        (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
+
+#ifndef PIPE_BUF
+#define PIPE_BUF       512
+#endif
+
+/* Compute revents values for file handle H.  */
+
+static int
+win32_poll_handle (HANDLE h, int fd, struct bitset *rbits, struct bitset 
*wbits,
+                  struct bitset *xbits)
+{
+  BOOL read, write, except;
+  int i, ret;
+  INPUT_RECORD *irbuffer;
+  DWORD avail, nbuffer;
+  BOOL bRet;
+  IO_STATUS_BLOCK iosb;
+  FILE_PIPE_LOCAL_INFORMATION fpli;
+  static PNtQueryInformationFile NtQueryInformationFile;
+  static BOOL once_only;
+
+  read = write = except = FALSE;
+  switch (GetFileType (h))
+    {
+    case FILE_TYPE_PIPE:
+      if (!once_only)
+       {
+         NtQueryInformationFile = (PNtQueryInformationFile)
+           GetProcAddress (GetModuleHandle ("ntdll.dll"),
+                           "NtQueryInformationFile");
+         once_only = TRUE;
+       }
+
+      if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
+       {
+         if (avail)
+           read = TRUE;
+       }
+
+      else
+       {
+         /* It was the write-end of the pipe.  Check if it is writable.
+            If NtQueryInformationFile fails, optimistically assume the pipe is
+            writable.  This could happen on Win9x, where NtQueryInformationFile
+            is not available, or if we inherit a pipe that doesn't permit
+            FILE_READ_ATTRIBUTES access on the write end (I think this should
+            not happen since WinXP SP2; WINE seems fine too).  Otherwise,
+            ensure that enough space is available for atomic writes.  */
+          memset (&iosb, 0, sizeof (iosb));
+          memset (&fpli, 0, sizeof (fpli));
+
+          if (!NtQueryInformationFile
+              || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
+                                        FilePipeLocalInformation)
+             || fpli.WriteQuotaAvailable >= PIPE_BUF
+             || (fpli.OutboundQuota < PIPE_BUF &&
+                 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
+           write = TRUE;
+       }
+      break;
+
+    case FILE_TYPE_CHAR:
+      ret = WaitForSingleObject (h, 0);
+      write = TRUE;
+      if (ret == WAIT_OBJECT_0)
+        {
+         nbuffer = avail = 0;
+         bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
+         if (!bRet || nbuffer == 0)
+           except = TRUE;
+
+         irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
+         bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
+         if (!bRet || avail == 0)
+           except = TRUE;
+
+         for (i = 0; i < avail; i++)
+           if (irbuffer[i].EventType == KEY_EVENT)
+             read = TRUE;
+       }
+      break;
+
+    default:
+      ret = WaitForSingleObject (h, 0);
+      write = TRUE;
+      if (ret == WAIT_OBJECT_0)
+        read = TRUE;
+
+      break;
+    }
+
+  ret = 0;
+  if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
+    {
+      rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
+      ret++;
+    }
+
+  if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
+    {
+      wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
+      ret++;
+    }
+
+  if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
+    {
+      xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
+      ret++;
+    }
+
+  return ret;
+}
+
+int
+rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
+            struct timeval *timeout)
+{
+  static struct timeval tv0;
+  static HANDLE hEvent;
+  HANDLE h, handle_array[FD_SETSIZE + 2];
+  fd_set handle_rfds, handle_wfds, handle_xfds;
+  struct bitset rbits, wbits, xbits;
+  unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT];
+  DWORD ret, wait_timeout, nhandles;
+  MSG msg;
+  int i, fd, rc;
+
+  if (nfds > FD_SETSIZE)
+    nfds = FD_SETSIZE;
+
+  if (!timeout)
+    wait_timeout = INFINITE;
+  else
+    wait_timeout = timeout->tv_sec + timeout->tv_usec / 1000;
+
+  if (!hEvent)
+    hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+  handle_array[0] = hEvent;
+  nhandles = 1;
+
+  /* Copy descriptors to bitsets.  */
+  memset (&rbits, 0, sizeof (rbits));
+  memset (&wbits, 0, sizeof (wbits));
+  memset (&xbits, 0, sizeof (xbits));
+  memset (anyfds_in, 0, sizeof (anyfds_in));
+  if (rfds)
+    for (i = 0; i < rfds->fd_count; i++)
+      {
+        fd = rfds->fd_array[i];
+        rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
+        anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
+      }
+  else
+    rfds = (fd_set *) alloca (sizeof (fd_set));
+
+  if (wfds)
+    for (i = 0; i < wfds->fd_count; i++)
+      {
+        fd = wfds->fd_array[i];
+        wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
+        anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
+      }
+  else
+    wfds = (fd_set *) alloca (sizeof (fd_set));
+
+  if (xfds)
+    for (i = 0; i < xfds->fd_count; i++)
+      {
+        fd = xfds->fd_array[i];
+        xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
+        anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
+      }
+  else
+    xfds = (fd_set *) alloca (sizeof (fd_set));
+
+  /* Zero all the fd_sets, including the application's.  */
+  FD_ZERO (rfds);
+  FD_ZERO (wfds);
+  FD_ZERO (xfds);
+  FD_ZERO (&handle_rfds);
+  FD_ZERO (&handle_wfds);
+  FD_ZERO (&handle_xfds);
+
+  /* Classify handles.  Create fd sets for sockets, poll the others. */
+  for (i = 0; i < nfds; i++)
+    {
+      WSANETWORKEVENTS ev;
+
+      if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
+       continue;
+
+      h = (HANDLE) _get_osfhandle (i);
+      if (!h)
+        {
+         errno = EBADF;
+         return -1;
+        }
+
+      /* Under Wine, it seems that getsockopt returns 0 for pipes too.
+        WSAEnumNetworkEvents instead distinguishes the two correctly.  */
+      ev.lNetworkEvents = 0xDEADBEEF;
+      WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
+      if (ev.lNetworkEvents != 0xDEADBEEF)
+        {
+          int requested = FD_CLOSE;
+
+          /* See above; socket handles are mapped onto select, but we
+            need to map descriptors to handles.  */
+          if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
+           {
+              requested |= FD_READ | FD_ACCEPT;
+             FD_SET ((SOCKET) h, rfds);
+             FD_SET ((SOCKET) h, &handle_rfds);
+           }
+          if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
+           {
+              requested |= FD_WRITE | FD_CONNECT;
+             FD_SET ((SOCKET) h, wfds);
+             FD_SET ((SOCKET) h, &handle_wfds);
+           }
+          if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
+           {
+              requested |= FD_OOB;
+             FD_SET ((SOCKET) h, xfds);
+             FD_SET ((SOCKET) h, &handle_xfds);
+           }
+
+          WSAEventSelect ((SOCKET) h, hEvent, requested);
+        }
+      else
+        {
+          handle_array[nhandles++] = h;
+
+         /* Poll now.  If we get an event, do not wait below.  */
+          if (wait_timeout != 0
+             && win32_poll_handle (h, i, &rbits, &wbits, &xbits))
+           wait_timeout = 0;
+        }
+    }
+
+  if (wait_timeout == 0)
+    rc = 0;
+  else
+    {
+      /* See if we need to wait in the loop below.  If any select is ready,
+         do MsgWaitForMultipleObjects anyway to dispatch messages, but
+         no need to call select again.  */
+      rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
+      if (rc == 0)
+       {
+         /* Restore the fd_sets for the other select we do below.  */
+          memcpy (&handle_rfds, rfds, sizeof (fd_set));
+          memcpy (&handle_wfds, wfds, sizeof (fd_set));
+          memcpy (&handle_xfds, xfds, sizeof (fd_set));
+       }
+      else
+        wait_timeout = 0;
+    }
+
+  for (;;)
+    {
+      ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
+                                      wait_timeout, QS_ALLINPUT);
+
+      if (ret == WAIT_OBJECT_0 + nhandles)
+       {
+          /* new input of some other kind */
+         BOOL bRet;
+          while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
+            {
+              TranslateMessage (&msg);
+              DispatchMessage (&msg);
+            }
+       }
+      else
+       break;
+    }
+
+  /* If we haven't done it yet, check the status of the sockets.  */
+  if (rc == 0)
+    rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
+
+  /* Now fill in the results.  */
+  FD_ZERO (rfds);
+  FD_ZERO (wfds);
+  FD_ZERO (xfds);
+
+  /* Place a sentinel at the end of the array.  */
+  handle_array[nhandles] = NULL;
+  nhandles = 1;
+  for (i = 0; i < nfds; i++)
+    {
+      if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
+       continue;
+
+      h = (HANDLE) _get_osfhandle (i);
+      if (h != handle_array[nhandles])
+       {
+         /* Perform handle->descriptor mapping.  Don't update rc, as these
+            results are counted in the return value of Winsock's select.  */
+          WSAEventSelect ((SOCKET) h, NULL, 0);
+          if (FD_ISSET (h, &handle_rfds))
+            FD_SET (i, rfds);
+          if (FD_ISSET (h, &handle_wfds))
+            FD_SET (i, wfds);
+          if (FD_ISSET (h, &handle_xfds))
+            FD_SET (i, xfds);
+       }
+      else
+        {
+          /* Not a socket.  */
+          nhandles++;
+          win32_poll_handle (h, i, &rbits, &wbits, &xbits);
+          if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
+            {
+             rc++;
+             FD_SET (i, rfds);
+           }
+          if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
+            {
+             rc++;
+             FD_SET (i, wfds);
+           }
+          if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
+            {
+             rc++;
+             FD_SET (i, xfds);
+           }
+        }
+    }
+
+  return rc;
+}
+
+#endif /* Native Win32.  */
diff --git a/m4/sys_select_h.m4 b/m4/sys_select_h.m4
index 8d1c36c..c9ad02a 100644
--- a/m4/sys_select_h.m4
+++ b/m4/sys_select_h.m4
@@ -6,6 +6,7 @@ dnl with or without modifications, as long as this notice is 
preserved.
 
 AC_DEFUN([gl_HEADER_SYS_SELECT],
 [
+  AC_REQUIRE([gl_HEADER_SYS_SOCKET])
   AC_CACHE_CHECK([whether <sys/select.h> is self-contained],
     [gl_cv_header_sys_select_h_selfcontained],
     [
@@ -26,4 +27,7 @@ AC_DEFUN([gl_HEADER_SYS_SELECT],
     AC_SUBST([HAVE_SYS_SELECT_H])
   fi
   AC_SUBST([SYS_SELECT_H])
+  if test x$ac_cv_header_winsock2_h = xyes; then
+    AC_LIBOBJ(winsock-select)
+  fi
 ])
diff --git a/modules/sys_select b/modules/sys_select
index 74e43b2..f2f7698 100644
--- a/modules/sys_select
+++ b/modules/sys_select
@@ -3,9 +3,11 @@ A <sys/select.h> for systems lacking it.
 
 Files:
 lib/sys_select.in.h
+lib/winsock-select.c
 m4/sys_select_h.m4
 
 Depends-on:
+alloca
 include_next
 sys_socket
 
@@ -26,6 +28,7 @@ sys/select.h: sys_select.in.h
              -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
              -e 's|@''NEXT_SYS_SELECT_H''@|$(NEXT_SYS_SELECT_H)|g' \
              -e 's|@''HAVE_SYS_SELECT_H''@|$(HAVE_SYS_SELECT_H)|g' \
+             -e 's|@''HAVE_WINSOCK2_H''@|$(HAVE_WINSOCK2_H)|g' \
              < $(srcdir)/sys_select.in.h; \
        } > address@hidden
        mv address@hidden $@
-- 
1.5.6.5





reply via email to

[Prev in Thread] Current Thread [Next in Thread]