1998-12-05 Roland McGrath <roland@baalperazim.frob.com>
authorRoland McGrath <roland@gnu.org>
Sun, 6 Dec 1998 00:44:12 +0000 (00:44 +0000)
committerRoland McGrath <roland@gnu.org>
Sun, 6 Dec 1998 00:44:12 +0000 (00:44 +0000)
* sysdeps/mach/hurd/pselect.c: New file.

* sysdeps/mach/hurd/poll.c: New file.

* hurd/Makefile (routines): Add hurdselect.
* hurd/hurdselect.c: New file.
(_hurd_select): New function, guts taken from ...
* sysdeps/mach/hurd/select.c (__select): ... here.
Now work by just calling _hurd_select.
* hurd/hurd/fd.h: Declare _hurd_select.

1998-12-05  Roland McGrath  <roland@baalperazim.frob.com>

* time/strptime.c: Fix unterminated comment in last change.

* argp/argp.h: Add __restrict.

ChangeLog
hurd/Makefile
hurd/hurd/fd.h
hurd/hurdselect.c [new file with mode: 0644]
sysdeps/mach/hurd/pselect.c [new file with mode: 0644]
sysdeps/mach/hurd/select.c

index 70039a8..6d84569 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+1998-12-05  Roland McGrath  <roland@baalperazim.frob.com>
+
+       * sysdeps/mach/hurd/pselect.c: New file.
+
+       * sysdeps/mach/hurd/poll.c: New file.
+
+       * hurd/Makefile (routines): Add hurdselect.
+       * hurd/hurdselect.c: New file.
+       (_hurd_select): New function, guts taken from ...
+       * sysdeps/mach/hurd/select.c (__select): ... here.
+       Now work by just calling _hurd_select.
+       * hurd/hurd/fd.h: Declare _hurd_select.
+
+1998-12-05  Roland McGrath  <roland@baalperazim.frob.com>
+
+       * time/strptime.c: Fix unterminated comment in last change.
+
 1998-12-05  Ulrich Drepper  <drepper@cygnus.com>
 
        * ctype/ctype.h (_ISbit): Protect use of parameter with
@@ -13,7 +30,7 @@
 
 1998-12-04  Ulrich Drepper  <drepper@cygnus.com>
 
-       * argp/argp.h: Add __retrict.
+       * argp/argp.h: Add __restrict.
        * dirent/dirent.h: Likewise.
        * elf/dlfcn.h: Likewise.
        * grp/grp.h: Likewise.
