Blame lib/poll.c

Packit 8f70b4
/* Emulation for poll(2)
Packit 8f70b4
   Contributed by Paolo Bonzini.
Packit 8f70b4
Packit 8f70b4
   Copyright 2001-2003, 2006-2018 Free Software Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   This file is part of gnulib.
Packit 8f70b4
Packit 8f70b4
   This program is free software; you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3, or (at your option)
Packit 8f70b4
   any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License along
Packit 8f70b4
   with this program; if not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
/* Tell gcc not to warn about the (nfd < 0) tests, below.  */
Packit 8f70b4
#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
Packit 8f70b4
# pragma GCC diagnostic ignored "-Wtype-limits"
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <alloca.h>
Packit 8f70b4
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
Packit 8f70b4
/* Specification.  */
Packit 8f70b4
#include <poll.h>
Packit 8f70b4
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <limits.h>
Packit 8f70b4
Packit 8f70b4
#if defined _WIN32 && ! defined __CYGWIN__
Packit 8f70b4
# define WINDOWS_NATIVE
Packit 8f70b4
# include <winsock2.h>
Packit 8f70b4
# include <windows.h>
Packit 8f70b4
# include <io.h>
Packit 8f70b4
# include <stdio.h>
Packit 8f70b4
# include <conio.h>
Packit 8f70b4
# if GNULIB_MSVC_NOTHROW
Packit 8f70b4
#  include "msvc-nothrow.h"
Packit 8f70b4
# else
Packit 8f70b4
#  include <io.h>
Packit 8f70b4
# endif
Packit 8f70b4
#else
Packit 8f70b4
# include <sys/time.h>
Packit 8f70b4
# include <unistd.h>
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <sys/select.h>
Packit 8f70b4
#include <sys/socket.h>
Packit 8f70b4
Packit 8f70b4
#ifdef HAVE_SYS_IOCTL_H
Packit 8f70b4
# include <sys/ioctl.h>
Packit 8f70b4
#endif
Packit 8f70b4
#ifdef HAVE_SYS_FILIO_H
Packit 8f70b4
# include <sys/filio.h>
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <time.h>
Packit 8f70b4
Packit 8f70b4
#include "assure.h"
Packit 8f70b4
Packit 8f70b4
#ifndef INFTIM
Packit 8f70b4
# define INFTIM (-1)
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* BeOS does not have MSG_PEEK.  */
Packit 8f70b4
#ifndef MSG_PEEK
Packit 8f70b4
# define MSG_PEEK 0
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#ifdef WINDOWS_NATIVE
Packit 8f70b4
Packit 8f70b4
/* Here we need the recv() function from Windows, that takes a SOCKET as
Packit 8f70b4
   first argument, not any possible gnulib override.  */
Packit 8f70b4
# undef recv
Packit 8f70b4
Packit 8f70b4
/* Here we need the select() function from Windows, because we pass bit masks
Packit 8f70b4
   of SOCKETs, not bit masks of FDs.  */
