Blame lib/poll.c

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