index 250e441..07e22eb 100644 (file)
@@ -41,7 +41,7 @@ user-interfaces               := $(addprefix hurd/,\
 server-interfaces      := hurd/msg faultexc
 
 routines = hurdstartup hurdinit \
-          hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec \
+          hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec hurdselect \
           get-host set-host \
           path-lookup \
           setauth \
index 10fd563..aa48f21 100644 (file)
@@ -1,5 +1,5 @@
 /* File descriptors.
-   Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1993,94,95,96,97,98 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -223,4 +223,18 @@ extern error_t _hurd_ctty_input (io_t port, io_t ctty, error_t (*rpc) (io_t));
 extern error_t _hurd_ctty_output (io_t port, io_t ctty, error_t (*rpc) (io_t));
 
 
+/* The guts of `select' and `poll'.  Check the first NFDS descriptors
+   either in POLLFDS (if nonnull) or in each of READFDS, WRITEFDS,
+   EXCEPTFDS that is nonnull.  If TIMEOUT is not NULL, time out after
+   waiting the interval specified therein.  If SIGMASK is nonnull,
+   the set of blocked signals is temporarily set to that during this call.
+   Returns the number of ready descriptors, or -1 for errors.  */
+struct pollfd;
+struct timespec;
+extern int _hurd_select (int nfds, struct pollfd *pollfds,
+                        fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+                        const struct timespec *timeout,
+                        const sigset_t *sigmask);
+
+
 #endif /* hurd/fd.h */
diff --git a/hurd/hurdselect.c b/hurd/hurdselect.c
new file mode 100644 (file)
index 0000000..e65def5
--- /dev/null
@@ -0,0 +1,444 @@
+/* Guts of both `select' and `poll' for Hurd.
+   Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <hurd.h>
+#include <hurd/fd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdint.h>
+
+/* All user select types.  */
+#define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
+
+/* Used to record that a particular select rpc returned.  Must be distinct
+   from SELECT_ALL (which better not have the high bit set).  */
+#define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
+
+/* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in
+   each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull.  If TIMEOUT is not
+   NULL, time out after waiting the interval specified therein.  Returns
+   the number of ready descriptors, or -1 for errors.  */
+int
+_hurd_select (int nfds,
+             struct pollfd *pollfds,
+             fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+             const struct timespec *timeout, const sigset_t *sigmask)
+{
+  int i;
+  mach_port_t portset;
+  int got;
+  error_t err;
+  fd_set rfds, wfds, xfds;
+  int firstfd, lastfd;
+  mach_msg_timeout_t to = (timeout != NULL ?
+                          (timeout->tv_sec * 1000 +
+                           timeout->tv_nsec / 1000000) :
+                          0);
+  struct
+    {
+      struct hurd_userlink ulink;
+      struct hurd_fd *cell;
+      mach_port_t io_port;
+      int type;
+      mach_port_t reply_port;
+    } d[nfds];
+  sigset_t oset;
+
+  if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset))
+    return -1;
+
+  if (pollfds)
+    {
+      /* Collect interesting descriptors from the user's `pollfd' array.
+        We do a first pass that reads the user's array before taking
+        any locks.  The second pass then only touches our own stack,
+        and gets the port references.  */
+
+      for (i = 0; i < nfds; ++i)
+       if (pollfds[i].fd >= 0)
+         {
+           int type = 0;
+           if (pollfds[i].events & POLLIN)
+             type |= SELECT_READ;
+           if (pollfds[i].events & POLLOUT)
+             type |= SELECT_WRITE;
+           if (pollfds[i].events & POLLPRI)
+             type |= SELECT_URG;
+
+           d[i].io_port = pollfds[i].fd;
+           d[i].type = type;
+         }
+       else
+         d[i].type = 0;
+
+      HURD_CRITICAL_BEGIN;
+      __mutex_lock (&_hurd_dtable_lock);
+
+      for (i = 0; i < nfds; ++i)
+       if (d[i].type != 0)
+         {
+           const int fd = (int) d[i].io_port;
+
+           if (fd < _hurd_dtablesize)
+             {
+               d[i].cell = _hurd_dtable[fd];
+               d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
+               if (d[i].io_port != MACH_PORT_NULL)
+                 continue;
+             }
+
+           /* If one descriptor is bogus, we fail completely.  */
+           while (i-- > 0)
+             if (d[i].type != 0)
+               _hurd_port_free (&d[i].cell->port,
+                                &d[i].ulink, d[i].io_port);
+           break;
+         }
+
+      __mutex_unlock (&_hurd_dtable_lock);
+      HURD_CRITICAL_END;
+
+      if (i < nfds)
+       {
+         if (sigmask)
+           __sigprocmask (SIG_SETMASK, &oset, NULL);
+         errno = EBADF;
+         return -1;
+       }
+
+      lastfd = i - 1;
+      firstfd = i == 0 ? lastfd : 0;
+    }
+  else
+    {
+      /* Collect interested descriptors from the user's fd_set arguments.
+        Use local copies so we can't crash from user bogosity.  */
+
+      if (readfds == NULL)
+       FD_ZERO (&rfds);
+      else
+       rfds = *readfds;
+      if (writefds == NULL)
+       FD_ZERO (&wfds);
+      else
+       wfds = *writefds;
+      if (exceptfds == NULL)
+       FD_ZERO (&xfds);
+      else
+       xfds = *exceptfds;
+
+      HURD_CRITICAL_BEGIN;
+      __mutex_lock (&_hurd_dtable_lock);
+
+      if (nfds > _hurd_dtablesize)
+       nfds = _hurd_dtablesize;
+
+      /* Collect the ports for interesting FDs.  */
+      firstfd = lastfd = -1;
+      for (i = 0; i < nfds; ++i)
+       {
+         int type = 0;
+         if (readfds != NULL && FD_ISSET (i, &rfds))
+           type |= SELECT_READ;
+         if (writefds != NULL && FD_ISSET (i, &wfds))
+           type |= SELECT_WRITE;
+         if (exceptfds != NULL && FD_ISSET (i, &xfds))
+           type |= SELECT_URG;
+         d[i].type = type;
+         if (type)
+           {
+             d[i].cell = _hurd_dtable[i];
+             d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
+             if (d[i].io_port == MACH_PORT_NULL)
+               {
+                 /* If one descriptor is bogus, we fail completely.  */
+                 while (i-- > 0)
+                   _hurd_port_free (&d[i].cell->port, &d[i].ulink,
+                                    d[i].io_port);
+                 break;
+               }
+             lastfd = i;
+             if (firstfd == -1)
+               firstfd = i;
+           }
+       }
+
+      __mutex_unlock (&_hurd_dtable_lock);
+      HURD_CRITICAL_END;
+
+      if (i < nfds)
+       {
+         if (sigmask)
+           __sigprocmask (SIG_SETMASK, &oset, NULL);
+         errno = EBADF;
+         return -1;
+       }
+    }
+
+
+  err = 0;
+  got = 0;
+
+  /* Send them all io_select request messages.  */
+
+  if (firstfd == -1)
+    /* But not if there were no ports to deal with at all.
+       We are just a pure timeout.  */
+    portset = __mach_reply_port ();
+  else
+    {
+      portset = MACH_PORT_NULL;
+
+      for (i = firstfd; i <= lastfd; ++i)
+       if (d[i].type)
+         {
+           int type = d[i].type;
+           d[i].reply_port = __mach_reply_port ();
+           err = __io_select (d[i].io_port, d[i].reply_port,
+                              /* Poll only if there's a single descriptor.  */
+                              (firstfd == lastfd) ? to : 0,
+                              &type);
+           switch (err)
+             {
+             case MACH_RCV_TIMED_OUT:
+               /* No immediate response.  This is normal.  */
+               err = 0;
+               if (firstfd == lastfd)
+                 /* When there's a single descriptor, we don't need a
+                    portset, so just pretend we have one, but really
+                    use the single reply port.  */
+                 portset = d[i].reply_port;
+               else if (got == 0)
+                 /* We've got multiple reply ports, so we need a port set to
+                    multiplex them.  */
+                 {
+                   /* We will wait again for a reply later.  */
+                   if (portset == MACH_PORT_NULL)
+                     /* Create the portset to receive all the replies on.  */
+                     err = __mach_port_allocate (__mach_task_self (),
+                                                 MACH_PORT_RIGHT_PORT_SET,
+                                                 &portset);
+                   if (! err)
+                     /* Put this reply port in the port set.  */
+                     __mach_port_move_member (__mach_task_self (),
+                                              d[i].reply_port, portset);
+                 }
+               break;
+
+             default:
+               /* No other error should happen.  Callers of select
+                  don't expect to see errors, so we simulate
+                  readiness of the erring object and the next call
+                  hopefully will get the error again.  */
+               type = SELECT_ALL;
+               /* FALLTHROUGH */
+
+             case 0:
+               /* We got an answer.  */
+               if ((type & SELECT_ALL) == 0)
+                 /* Bogus answer; treat like an error, as a fake positive.  */
+                 type = SELECT_ALL;
+
+               /* This port is already ready already.  */
+               d[i].type &= type;
+               d[i].type |= SELECT_RETURNED;
+               ++got;
+               break;
+             }
+           _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
+         }
+    }
+
+  /* Now wait for reply messages.  */
+  if (!err && got == 0)
+    {
+      /* Now wait for io_select_reply messages on PORT,
+        timing out as appropriate.  */
+
+      union
+       {
+         mach_msg_header_t head;
+         struct
+           {
+             mach_msg_header_t head;
+             mach_msg_type_t err_type;
+             error_t err;
+           } error;
+         struct
+           {
+             mach_msg_header_t head;
+             mach_msg_type_t err_type;
+             error_t err;
+             mach_msg_type_t result_type;
+             int result;
+           } success;
+       } msg;
+      mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
+      error_t msgerr;
+      while ((msgerr = __mach_msg (&msg.head,
+                                  MACH_RCV_MSG | options,
+                                  0, sizeof msg, portset, to,
+                                  MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
+       {
+         /* We got a message.  Decode it.  */
+#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
+         const mach_msg_type_t inttype =
+           { MACH_MSG_TYPE_INTEGER_T, sizeof (MACH_MSG_TYPE_INTEGER_T) * 8,
+             1, 1, 0, 0 };
+         if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
+             msg.head.msgh_size >= sizeof msg.error &&
+             !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
+             *(int *) &msg.error.err_type == *(int *) &inttype)
+           {
+             /* This is a properly formatted message so far.
+                See if it is a success or a failure.  */
+             if (msg.error.err == EINTR &&
+                 msg.head.msgh_size == sizeof msg.error)
+               {
+                 /* EINTR response; poll for further responses
+                    and then return quickly.  */
+                 err = EINTR;
+                 goto poll;
+               }
+             if (msg.error.err ||
+                 msg.head.msgh_size != sizeof msg.success ||
+                 *(int *) &msg.success.result_type != *(int *) &inttype ||
+                 (msg.success.result & SELECT_ALL) == 0)
+               {
+                 /* Error or bogus reply.  Simulate readiness.  */
+                 __mach_msg_destroy (&msg.head);
+                 msg.success.result = SELECT_ALL;
+               }
+
+             /* Look up the respondent's reply port and record its
+                 readiness.  */
+             {
+               int had = got;
+               if (firstfd != -1)
+                 for (i = firstfd; i <= lastfd; ++i)
+                   if (d[i].type
+                       && d[i].reply_port == msg.head.msgh_local_port)
+                     {
+                       d[i].type &= msg.success.result;
+                       d[i].type |= SELECT_RETURNED;
+                       ++got;
+                     }
+               assert (got > had);
+             }
+           }
+
+         if (msg.head.msgh_remote_port != MACH_PORT_NULL)
+           __mach_port_deallocate (__mach_task_self (),
+                                   msg.head.msgh_remote_port);
+
+         if (got)
+         poll:
+           {
+             /* Poll for another message.  */
+             to = 0;
+             options |= MACH_RCV_TIMEOUT;
+           }
+       }
+
+      if (err == MACH_RCV_TIMED_OUT)
+       /* This is the normal value for ERR.  We might have timed out and
+          read no messages.  Otherwise, after receiving the first message,
+          we poll for more messages.  We receive with a timeout of 0 to
+          effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
+          message waiting.  */
+       err = 0;
+
+      if (got)
+       /* At least one descriptor is known to be ready now, so we will
+          return success.  */
+       err = 0;
+    }
+
+  if (firstfd != -1)
+    for (i = firstfd; i <= lastfd; ++i)
+      if (d[i].type)
+       __mach_port_destroy (__mach_task_self (), d[i].reply_port);
+  if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL))
+    /* Destroy PORTSET, but only if it's not actually the reply port for a
+       single descriptor (in which case it's destroyed in the previous loop;
+       not doing it here is just a bit more efficient).  */
+    __mach_port_destroy (__mach_task_self (), portset);
+
+  if (err)
+    {
+      if (sigmask)
+       __sigprocmask (SIG_SETMASK, &oset, NULL);
+      return __hurd_fail (err);
+    }
+
+  if (pollfds)
+    /* Fill in the `revents' members of the user's array.  */
+    for (i = 0; i < nfds; ++i)
+      {
+       const int type = d[i].type;
+       int_fast16_t revents = 0;
+
+       if (type & SELECT_READ)
+         revents |= POLLIN;
+       if (type & SELECT_WRITE)
+         revents |= POLLOUT;
+       if (type & SELECT_URG)
+         revents |= POLLPRI;
+
+       pollfds[i].revents = revents;
+      }
+  else
+    {
+      /* Below we recalculate GOT to include an increment for each operation
+        allowed on each fd.  */
+      got = 0;
+
+      /* Set the user bitarrays.  We only ever have to clear bits, as all
+        desired ones are initially set.  */
+      if (firstfd != -1)
+       for (i = firstfd; i <= lastfd; ++i)
+         {
+           int type = d[i].type;
+
+           if ((type & SELECT_RETURNED) == 0)
+             type = 0;
+
+           if (type & SELECT_READ)
+             got++;
+           else if (readfds)
+             FD_CLR (i, readfds);
+           if (type & SELECT_WRITE)
+             got++;
+           else if (writefds)
+             FD_CLR (i, writefds);
+           if (type & SELECT_URG)
+             got++;
+           else if (exceptfds)
+             FD_CLR (i, exceptfds);
+         }
+    }
+
+  if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL))
+    return -1;
+
+  return got;
+}
diff --git a/sysdeps/mach/hurd/pselect.c b/sysdeps/mach/hurd/pselect.c
new file mode 100644 (file)
index 0000000..6c0bc7b
--- /dev/null
@@ -0,0 +1,45 @@
+/* pselect for Hurd.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <hurd.h>
+#include <hurd/fd.h>
+
+/* Check the first NFDS descriptors each in READFDS (if not NULL) for read
+   readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
+   (if not NULL) for exceptional conditions.  If TIMEOUT is not NULL, time out
+   after waiting the interval specified therein.  Additionally set the sigmask
+   SIGMASK for this call.  Returns the number of ready descriptors, or -1 for
+   errors.  */
+int
+__pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask)
+     int nfds;
+     fd_set *readfds;
+     fd_set *writefds;
+     fd_set *exceptfds;
+     const struct timespec *timeout;
+     const sigset_t *sigmask;
+{
+  return _hurd_select (nfds, NULL,
+                      readfds, writefds, exceptfds, timeout, sigmask);
+}
+weak_alias (__pselect, pselect)
index 904e141..e58b542 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+/* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
    Boston, MA 02111-1307, USA.  */
 
 #include <sys/types.h>