Packit 8f70b4
# undef select
Packit 8f70b4
Packit 8f70b4
static BOOL IsConsoleHandle (HANDLE h)
Packit 8f70b4
{
Packit 8f70b4
  DWORD mode;
Packit 8f70b4
  return GetConsoleMode (h, &mode) != 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static BOOL
Packit 8f70b4
IsSocketHandle (HANDLE h)
Packit 8f70b4
{
Packit 8f70b4
  WSANETWORKEVENTS ev;
Packit 8f70b4
Packit 8f70b4
  if (IsConsoleHandle (h))
Packit 8f70b4
    return FALSE;
Packit 8f70b4
Packit 8f70b4
  /* Under Wine, it seems that getsockopt returns 0 for pipes too.
Packit 8f70b4
     WSAEnumNetworkEvents instead distinguishes the two correctly.  */
Packit 8f70b4
  ev.lNetworkEvents = 0xDEADBEEF;
Packit 8f70b4
  WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev;;
Packit 8f70b4
  return ev.lNetworkEvents != 0xDEADBEEF;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Declare data structures for ntdll functions.  */
Packit 8f70b4
typedef struct _FILE_PIPE_LOCAL_INFORMATION {
Packit 8f70b4
  ULONG NamedPipeType;
Packit 8f70b4
  ULONG NamedPipeConfiguration;
Packit 8f70b4
  ULONG MaximumInstances;
Packit 8f70b4
  ULONG CurrentInstances;
Packit 8f70b4
  ULONG InboundQuota;
Packit 8f70b4
  ULONG ReadDataAvailable;
Packit 8f70b4
  ULONG OutboundQuota;
Packit 8f70b4
  ULONG WriteQuotaAvailable;
Packit 8f70b4
  ULONG NamedPipeState;
Packit 8f70b4
  ULONG NamedPipeEnd;
Packit 8f70b4
} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
Packit 8f70b4
Packit 8f70b4
typedef struct _IO_STATUS_BLOCK
Packit 8f70b4
{
Packit 8f70b4
  union {
Packit 8f70b4
    DWORD Status;
Packit 8f70b4
    PVOID Pointer;
Packit 8f70b4
  } u;
Packit 8f70b4
  ULONG_PTR Information;
Packit 8f70b4
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
Packit 8f70b4
Packit 8f70b4
typedef enum _FILE_INFORMATION_CLASS {
Packit 8f70b4
  FilePipeLocalInformation = 24
Packit 8f70b4
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
Packit 8f70b4
Packit 8f70b4
typedef DWORD (WINAPI *PNtQueryInformationFile)
Packit 8f70b4
         (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
Packit 8f70b4
Packit 8f70b4
# ifndef PIPE_BUF
Packit 8f70b4
#  define PIPE_BUF      512
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
/* Compute revents values for file handle H.  If some events cannot happen
Packit 8f70b4
   for the handle, eliminate them from *P_SOUGHT.  */
Packit 8f70b4
Packit 8f70b4
static int
Packit 8f70b4
windows_compute_revents (HANDLE h, int *p_sought)
Packit 8f70b4
{
Packit 8f70b4
  int i, ret, happened;
Packit 8f70b4
  INPUT_RECORD *irbuffer;
Packit 8f70b4
  DWORD avail, nbuffer;
Packit 8f70b4
  BOOL bRet;
Packit 8f70b4
  IO_STATUS_BLOCK iosb;
Packit 8f70b4
  FILE_PIPE_LOCAL_INFORMATION fpli;
Packit 8f70b4
  static PNtQueryInformationFile NtQueryInformationFile;
Packit 8f70b4
  static BOOL once_only;
Packit 8f70b4
Packit 8f70b4
  switch (GetFileType (h))
Packit 8f70b4
    {
Packit 8f70b4
    case FILE_TYPE_PIPE:
Packit 8f70b4
      if (!once_only)
Packit 8f70b4
        {
Packit 8f70b4
          NtQueryInformationFile = (PNtQueryInformationFile)
Packit 8f70b4
            GetProcAddress (GetModuleHandle ("ntdll.dll"),
Packit 8f70b4
                            "NtQueryInformationFile");
Packit 8f70b4
          once_only = TRUE;
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
      happened = 0;
Packit 8f70b4
      if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
Packit 8f70b4
        {
Packit 8f70b4
          if (avail)
Packit 8f70b4
            happened |= *p_sought & (POLLIN | POLLRDNORM);
Packit 8f70b4
        }
Packit 8f70b4
      else if (GetLastError () == ERROR_BROKEN_PIPE)
Packit 8f70b4
        happened |= POLLHUP;
Packit 8f70b4
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* It was the write-end of the pipe.  Check if it is writable.
Packit 8f70b4
             If NtQueryInformationFile fails, optimistically assume the pipe is
Packit 8f70b4
             writable.  This could happen on Windows 9x, where
Packit 8f70b4
             NtQueryInformationFile is not available, or if we inherit a pipe
Packit 8f70b4
             that doesn't permit FILE_READ_ATTRIBUTES access on the write end
Packit 8f70b4
             (I think this should not happen since Windows XP SP2; WINE seems
Packit 8f70b4
             fine too).  Otherwise, ensure that enough space is available for
Packit 8f70b4
             atomic writes.  */
Packit 8f70b4
          memset (&iosb, 0, sizeof (iosb));
Packit 8f70b4
          memset (&fpli, 0, sizeof (fpli));
Packit 8f70b4
Packit 8f70b4
          if (!NtQueryInformationFile
Packit 8f70b4
              || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
Packit 8f70b4
                                         FilePipeLocalInformation)
Packit 8f70b4
              || fpli.WriteQuotaAvailable >= PIPE_BUF
Packit 8f70b4
              || (fpli.OutboundQuota < PIPE_BUF &&
Packit 8f70b4
                  fpli.WriteQuotaAvailable == fpli.OutboundQuota))
Packit 8f70b4
            happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
Packit 8f70b4
        }
Packit 8f70b4
      return happened;
Packit 8f70b4
Packit 8f70b4
    case FILE_TYPE_CHAR:
Packit 8f70b4
      ret = WaitForSingleObject (h, 0);
Packit 8f70b4
      if (!IsConsoleHandle (h))
Packit 8f70b4
        return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0;
Packit 8f70b4
Packit 8f70b4
      nbuffer = avail = 0;
Packit 8f70b4
      bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
Packit 8f70b4
      if (bRet)
Packit 8f70b4
        {
Packit 8f70b4
          /* Input buffer.  */
Packit 8f70b4
          *p_sought &= POLLIN | POLLRDNORM;
Packit 8f70b4
          if (nbuffer == 0)
Packit 8f70b4
            return POLLHUP;
Packit 8f70b4
          if (!*p_sought)
Packit 8f70b4
            return 0;
Packit 8f70b4
Packit 8f70b4
          irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
Packit 8f70b4
          bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
Packit 8f70b4
          if (!bRet || avail == 0)
Packit 8f70b4
            return POLLHUP;
Packit 8f70b4
Packit 8f70b4
          for (i = 0; i < avail; i++)
Packit 8f70b4
            if (irbuffer[i].EventType == KEY_EVENT)
Packit 8f70b4
              return *p_sought;
Packit 8f70b4
          return 0;
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Screen buffer.  */
Packit 8f70b4
          *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
Packit 8f70b4
          return *p_sought;
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
    default:
Packit 8f70b4
      ret = WaitForSingleObject (h, 0);
Packit 8f70b4
      if (ret == WAIT_OBJECT_0)
Packit 8f70b4
        return *p_sought & ~(POLLPRI | POLLRDBAND);
Packit 8f70b4
Packit 8f70b4
      return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
Packit 8f70b4
    }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Convert fd_sets returned by select into revents values.  */
Packit 8f70b4
Packit 8f70b4
static int
Packit 8f70b4
windows_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
Packit 8f70b4
{
Packit 8f70b4
  int happened = 0;
Packit 8f70b4
Packit 8f70b4
  if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
Packit 8f70b4
    happened |= (POLLIN | POLLRDNORM) & sought;
Packit 8f70b4
Packit 8f70b4
  else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
Packit 8f70b4
    {
Packit 8f70b4
      int r, error;
Packit 8f70b4
Packit 8f70b4
      char data[64];
Packit 8f70b4
      WSASetLastError (0);
Packit 8f70b4
      r = recv (h, data, sizeof (data), MSG_PEEK);
Packit 8f70b4
      error = WSAGetLastError ();
Packit 8f70b4
      WSASetLastError (0);
Packit 8f70b4
Packit 8f70b4
      if (r > 0 || error == WSAENOTCONN)
Packit 8f70b4
        happened |= (POLLIN | POLLRDNORM) & sought;
Packit 8f70b4
Packit 8f70b4
      /* Distinguish hung-up sockets from other errors.  */
Packit 8f70b4
      else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
Packit 8f70b4
               || error == WSAECONNABORTED || error == WSAENETRESET)
Packit 8f70b4
        happened |= POLLHUP;
Packit 8f70b4
Packit 8f70b4
      else
Packit 8f70b4
        happened |= POLLERR;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
Packit 8f70b4
    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
Packit 8f70b4
Packit 8f70b4
  if (lNetworkEvents & FD_OOB)
Packit 8f70b4
    happened |= (POLLPRI | POLLRDBAND) & sought;
Packit 8f70b4
Packit 8f70b4
  return happened;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#else /* !MinGW */
Packit 8f70b4
Packit 8f70b4
/* Convert select(2) returned fd_sets into poll(2) revents values.  */
Packit 8f70b4
static int
Packit 8f70b4
compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
Packit 8f70b4
{
Packit 8f70b4
  int happened = 0;
Packit 8f70b4
  if (FD_ISSET (fd, rfds))
Packit 8f70b4
    {
Packit 8f70b4
      int r;
Packit 8f70b4
      int socket_errno;
Packit 8f70b4
Packit 8f70b4
# if defined __MACH__ && defined __APPLE__
Packit 8f70b4
      /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
Packit 8f70b4
         for some kinds of descriptors.  Detect if this descriptor is a
Packit 8f70b4
         connected socket, a server socket, or something else using a
Packit 8f70b4
         0-byte recv, and use ioctl(2) to detect POLLHUP.  */
Packit 8f70b4
      r = recv (fd, NULL, 0, MSG_PEEK);
Packit 8f70b4
      socket_errno = (r < 0) ? errno : 0;
Packit 8f70b4
      if (r == 0 || socket_errno == ENOTSOCK)
Packit 8f70b4
        ioctl (fd, FIONREAD, &r);
Packit 8f70b4
# else
Packit 8f70b4
      char data[64];
Packit 8f70b4
      r = recv (fd, data, sizeof (data), MSG_PEEK);
Packit 8f70b4
      socket_errno = (r < 0) ? errno : 0;
Packit 8f70b4
# endif
Packit 8f70b4
      if (r == 0)
Packit 8f70b4
        happened |= POLLHUP;
Packit 8f70b4
Packit 8f70b4
      /* If the event happened on an unconnected server socket,
Packit 8f70b4
         that's fine. */
Packit 8f70b4
      else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
Packit 8f70b4
        happened |= (POLLIN | POLLRDNORM) & sought;
Packit 8f70b4
Packit 8f70b4
      /* Distinguish hung-up sockets from other errors.  */
Packit 8f70b4
      else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
Packit 8f70b4
               || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
Packit 8f70b4
        happened |= POLLHUP;
Packit 8f70b4
Packit 8f70b4
      /* some systems can't use recv() on non-socket, including HP NonStop */
Packit 8f70b4
      else if (socket_errno == ENOTSOCK)
Packit 8f70b4
        happened |= (POLLIN | POLLRDNORM) & sought;
Packit 8f70b4
Packit 8f70b4
      else
Packit 8f70b4
        happened |= POLLERR;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (FD_ISSET (fd, wfds))
Packit 8f70b4
    happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
Packit 8f70b4
Packit 8f70b4
  if (FD_ISSET (fd, efds))
Packit 8f70b4
    happened |= (POLLPRI | POLLRDBAND) & sought;
Packit 8f70b4
Packit 8f70b4
  return happened;
Packit 8f70b4
}
Packit 8f70b4
#endif /* !MinGW */
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
poll (struct pollfd *pfd, nfds_t nfd, int timeout)
Packit 8f70b4
{
Packit 8f70b4
#ifndef WINDOWS_NATIVE
Packit 8f70b4
  fd_set rfds, wfds, efds;
Packit 8f70b4
  struct timeval tv;
Packit 8f70b4
  struct timeval *ptv;
Packit 8f70b4
  int maxfd, rc;
Packit 8f70b4
  nfds_t i;
Packit 8f70b4
Packit 8f70b4
  if (nfd > INT_MAX)
Packit 8f70b4
    {
Packit 8f70b4
      errno = EINVAL;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
  /* Don't check directly for NFD greater than OPEN_MAX.  Any practical use
Packit 8f70b4
     of a too-large NFD is caught by one of the other checks below, and
Packit 8f70b4
     checking directly for getdtablesize is too much of a portability
Packit 8f70b4
     and/or performance and/or correctness hassle.  */
Packit 8f70b4
Packit 8f70b4
  /* EFAULT is not necessary to implement, but let's do it in the
Packit 8f70b4
     simplest case. */
Packit 8f70b4
  if (!pfd && nfd)
Packit 8f70b4
    {
Packit 8f70b4
      errno = EFAULT;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* convert timeout number into a timeval structure */
Packit 8f70b4
  if (timeout == 0)
Packit 8f70b4
    {
Packit 8f70b4
      ptv = &tv;
Packit 8f70b4
      ptv->tv_sec = 0;
Packit 8f70b4
      ptv->tv_usec = 0;
Packit 8f70b4
    }
Packit 8f70b4
  else if (timeout > 0)
Packit 8f70b4
    {
Packit 8f70b4
      ptv = &tv;
Packit 8f70b4
      ptv->tv_sec = timeout / 1000;
Packit 8f70b4
      ptv->tv_usec = (timeout % 1000) * 1000;
Packit 8f70b4
    }
Packit 8f70b4
  else if (timeout == INFTIM)
Packit 8f70b4
    /* wait forever */
Packit 8f70b4
    ptv = NULL;
Packit 8f70b4
  else
Packit 8f70b4
    {
Packit 8f70b4
      errno = EINVAL;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* create fd sets and determine max fd */
Packit 8f70b4
  maxfd = -1;
Packit 8f70b4
  FD_ZERO (&rfds);
Packit 8f70b4
  FD_ZERO (&wfds);
Packit 8f70b4
  FD_ZERO (&efds);
Packit 8f70b4
  for (i = 0; i < nfd; i++)
Packit 8f70b4
    {
Packit 8f70b4
      if (pfd[i].fd < 0)
Packit 8f70b4
        continue;
Packit 8f70b4
      if (maxfd < pfd[i].fd)
Packit 8f70b4
        {
Packit 8f70b4
          maxfd = pfd[i].fd;
Packit 8f70b4
          if (FD_SETSIZE <= maxfd)
Packit 8f70b4
            {
Packit 8f70b4
              errno = EINVAL;
Packit 8f70b4
              return -1;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      if (pfd[i].events & (POLLIN | POLLRDNORM))
Packit 8f70b4
        FD_SET (pfd[i].fd, &rfds);
Packit 8f70b4
      /* see select(2): "the only exceptional condition detectable
Packit 8f70b4
         is out-of-band data received on a socket", hence we push
Packit 8f70b4
         POLLWRBAND events onto wfds instead of efds. */
Packit 8f70b4
      if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
Packit 8f70b4
        FD_SET (pfd[i].fd, &wfds);
Packit 8f70b4
      if (pfd[i].events & (POLLPRI | POLLRDBAND))
Packit 8f70b4
        FD_SET (pfd[i].fd, &efds);
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* examine fd sets */
Packit 8f70b4
  rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
Packit 8f70b4
  if (rc < 0)
Packit 8f70b4
    return rc;
Packit 8f70b4
Packit 8f70b4
  /* establish results */
Packit 8f70b4
  rc = 0;
Packit 8f70b4
  for (i = 0; i < nfd; i++)
Packit 8f70b4
    {
Packit 8f70b4
      pfd[i].revents = (pfd[i].fd < 0
Packit 8f70b4
                        ? 0
Packit 8f70b4
                        : compute_revents (pfd[i].fd, pfd[i].events,
Packit 8f70b4
                                           &rfds, &wfds, &efds));
Packit 8f70b4
      rc += pfd[i].revents != 0;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  return rc;
Packit 8f70b4
#else
Packit 8f70b4
  static struct timeval tv0;
Packit 8f70b4
  static HANDLE hEvent;
Packit 8f70b4
  WSANETWORKEVENTS ev;
Packit 8f70b4
  HANDLE h, handle_array[FD_SETSIZE + 2];
Packit 8f70b4
  DWORD ret, wait_timeout, nhandles;
Packit 8f70b4
  fd_set rfds, wfds, xfds;
Packit 8f70b4
  BOOL poll_again;
Packit 8f70b4
  MSG msg;
Packit 8f70b4
  int rc = 0;
Packit 8f70b4
  nfds_t i;
Packit 8f70b4
Packit 8f70b4
  if (nfd > INT_MAX || timeout < -1)
Packit 8f70b4
    {
Packit 8f70b4
      errno = EINVAL;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (!hEvent)
Packit 8f70b4
    hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
Packit 8f70b4
Packit 8f70b4
restart:
Packit 8f70b4
  handle_array[0] = hEvent;
Packit 8f70b4
  nhandles = 1;
Packit 8f70b4
  FD_ZERO (&rfds);
Packit 8f70b4
  FD_ZERO (&wfds);
Packit 8f70b4
  FD_ZERO (&xfds);
Packit 8f70b4
Packit 8f70b4
  /* Classify socket handles and create fd sets. */
Packit 8f70b4
  for (i = 0; i < nfd; i++)
Packit 8f70b4
    {
Packit 8f70b4
      int sought = pfd[i].events;
Packit 8f70b4
      pfd[i].revents = 0;
Packit 8f70b4
      if (pfd[i].fd < 0)
Packit 8f70b4
        continue;
Packit 8f70b4
      if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
Packit 8f70b4
                      | POLLPRI | POLLRDBAND)))
Packit 8f70b4
        continue;
Packit 8f70b4
Packit 8f70b4
      h = (HANDLE) _get_osfhandle (pfd[i].fd);
Packit 8f70b4
      assure (h != NULL);
Packit 8f70b4
      if (IsSocketHandle (h))
Packit 8f70b4
        {
Packit 8f70b4
          int requested = FD_CLOSE;
Packit 8f70b4
Packit 8f70b4
          /* see above; socket handles are mapped onto select.  */
Packit 8f70b4
          if (sought & (POLLIN | POLLRDNORM))
Packit 8f70b4
            {
Packit 8f70b4
              requested |= FD_READ | FD_ACCEPT;
Packit 8f70b4
              FD_SET ((SOCKET) h, &rfds);
Packit 8f70b4
            }
Packit 8f70b4
          if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
Packit 8f70b4
            {
Packit 8f70b4
              requested |= FD_WRITE | FD_CONNECT;
Packit 8f70b4
              FD_SET ((SOCKET) h, &wfds);
Packit 8f70b4
            }
Packit 8f70b4
          if (sought & (POLLPRI | POLLRDBAND))
Packit 8f70b4
            {
Packit 8f70b4
              requested |= FD_OOB;
Packit 8f70b4
              FD_SET ((SOCKET) h, &xfds);
Packit 8f70b4
            }
Packit 8f70b4
Packit 8f70b4
          if (requested)
Packit 8f70b4
            WSAEventSelect ((SOCKET) h, hEvent, requested);
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Poll now.  If we get an event, do not poll again.  Also,
Packit 8f70b4
             screen buffer handles are waitable, and they'll block until
Packit 8f70b4
             a character is available.  windows_compute_revents eliminates
Packit 8f70b4
             bits for the "wrong" direction. */
Packit 8f70b4
          pfd[i].revents = windows_compute_revents (h, &sought);
Packit 8f70b4
          if (sought)
Packit 8f70b4
            handle_array[nhandles++] = h;
Packit 8f70b4
          if (pfd[i].revents)
Packit 8f70b4
            timeout = 0;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (select (0, &rfds, &wfds, &xfds, &tv0) > 0)
Packit 8f70b4
    {
Packit 8f70b4
      /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
Packit 8f70b4
         no need to call select again.  */
Packit 8f70b4
      poll_again = FALSE;
Packit 8f70b4
      wait_timeout = 0;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    {
Packit 8f70b4
      poll_again = TRUE;
Packit 8f70b4
      if (timeout == INFTIM)
Packit 8f70b4
        wait_timeout = INFINITE;
Packit 8f70b4
      else
Packit 8f70b4
        wait_timeout = timeout;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  for (;;)
Packit 8f70b4
    {
Packit 8f70b4
      ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
Packit 8f70b4
                                       wait_timeout, QS_ALLINPUT);
Packit 8f70b4
Packit 8f70b4
      if (ret == WAIT_OBJECT_0 + nhandles)
Packit 8f70b4
        {
Packit 8f70b4
          /* new input of some other kind */
Packit 8f70b4
          BOOL bRet;
Packit 8f70b4
          while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
Packit 8f70b4
            {
Packit 8f70b4
              TranslateMessage (&msg;;
Packit 8f70b4
              DispatchMessage (&msg;;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        break;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (poll_again)
Packit 8f70b4
    select (0, &rfds, &wfds, &xfds, &tv0);
Packit 8f70b4
Packit 8f70b4
  /* Place a sentinel at the end of the array.  */
Packit 8f70b4
  handle_array[nhandles] = NULL;
Packit 8f70b4
  nhandles = 1;
Packit 8f70b4
  for (i = 0; i < nfd; i++)
Packit 8f70b4
    {
Packit 8f70b4
      int happened;
Packit 8f70b4
Packit 8f70b4
      if (pfd[i].fd < 0)
Packit 8f70b4
        continue;
Packit 8f70b4
      if (!(pfd[i].events & (POLLIN | POLLRDNORM |
Packit 8f70b4
                             POLLOUT | POLLWRNORM | POLLWRBAND)))
Packit 8f70b4
        continue;
Packit 8f70b4
Packit 8f70b4
      h = (HANDLE) _get_osfhandle (pfd[i].fd);
Packit 8f70b4
      if (h != handle_array[nhandles])
Packit 8f70b4
        {
Packit 8f70b4
          /* It's a socket.  */
Packit 8f70b4
          WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev;;
Packit 8f70b4
          WSAEventSelect ((SOCKET) h, 0, 0);
Packit 8f70b4
Packit 8f70b4
          /* If we're lucky, WSAEnumNetworkEvents already provided a way
Packit 8f70b4
             to distinguish FD_READ and FD_ACCEPT; this saves a recv later.  */
Packit 8f70b4
          if (FD_ISSET ((SOCKET) h, &rfds)
Packit 8f70b4
              && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
Packit 8f70b4
            ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
Packit 8f70b4
          if (FD_ISSET ((SOCKET) h, &wfds))
Packit 8f70b4
            ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
Packit 8f70b4
          if (FD_ISSET ((SOCKET) h, &xfds))
Packit 8f70b4
            ev.lNetworkEvents |= FD_OOB;
Packit 8f70b4
Packit 8f70b4
          happened = windows_compute_revents_socket ((SOCKET) h, pfd[i].events,
Packit 8f70b4
                                                     ev.lNetworkEvents);
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Not a socket.  */
Packit 8f70b4
          int sought = pfd[i].events;
Packit 8f70b4
          happened = windows_compute_revents (h, &sought);
Packit 8f70b4
          nhandles++;
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
       if ((pfd[i].revents |= happened) != 0)
Packit 8f70b4
        rc++;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (!rc && timeout == INFTIM)
Packit 8f70b4
    {
Packit 8f70b4
      SleepEx (1, TRUE);
Packit 8f70b4
      goto restart;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  return rc;
Packit 8f70b4
#endif
Packit 8f70b4
}