summaryrefslogtreecommitdiff
path: root/hurd/hurdselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'hurd/hurdselect.c')
-rw-r--r--hurd/hurdselect.c259
1 files changed, 170 insertions, 89 deletions
diff --git a/hurd/hurdselect.c b/hurd/hurdselect.c
index c2225d2729..95f4c63264 100644
--- a/hurd/hurdselect.c
+++ b/hurd/hurdselect.c
@@ -16,14 +16,17 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <sys/time.h>
#include <sys/types.h>
#include <sys/poll.h>
#include <hurd.h>
#include <hurd/fd.h>
+#include <hurd/io_request.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
+#include <limits.h>
/* All user select types. */
#define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
@@ -31,6 +34,7 @@
/* 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)
+#define SELECT_ERROR (SELECT_RETURNED << 1)
/* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in
each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull. If TIMEOUT is not
@@ -44,11 +48,13 @@ _hurd_select (int nfds,
{
int i;
mach_port_t portset;
- int got;
+ int got, ready;
error_t err;
fd_set rfds, wfds, xfds;
int firstfd, lastfd;
- mach_msg_timeout_t to = 0;
+ mach_msg_id_t reply_msgid;
+ mach_msg_timeout_t to;
+ struct timespec ts;
struct
{
struct hurd_userlink ulink;
@@ -56,6 +62,7 @@ _hurd_select (int nfds,
mach_port_t io_port;
int type;
mach_port_t reply_port;
+ int error;
} d[nfds];
sigset_t oset;
@@ -67,22 +74,45 @@ _hurd_select (int nfds,
assert (sizeof (union typeword) == sizeof (mach_msg_type_t));
assert (sizeof (uint32_t) == sizeof (mach_msg_type_t));
- if (nfds < 0 || nfds > FD_SETSIZE)
+ if (nfds < 0 || (!pollfds && nfds > FD_SETSIZE))
{
errno = EINVAL;
return -1;
}
- if (timeout != NULL)
+#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
+#define IO_SELECT_TIMEOUT_REPLY_MSGID (21031 + 100) /* XXX */
+
+ if (timeout == NULL)
+ reply_msgid = IO_SELECT_REPLY_MSGID;
+ else
{
- if (timeout->tv_sec < 0 || timeout->tv_nsec < 0)
+ struct timeval now;
+
+ if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 ||
+ timeout->tv_nsec >= 1000000000)
{
errno = EINVAL;
return -1;
}
- to = (timeout->tv_sec * 1000 +
- (timeout->tv_nsec + 999999) / 1000000);
+ err = __gettimeofday(&now, NULL);
+ if (err)
+ return -1;
+
+ ts.tv_sec = now.tv_sec + timeout->tv_sec;
+ ts.tv_nsec = now.tv_usec * 1000 + timeout->tv_nsec;
+
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+
+ if (ts.tv_sec < 0)
+ ts.tv_sec = LONG_MAX; /* XXX */
+
+ reply_msgid = IO_SELECT_TIMEOUT_REPLY_MSGID;
}
if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset))
@@ -128,25 +158,33 @@ _hurd_select (int nfds,
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;
+ /* Bogus descriptor, make it EBADF already. */
+ d[i].error = EBADF;
+ d[i].type = SELECT_ERROR;
+
+ /* And set timeout to 0. */
+ {
+ struct timeval now;
+ err = __gettimeofday(&now, NULL);
+ if (err)
+ {
+ err = errno;
+ while (i-- > 0)
+ if (d[i].type & ~SELECT_ERROR != 0)
+ _hurd_port_free (&d[i].cell->port, &d[i].ulink,
+ d[i].io_port);
+ errno = err;
+ return -1;
+ }
+ ts.tv_sec = now.tv_sec;
+ ts.tv_nsec = now.tv_usec * 1000;
+ reply_msgid = IO_SELECT_TIMEOUT_REPLY_MSGID;
+ }
}
__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;
}
@@ -171,9 +209,6 @@ _hurd_select (int nfds,
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)
@@ -188,9 +223,12 @@ _hurd_select (int nfds,
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 (i < _hurd_dtablesize)
+ {
+ d[i].cell = _hurd_dtable[i];
+ d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
+ }
+ if (i >= _hurd_dtablesize || d[i].io_port == MACH_PORT_NULL)
{
/* If one descriptor is bogus, we fail completely. */
while (i-- > 0)
@@ -215,6 +253,9 @@ _hurd_select (int nfds,
errno = EBADF;
return -1;
}
+
+ if (nfds > _hurd_dtablesize)
+ nfds = _hurd_dtablesize;
}
@@ -232,19 +273,17 @@ _hurd_select (int nfds,
portset = MACH_PORT_NULL;
for (i = firstfd; i <= lastfd; ++i)
- if (d[i].type)
+ if (d[i].type & ~SELECT_ERROR)
{
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)
+ if (timeout == NULL)
+ err = __io_select_request (d[i].io_port, d[i].reply_port, type);
+ else
+ err = __io_select_timeout_request (d[i].io_port, d[i].reply_port,
+ ts, type);
+ if (!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
@@ -265,32 +304,23 @@ _hurd_select (int nfds,
__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;
+ }
+ else
+ {
+ /* No error should happen, but record it for later
+ processing. */
+ d[i].error = err;
+ d[i].type |= SELECT_ERROR;
++got;
- break;
}
_hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
}
}
+ /* GOT is the number of replies (or errors), while READY is the number of
+ replies with at least one type bit set. */
+ ready = 0;
+
/* Now wait for reply messages. */
if (!err && got == 0)
{
@@ -332,22 +362,35 @@ _hurd_select (int nfds,
} success;
#endif
} msg;
- mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
+ mach_msg_option_t options;
error_t msgerr;
+
+ /* We rely on servers to implement the timeout, but when there are none,
+ do it on the client side. */
+ if (timeout != NULL && firstfd == -1)
+ {
+ options = MACH_RCV_TIMEOUT;
+ to = timeout->tv_sec * 1000 + (timeout->tv_nsec + 999999) / 1000000;
+ }
+ else
+ {
+ options = 0;
+ to = MACH_MSG_TIMEOUT_NONE;
+ }
+
while ((msgerr = __mach_msg (&msg.head,
- MACH_RCV_MSG | options,
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT | 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 */
#ifdef MACH_MSG_TYPE_BIT
const union typeword inttype =
{ type:
{ MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8, 1, 1, 0, 0 }
};
#endif
- if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
+ if (msg.head.msgh_id == reply_msgid &&
msg.head.msgh_size >= sizeof msg.error &&
!(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
#ifdef MACH_MSG_TYPE_BIT
@@ -365,16 +408,18 @@ _hurd_select (int nfds,
err = EINTR;
goto poll;
}
+ /* Keep in mind msg.success.result can be 0 if a timeout
+ occurred. */
if (msg.error.err ||
- msg.head.msgh_size != sizeof msg.success ||
#ifdef MACH_MSG_TYPE_BIT
msg.success.result_type.word != inttype.word ||
#endif
- (msg.success.result & SELECT_ALL) == 0)
+ msg.head.msgh_size != sizeof msg.success)
{
- /* Error or bogus reply. Simulate readiness. */
+ /* Error or bogus reply. */
+ if (!msg.error.err)
+ msg.error.err = EIO;
__mach_msg_destroy (&msg.head);
- msg.success.result = SELECT_ALL;
}
/* Look up the respondent's reply port and record its
@@ -386,7 +431,19 @@ _hurd_select (int nfds,
if (d[i].type
&& d[i].reply_port == msg.head.msgh_local_port)
{
- d[i].type &= msg.success.result;
+ if (msg.error.err)
+ {
+ d[i].error = msg.error.err;
+ d[i].type = SELECT_ERROR;
+ ++ready;
+ }
+ else
+ {
+ d[i].type &= msg.success.result;
+ if (d[i].type)
+ ++ready;
+ }
+
d[i].type |= SELECT_RETURNED;
++got;
}
@@ -407,15 +464,11 @@ _hurd_select (int nfds,
}
}
- 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 (msgerr == MACH_RCV_INTERRUPTED)
+ /* Interruption on our side (e.g. signal reception). */
+ err = EINTR;
- if (got)
+ if (ready)
/* At least one descriptor is known to be ready now, so we will
return success. */
err = 0;
@@ -423,7 +476,7 @@ _hurd_select (int nfds,
if (firstfd != -1)
for (i = firstfd; i <= lastfd; ++i)
- if (d[i].type)
+ if (d[i].type & ~SELECT_ERROR)
__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
@@ -445,23 +498,37 @@ _hurd_select (int nfds,
int type = d[i].type;
int_fast16_t revents = 0;
- if (type & SELECT_RETURNED)
- {
- if (type & SELECT_READ)
- revents |= POLLIN;
- if (type & SELECT_WRITE)
- revents |= POLLOUT;
- if (type & SELECT_URG)
- revents |= POLLPRI;
- }
+ if (type & SELECT_ERROR)
+ switch (d[i].error)
+ {
+ case EPIPE:
+ revents = POLLHUP;
+ break;
+ case EBADF:
+ revents = POLLNVAL;
+ break;
+ default:
+ revents = POLLERR;
+ break;
+ }
+ else
+ if (type & SELECT_RETURNED)
+ {
+ 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
+ /* Below we recalculate READY to include an increment for each operation
allowed on each fd. */
- got = 0;
+ ready = 0;
/* Set the user bitarrays. We only ever have to clear bits, as all
desired ones are initially set. */
@@ -473,16 +540,30 @@ _hurd_select (int nfds,
if ((type & SELECT_RETURNED) == 0)
type = 0;
+ /* 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. */
+ if (type & SELECT_ERROR)
+ {
+ type = 0;
+ if (readfds != NULL && FD_ISSET (i, readfds))
+ type |= SELECT_READ;
+ if (writefds != NULL && FD_ISSET (i, writefds))
+ type |= SELECT_WRITE;
+ if (exceptfds != NULL && FD_ISSET (i, exceptfds))
+ type |= SELECT_URG;
+ }
+
if (type & SELECT_READ)
- got++;
+ ready++;
else if (readfds)
FD_CLR (i, readfds);
if (type & SELECT_WRITE)
- got++;
+ ready++;
else if (writefds)
FD_CLR (i, writefds);
if (type & SELECT_URG)
- got++;
+ ready++;
else if (exceptfds)
FD_CLR (i, exceptfds);
}
@@ -491,5 +572,5 @@ _hurd_select (int nfds,
if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL))
return -1;
- return got;
+ return ready;
}