+#include <sys/time.h>
 #include <hurd.h>
 #include <hurd/fd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-/* All user select types.  */
-#define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
-
-/* Used to record that a particular select rpc returned. Must be distinct
-   from SELECT_ALL (which better not have the high bit set).  */
-#define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
 
 /* Check the first NFDS descriptors each in READFDS (if not NULL) for read
    readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
@@ -43,299 +34,17 @@ __select (nfds, readfds, writefds, exceptfds, timeout)
      fd_set *exceptfds;
      struct timeval *timeout;
 {
-  int i;
-  mach_port_t portset;
-  int got;
-  error_t err;
-  fd_set rfds, wfds, xfds;
-  int firstfd, lastfd;
-  mach_msg_timeout_t to = (timeout != NULL ?
-                          (timeout->tv_sec * 1000 +
-                           timeout->tv_usec / 1000) :
-                          0);
-  struct
-    {
-      struct hurd_userlink ulink;
-      struct hurd_fd *cell;
-      mach_port_t io_port;
-      int type;
-      mach_port_t reply_port;
-    } d[nfds];
-
-  /* Use local copies so we can't crash from user bogosity.  */
-  if (readfds == NULL)
-    FD_ZERO (&rfds);
-  else
-    rfds = *readfds;
-  if (writefds == NULL)
-    FD_ZERO (&wfds);
-  else
-    wfds = *writefds;
-  if (exceptfds == NULL)
-    FD_ZERO (&xfds);
-  else
-    xfds = *exceptfds;
-
-  HURD_CRITICAL_BEGIN;
-  __mutex_lock (&_hurd_dtable_lock);
-
-  if (nfds > _hurd_dtablesize)
-    nfds = _hurd_dtablesize;
+  struct timespec ts, *to;
 
-  /* Collect the ports for interesting FDs.  */
-  firstfd = lastfd = -1;
-  for (i = 0; i < nfds; ++i)
+  if (timeout)
     {
-      int type = 0;
-      if (readfds != NULL && FD_ISSET (i, &rfds))
-       type |= SELECT_READ;
-      if (writefds != NULL && FD_ISSET (i, &wfds))
-       type |= SELECT_WRITE;
-      if (exceptfds != NULL && FD_ISSET (i, &xfds))
-       type |= SELECT_URG;
-      d[i].type = type;
-      if (type)
-       {
-         d[i].cell = _hurd_dtable[i];
-         d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
-         if (d[i].io_port == MACH_PORT_NULL)
-           {
-             /* If one descriptor is bogus, we fail completely.  */
-             while (i-- > 0)
-               _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
-             errno = EBADF;
-             break;
-           }
-         lastfd = i;
-         if (firstfd == -1)
-           firstfd = i;
-       }
+      to = &ts;
+      TIMEVAL_TO_TIMESPEC (timeout, to);
     }
-
-  __mutex_unlock (&_hurd_dtable_lock);
-  HURD_CRITICAL_END;
-
-  if (i < nfds)
-    return -1;
-
-  /* Send them all io_select request messages.  */
-
-  if (firstfd == -1)
-    /* But not if there were no ports to deal with at all. */
-    portset = __mach_reply_port ();
   else
-    {
-      err = 0;
-      got = 0;
-      portset = MACH_PORT_NULL;
-
-      for (i = firstfd; i <= lastfd; ++i)
-       if (d[i].type)
-         {
-           int type = d[i].type;
-           d[i].reply_port = __mach_reply_port ();
-           err = __io_select (d[i].io_port, d[i].reply_port,
-                              /* Poll only if there's a single descriptor.  */
-                              (firstfd == lastfd) ? to : 0,
-                              &type);
-           switch (err)
-             {
-             case MACH_RCV_TIMED_OUT:
-               /* No immediate response.  This is normal.  */
-               err = 0;
-               if (firstfd == lastfd)
-                 /* When there's a single descriptor, we don't need a
-                    portset, so just pretend we have one, but really
-                    use the single reply port.  */
-                 portset = d[i].reply_port;
-               else if (got == 0)
-                 /* We've got multiple reply ports, so we need a port set to
-                    multiplex them.  */
-                 {
-                   /* We will wait again for a reply later.  */
-                   if (portset == MACH_PORT_NULL)
-                     /* Create the portset to receive all the replies on.  */
-                     err = __mach_port_allocate (__mach_task_self (),
-                                                 MACH_PORT_RIGHT_PORT_SET,
-                                                 &portset);
-                   if (! err)
-                     /* Put this reply port in the port set.  */
-                     __mach_port_move_member (__mach_task_self (),
-                                              d[i].reply_port, portset);
-                 }
-               break;
-
-             default:
-               /* No other error should happen.  Callers of select
-                  don't expect to see errors, so we simulate
-                  readiness of the erring object and the next call
-                  hopefully will get the error again.  */
-               type = SELECT_ALL;
-               /* FALLTHROUGH */
-
-             case 0:
-               /* We got an answer.  */
-               if ((type & SELECT_ALL) == 0)
-                 /* Bogus answer; treat like an error, as a fake positive.  */
-                 type = SELECT_ALL;
-
-               /* This port is already ready already.  */
-               d[i].type &= type;
-               d[i].type |= SELECT_RETURNED;
-               ++got;
-               break;
-             }
-           _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
-         }
-    }
-
-  /* Now wait for reply messages.  */
-  if (!err && got == 0)
-    {
-      /* Now wait for io_select_reply messages on PORT,
-        timing out as appropriate.  */
-
-      union
-       {
-         mach_msg_header_t head;
-         struct
-           {
-             mach_msg_header_t head;
-             mach_msg_type_t err_type;
-             error_t err;
-           } error;
-         struct
-           {
-             mach_msg_header_t head;
-             mach_msg_type_t err_type;
-             error_t err;
-             mach_msg_type_t result_type;
-             int result;
-           } success;
-       } msg;
-      mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
-      error_t msgerr;
-      while ((msgerr = __mach_msg (&msg.head,
-                                  MACH_RCV_MSG | options,
-                                  0, sizeof msg, portset, to,
-                                  MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
-       {
-         /* We got a message.  Decode it.  */
-#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
-         const mach_msg_type_t inttype =
-           { MACH_MSG_TYPE_INTEGER_T, sizeof (MACH_MSG_TYPE_INTEGER_T) * 8,
-             1, 1, 0, 0 };
-         if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
-             msg.head.msgh_size >= sizeof msg.error &&
-             !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
-             *(int *) &msg.error.err_type == *(int *) &inttype)
-           {
-             /* This is a properly formatted message so far.
-                See if it is a success or a failure.  */
-             if (msg.error.err == EINTR &&
-                 msg.head.msgh_size == sizeof msg.error)
-               {
-                 /* EINTR response; poll for further responses
-                    and then return quickly.  */
-                 err = EINTR;
-                 goto poll;
-               }
-             if (msg.error.err ||
-                 msg.head.msgh_size != sizeof msg.success ||
-                 *(int *) &msg.success.result_type != *(int *) &inttype ||
-                 (msg.success.result & SELECT_ALL) == 0)
-               {
-                 /* Error or bogus reply.  Simulate readiness.  */
-                 __mach_msg_destroy (&msg.head);
-                 msg.success.result = SELECT_ALL;
-               }
-
-             /* Look up the respondent's reply port and record its
-                 readiness.  */
-             {
-               int had = got;
-               if (firstfd != -1)
-                 for (i = firstfd; i <= lastfd; ++i)
-                   if (d[i].type
-                       && d[i].reply_port == msg.head.msgh_local_port)
-                     {
-                       d[i].type &= msg.success.result;
-                       d[i].type |= SELECT_RETURNED;
-                       ++got;
-                     }
-               assert (got > had);
-             }
-           }
-
-         if (msg.head.msgh_remote_port != MACH_PORT_NULL)
-           __mach_port_deallocate (__mach_task_self (),
-                                   msg.head.msgh_remote_port);
-
-         if (got)
-         poll:
-           {
-             /* Poll for another message.  */
-             to = 0;
-             options |= MACH_RCV_TIMEOUT;
-           }
-       }
-
-      if (err == MACH_RCV_TIMED_OUT)
-       /* This is the normal value for ERR.  We might have timed out and
-          read no messages.  Otherwise, after receiving the first message,
-          we poll for more messages.  We receive with a timeout of 0 to
-          effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
-          message waiting.  */
-       err = 0;
-
-      if (got)
-       /* At least one descriptor is known to be ready now, so we will
-          return success.  */
-       err = 0;
-    }
-
-  if (firstfd != -1)
-    for (i = firstfd; i <= lastfd; ++i)
-      if (d[i].type)
-       __mach_port_destroy (__mach_task_self (), d[i].reply_port);
-  if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL))
-    /* Destroy PORTSET, but only if it's not actually the reply port for a
-       single descriptor (in which case it's destroyed in the previous loop;
-       not doing it here is just a bit more efficient).  */
-    __mach_port_destroy (__mach_task_self (), portset);
-
-  if (err)
-    return __hurd_fail (err);
-
-  /* Below we recalculate GOT to include an increment for each operation
-     allowed on each fd.  */
-  got = 0;
-
-  /* Set the user bitarrays.  We only ever have to clear bits, as all desired
-     ones are initially set.  */
-  if (firstfd != -1)
-    for (i = firstfd; i <= lastfd; ++i)
-      {
-       int type = d[i].type;
-
-       if ((type & SELECT_RETURNED) == 0)
-         type = 0;
-
-       if (type & SELECT_READ)
-         got++;
-       else if (readfds)
-         FD_CLR (i, readfds);
-       if (type & SELECT_WRITE)
-         got++;
-       else if (writefds)
-         FD_CLR (i, writefds);
-       if (type & SELECT_URG)
-         got++;
-       else if (exceptfds)
-         FD_CLR (i, exceptfds);
-      }
+    to = NULL;
 
-  return got;
+  return _hurd_select (nfds, NULL, readfds, writefds, exceptfds, to, NULL);
 }
 
 weak_alias (__select, select)