From 8cd75c4d6b229bb4e3de9264466731e3a32e0133 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 26 Feb 2013 22:24:47 +0100 Subject: Add io_select_timeout to the io interface This change fixes a problem that can occur with non-blocking (and also blocking for very short times) select/poll calls. The problem occurs because the timeout is implemented at the client side. For a non-blocking call, this means that (depending on the code path taken in the C library) the client could get a timeout without a full RPC round-trip to the server. Moving the implementation of the timeout to the servers guarantees a full round-trip, and correct results for non-blocking calls. The modifications in this change depend on the availability of the recently added pthread_hurd_cond_timedwait_np function in libpthread. * boot/boot.c (io_select_common): New static function. (S_io_select): Use io_select_common. (S_io_select_timeout): New function which makes use of io_select_common. * console-client/kbd-repeat.c (repeater_select): Add a timeout parameter. * console-client/pc-mouse.c (repeater_select): Likewise. * console-client/trans.c (io_select_common): New static function. (netfs_S_io_select): Use io_select_common. (netfs_S_io_select_timeout): New function which makes use of io_select_common. * console-client/trans.h (struct consnode): Add a timeout parameter. * hurd/io.defs (io_select_timeout): New MIG routine. * hurd/io_reply.defs (io_select_timeout_reply): New MIG simpleroutine. * hurd/io_request.defs (io_select_timeout_request): Likewise. * libdiskfs/io-select.c (diskfs_S_io_select_timeout): New function. * libnetfs/io-select.c (netfs_S_io_select_timeout): Likewise. * libpipe/pipe.c (pipe_pair_select): Add a timeout parameter. * libpipe/pipe.h (pipe_select_readable): Likewise. (pipe_select_writable): Likewise. (pipe_pair_select): Likewise. * libpipe/pq.h: Include . * libtrivfs/io-select.c (trivfs_S_io_select_timeout): New function. * pfinet/glue-include/linux/sched.h: Include . (interruptible_sleep_on): Function removed, replaced with ... (interruptible_sleep_on_timeout): New function. (schedule): Update to use interruptible_sleep_on_timeout. (schedule_timeout): Likewise. * pfinet/io-ops.c (io_select_common): New static function. (S_io_select): Use io_select_common. (S_io_select_timeout): New function which makes use of io_select_common. * pfinet/tunnel.c (io_select_common): New static function. (trivfs_S_io_select): Use io_select_common. (trivfs_S_io_select_timeout): New function which makes use of io_select_common. * pflocal/connq.c (connq_listen): Replace noblock with a timeout parameter. * pflocal/connq.h: Include . (connq_listen): Update declaration to replace noblock with a timeout parameter. * pflocal/io.c (io_select_common): New static function. (S_io_select): Use io_select_common. (S_io_select_timeout): New function which makes use of io_select_common. * pflocal/socket.c (S_socket_accept): Update call to connq_listen to match new declaration. * storeio/io.c (trivfs_S_io_select_timeout): New function. * term/ptyio.c (pty_io_select): Add a timeout parameter. * term/term.h (pty_io_select): Likewise. * term/users.c (io_select_common): New static function. (trivfs_S_io_select): Use io_select_common. (trivfs_S_io_select_timeout): New function which makes use of io_select_common. * trans/fifo.c (io_select_common): New static function. (trivfs_S_io_select): Use io_select_common. (trivfs_S_io_select_timeout): New function which makes use of io_select_common. * trans/firmlink.c (trivfs_S_io_select_timeout): New function. * trans/new-fifo.c (io_select_common): New static function. (trivfs_S_io_select): Use io_select_common. (trivfs_S_io_select_timeout): New function which makes use of io_select_common. * trans/null.c (trivfs_S_io_select_timeout): New function. * trans/streamio.c (io_select_common): New static function. (trivfs_S_io_select): Use io_select_common. (trivfs_S_io_select_timeout): New function which makes use of io_select_common. --- boot/boot.c | 41 ++++++++++++++++++++++++++++++------ console-client/kbd-repeat.c | 13 +++++++++--- console-client/pc-mouse.c | 13 +++++++++--- console-client/trans.c | 26 +++++++++++++++++++---- console-client/trans.h | 5 +++-- hurd/io.defs | 11 ++++++++++ hurd/io_reply.defs | 5 +++++ hurd/io_request.defs | 6 ++++++ libdiskfs/io-select.c | 8 +++++++ libnetfs/io-select.c | 10 +++++++++ libpipe/pipe.c | 16 +++++++++----- libpipe/pipe.h | 34 ++++++++++++++++++++---------- libtrivfs/io-select.c | 10 +++++++++ pfinet/glue-include/linux/sched.h | 15 +++++++------ pfinet/io-ops.c | 44 +++++++++++++++++++++++++++++++-------- pfinet/tunnel.c | 40 ++++++++++++++++++++++++++++------- pflocal/connq.c | 17 +++++++++------ pflocal/connq.h | 9 +++++--- pflocal/io.c | 39 +++++++++++++++++++++++++++------- pflocal/socket.c | 4 +++- storeio/io.c | 9 ++++++++ term/ptyio.c | 14 +++++++++---- term/term.h | 3 ++- term/users.c | 43 +++++++++++++++++++++++++++++++------- trans/fifo.c | 27 +++++++++++++++++++----- trans/firmlink.c | 9 ++++++++ trans/new-fifo.c | 27 +++++++++++++++++++----- trans/null.c | 9 ++++++++ trans/streamio.c | 36 ++++++++++++++++++++++++++------ 29 files changed, 441 insertions(+), 102 deletions(-) diff --git a/boot/boot.c b/boot/boot.c index fbbced77..0dbe1c20 100644 --- a/boot/boot.c +++ b/boot/boot.c @@ -1588,12 +1588,13 @@ S_io_get_icky_async_id (mach_port_t object, return EOPNOTSUPP; } -kern_return_t -S_io_select (mach_port_t object, - mach_port_t reply_port, - mach_msg_type_name_t reply_type, - int *type) +static kern_return_t +io_select_common (mach_port_t object, + mach_port_t reply_port, + mach_msg_type_name_t reply_type, + struct timespec *tsp, int *type) { + struct timeval tv, *tvp; fd_set r, w, x; int n; @@ -1607,11 +1608,20 @@ S_io_select (mach_port_t object, FD_SET (0, &w); FD_SET (0, &x); + if (tsp == NULL) + tvp = NULL; + else + { + tv.tv_sec = tsp->tv_sec; + tv.tv_usec = tsp->tv_nsec / 1000; + tvp = &tv; + } + n = select (1, (*type & SELECT_READ) ? &r : 0, (*type & SELECT_WRITE) ? &w : 0, (*type & SELECT_URG) ? &x : 0, - 0); + tvp); if (n < 0) return errno; @@ -1625,6 +1635,25 @@ S_io_select (mach_port_t object, return 0; } +kern_return_t +S_io_select (mach_port_t object, + mach_port_t reply_port, + mach_msg_type_name_t reply_type, + int *type) +{ + return io_select_common (object, reply_port, reply_type, NULL, type); +} + +kern_return_t +S_io_select_timeout (mach_port_t object, + mach_port_t reply_port, + mach_msg_type_name_t reply_type, + struct timespec ts, + int *type) +{ + return io_select_common (object, reply_port, reply_type, &ts, type); +} + kern_return_t S_io_stat (mach_port_t object, mach_port_t reply_port, diff --git a/console-client/kbd-repeat.c b/console-client/kbd-repeat.c index 641520ca..d3a39d07 100644 --- a/console-client/kbd-repeat.c +++ b/console-client/kbd-repeat.c @@ -86,8 +86,11 @@ kbd_repeat_key (kd_event *key) static error_t repeater_select (struct protid *cred, mach_port_t reply, - mach_msg_type_name_t replytype, int *type) + mach_msg_type_name_t replytype, + struct timespec *tsp, int *type) { + error_t err; + if (!cred) return EOPNOTSUPP; @@ -109,12 +112,16 @@ repeater_select (struct protid *cred, mach_port_t reply, } ports_interrupt_self_on_port_death (cred, reply); - if (pthread_hurd_cond_wait_np (&select_alert, &global_lock)) + err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp); + if (err) { *type = 0; pthread_mutex_unlock (&global_lock); - return EINTR; + if (err == ETIMEDOUT) + err = 0; + + return err; } } } diff --git a/console-client/pc-mouse.c b/console-client/pc-mouse.c index 2cec2042..8be7ff1c 100644 --- a/console-client/pc-mouse.c +++ b/console-client/pc-mouse.c @@ -110,8 +110,11 @@ repeat_event (kd_event *evt) static error_t repeater_select (struct protid *cred, mach_port_t reply, - mach_msg_type_name_t replytype, int *type) + mach_msg_type_name_t replytype, + struct timespec *tsp, int *type) { + error_t err; + if (!cred) return EOPNOTSUPP; @@ -133,12 +136,16 @@ repeater_select (struct protid *cred, mach_port_t reply, } ports_interrupt_self_on_port_death (cred, reply); - if (pthread_hurd_cond_wait_np (&select_alert, &global_lock)) + err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp); + if (err) { *type = 0; pthread_mutex_unlock (&global_lock); - return EINTR; + if (err == ETIMEDOUT) + err = 0; + + return err; } } } diff --git a/console-client/trans.c b/console-client/trans.c index 67c84b4e..6949af5e 100644 --- a/console-client/trans.c +++ b/console-client/trans.c @@ -332,9 +332,10 @@ netfs_S_io_seek (struct protid *user, off_t offset, } -error_t -netfs_S_io_select (struct protid *user, mach_port_t reply, - mach_msg_type_name_t replytype, int *type) +static error_t +io_select_common (struct protid *user, mach_port_t reply, + mach_msg_type_name_t replytype, + struct timespec *tsp, int *type) { struct node *np; @@ -344,11 +345,28 @@ netfs_S_io_select (struct protid *user, mach_port_t reply, np = user->po->np; if (np->nn->node && np->nn->node->select) - return np->nn->node->select (user, reply, replytype, type); + return np->nn->node->select (user, reply, replytype, tsp, type); return EOPNOTSUPP; } +error_t +netfs_S_io_select (struct protid *user, mach_port_t reply, + mach_msg_type_name_t replytype, int *type) +{ + return io_select_common (user, reply, replytype, NULL, type); +} + + +error_t +netfs_S_io_select_timeout (struct protid *user, mach_port_t reply, + mach_msg_type_name_t replytype, + struct timespec ts, int *type) +{ + return io_select_common (user, reply, replytype, &ts, type); +} + + /* Delete NAME in DIR (which is locked) for USER. */ error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, diff --git a/console-client/trans.h b/console-client/trans.h index f781e31f..9891cf3f 100644 --- a/console-client/trans.h +++ b/console-client/trans.h @@ -41,9 +41,10 @@ struct consnode mach_msg_type_number_t datalen, off_t offset, mach_msg_type_number_t *amount); - /* This is exactly the same as io_select does. */ + /* This is exactly the same as io_select{,_timeout} do. */ error_t (*select) (struct protid *user, mach_port_t reply, - mach_msg_type_name_t replytype, int *type); + mach_msg_type_name_t replytype, + struct timespec *tsp, int *type); /* Called when the node is opened. */ void (*open) (void); diff --git a/hurd/io.defs b/hurd/io.defs index 9119b05b..ba0b8077 100644 --- a/hurd/io.defs +++ b/hurd/io.defs @@ -320,3 +320,14 @@ routine io_identity ( on the specified object. */ routine io_revoke ( io_object: io_t RPTLAST); + +/* INTR */ +routine io_select_timeout ( + io_object: io_t; +#if defined (REPLY_PORTS) || defined (IO_SELECT_REPLY_PORT) + replyport reply: sreply_port_t; +#else + ureplyport reply: mach_port_make_send_t; +#endif + timeout: timespec_t; + inout select_type: int); diff --git a/hurd/io_reply.defs b/hurd/io_reply.defs index ffc4e752..eee68244 100644 --- a/hurd/io_reply.defs +++ b/hurd/io_reply.defs @@ -175,3 +175,8 @@ simpleroutine io_identity_reply ( simpleroutine io_revoke_reply ( reply: reply_port_t; RETURN_CODE_ARG); + +simpleroutine io_select_timeout_reply ( + reply: reply_port_t; + RETURN_CODE_ARG; + select_result: int); diff --git a/hurd/io_request.defs b/hurd/io_request.defs index a3e775aa..0d5e36dd 100644 --- a/hurd/io_request.defs +++ b/hurd/io_request.defs @@ -172,3 +172,9 @@ simpleroutine io_identity_request ( simpleroutine io_revoke_request ( io_object: io_t; reply: reply_port_t); + +simpleroutine io_select_timeout_request ( + io_object: io_t; + ureplyport reply: mach_port_make_send_t; + timeout: timespec_t; + select_type: int); diff --git a/libdiskfs/io-select.c b/libdiskfs/io-select.c index 07112f7b..ddac738f 100644 --- a/libdiskfs/io-select.c +++ b/libdiskfs/io-select.c @@ -30,3 +30,11 @@ diskfs_S_io_select (struct protid *cred, *type &= ~SELECT_URG; return 0; } + +kern_return_t +diskfs_S_io_select_timeout (struct protid *cred, + struct timespec ts, + int *type) +{ + return diskfs_S_io_select (cred, type); +} diff --git a/libnetfs/io-select.c b/libnetfs/io-select.c index c72eba6b..63036145 100644 --- a/libnetfs/io-select.c +++ b/libnetfs/io-select.c @@ -34,3 +34,13 @@ netfs_S_io_select (struct protid *user, *type &= ~SELECT_URG; return 0; } + +error_t +netfs_S_io_select_timeout (struct protid *user, + mach_port_t reply, + mach_msg_type_name_t replytype, + struct timespec ts, + int *type) +{ + return netfs_S_io_select (user, reply, replytype, type); +} diff --git a/libpipe/pipe.c b/libpipe/pipe.c index dd306f60..1ddaf126 100644 --- a/libpipe/pipe.c +++ b/libpipe/pipe.c @@ -209,7 +209,7 @@ void _pipe_no_writers (struct pipe *pipe) this function (unlike most pipe functions). */ error_t pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe, - int *select_type, int data_only) + struct timespec *tsp, int *select_type, int data_only) { error_t err = 0; @@ -218,13 +218,13 @@ pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe, if (*select_type == SELECT_READ) { pthread_mutex_lock (&rpipe->lock); - err = pipe_select_readable (rpipe, data_only); + err = pipe_select_readable (rpipe, tsp, data_only); pthread_mutex_unlock (&rpipe->lock); } else if (*select_type == SELECT_WRITE) { pthread_mutex_lock (&wpipe->lock); - err = pipe_select_writable (wpipe); + err = pipe_select_writable (wpipe, tsp); pthread_mutex_unlock (&wpipe->lock); } else @@ -260,8 +260,8 @@ pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe, pthread_mutex_unlock (&rpipe->lock); pthread_mutex_unlock (&wpipe->lock); } - if (pthread_hurd_cond_wait_np (&pending_select.cond, lock)) - err = EINTR; + err = pthread_hurd_cond_timedwait_np (&pending_select.cond, lock, + tsp); if (rpipe != wpipe) { pthread_mutex_lock (&rpipe->lock); @@ -295,6 +295,12 @@ pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe, pthread_mutex_unlock (lock); } + if (err == ETIMEDOUT) + { + err = 0; + *select_type = 0; + } + return err; } diff --git a/libpipe/pipe.h b/libpipe/pipe.h index b8e70681..040204d5 100644 --- a/libpipe/pipe.h +++ b/libpipe/pipe.h @@ -126,11 +126,12 @@ extern int pipe_is_readable (struct pipe *pipe, int data_only); extern error_t pipe_wait_readable (struct pipe *pipe, int noblock, int data_only); -extern error_t pipe_select_readable (struct pipe *pipe, int data_only); +extern error_t pipe_select_readable (struct pipe *pipe, struct timespec *tsp, + int data_only); extern error_t pipe_wait_writable (struct pipe *pipe, int noblock); -extern error_t pipe_select_writable (struct pipe *pipe); +extern error_t pipe_select_writable (struct pipe *pipe, struct timespec *tsp); #if defined(__USE_EXTERN_INLINES) || defined(PIPE_DEFINE_EI) @@ -188,12 +189,17 @@ pipe_wait_readable (struct pipe *pipe, int noblock, int data_only) given a chance to read, and if there is still data available thereafter. If DATA_ONLY is true, then `control' packets are ignored. */ PIPE_EI error_t -pipe_select_readable (struct pipe *pipe, int data_only) +pipe_select_readable (struct pipe *pipe, struct timespec *tsp, int data_only) { + error_t err = 0; while (! pipe_is_readable (pipe, data_only) && ! (pipe->flags & PIPE_BROKEN)) - if (pthread_hurd_cond_wait_np (&pipe->pending_read_selects, &pipe->lock)) - return EINTR; - return 0; + { + err = pthread_hurd_cond_timedwait_np (&pipe->pending_read_selects, + &pipe->lock, tsp); + if (err) + break; + } + return err; } /* Block until data can be written to PIPE. If NOBLOCK is true, then @@ -221,13 +227,18 @@ pipe_wait_writable (struct pipe *pipe, int noblock) threads waiting using pipe_wait_writable have been woken and given a chance to write, and if there is still space available thereafter. */ PIPE_EI error_t -pipe_select_writable (struct pipe *pipe) +pipe_select_writable (struct pipe *pipe, struct timespec *tsp) { size_t limit = pipe->write_limit; + error_t err = 0; while (! (pipe->flags & PIPE_BROKEN) && pipe_readable (pipe, 1) >= limit) - if (pthread_hurd_cond_wait_np (&pipe->pending_writes, &pipe->lock)) - return EINTR; - return 0; + { + err = pthread_hurd_cond_timedwait_np (&pipe->pending_writes, + &pipe->lock, tsp); + if (err) + break; + } + return err; } #endif /* Use extern inlines. */ @@ -419,7 +430,8 @@ extern pthread_mutex_t pipe_multiple_lock; waited for on RPIPE. Neither RPIPE or WPIPE should be locked when calling this function (unlike most pipe functions). */ error_t pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe, - int *select_type, int data_only); + struct timespec *tsp, int *select_type, + int data_only); /* ---------------------------------------------------------------- */ /* User-provided functions. */ diff --git a/libtrivfs/io-select.c b/libtrivfs/io-select.c index 9df24d61..e44a8365 100644 --- a/libtrivfs/io-select.c +++ b/libtrivfs/io-select.c @@ -36,3 +36,13 @@ trivfs_S_io_select (struct trivfs_protid *cred, assert (!trivfs_support_write); return EOPNOTSUPP; } + +kern_return_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + struct timespec ts, + int *seltype) +{ + return trivfs_S_io_select (cred, reply, replytype, seltype); +} diff --git a/pfinet/glue-include/linux/sched.h b/pfinet/glue-include/linux/sched.h index aea6c47a..26ab10a8 100644 --- a/pfinet/glue-include/linux/sched.h +++ b/pfinet/glue-include/linux/sched.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "mapped-time.h" @@ -92,12 +93,13 @@ capable(int cap) extern pthread_mutex_t global_lock; -static inline void -interruptible_sleep_on (struct wait_queue **p) +static inline int +interruptible_sleep_on_timeout (struct wait_queue **p, struct timespec *tsp) { pthread_cond_t **condp = (void *) p, *c; int isroot; struct wait_queue **next_wait; + error_t err; c = *condp; if (c == 0) @@ -111,12 +113,13 @@ interruptible_sleep_on (struct wait_queue **p) isroot = current->isroot; /* This is our context that needs switched. */ next_wait = current->next_wait; /* This too, for multiple schedule calls. */ current->next_wait = 0; - if (pthread_hurd_cond_wait_np (c, &global_lock)) + err = pthread_hurd_cond_timedwait_np(c, &global_lock, tsp); + if (err == EINTR) current->signal = 1; /* We got cancelled, mark it for later. */ current->isroot = isroot; /* Switch back to our context. */ current->next_wait = next_wait; + return (err == ETIMEDOUT); } -#define sleep_on interruptible_sleep_on static inline void wake_up_interruptible (struct wait_queue **p) @@ -146,7 +149,7 @@ static inline void schedule (void) { assert (current->next_wait); - interruptible_sleep_on (current->next_wait); + interruptible_sleep_on_timeout (current->next_wait, NULL); } static inline void @@ -171,7 +174,7 @@ schedule_timeout (long timeout) timer.function = process_schedule_timeout; add_timer (&timer); - interruptible_sleep_on (&sleep); + interruptible_sleep_on_timeout (&sleep, NULL); if (signal_pending (current)) { /* We were canceled. */ diff --git a/pfinet/io-ops.c b/pfinet/io-ops.c index 5f3b1e90..69bd93c5 100644 --- a/pfinet/io-ops.c +++ b/pfinet/io-ops.c @@ -251,14 +251,14 @@ S_io_clear_some_openmodes (struct sock_user *user, return 0; } -error_t -S_io_select (struct sock_user *user, - mach_port_t reply, - mach_msg_type_name_t reply_type, - int *select_type) +static error_t +io_select_common (struct sock_user *user, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec *tsp, int *select_type) { const int want = *select_type | POLLERR; - int avail; + int avail, timedout; int ret = 0; if (!user) @@ -281,9 +281,16 @@ S_io_select (struct sock_user *user, do { - /* Block until we are woken or cancelled. */ - interruptible_sleep_on (user->sock->sk->sleep); - if (signal_pending (current)) /* This means we were cancelled. */ + /* Block until we time out, are woken or cancelled. */ + timedout = interruptible_sleep_on_timeout (user->sock->sk->sleep, + tsp); + if (timedout) + { + __mutex_unlock (&global_lock); + *select_type = 0; + return 0; + } + else if (signal_pending (current)) /* This means we were cancelled. */ { pthread_mutex_unlock (&global_lock); return EINTR; @@ -306,6 +313,25 @@ S_io_select (struct sock_user *user, return ret; } +error_t +S_io_select (struct sock_user *user, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int *select_type) +{ + return io_select_common (user, reply, reply_type, NULL, select_type); +} + +error_t +S_io_select_timeout (struct sock_user *user, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec ts, + int *select_type) +{ + return io_select_common (user, reply, reply_type, &ts, select_type); +} + error_t S_io_stat (struct sock_user *user, struct stat *st) diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c index 5d2544b2..4a6f616a 100644 --- a/pfinet/tunnel.c +++ b/pfinet/tunnel.c @@ -462,13 +462,14 @@ trivfs_S_io_readable (struct trivfs_protid *cred, return the types that are then available. ID_TAG is returned as passed; it is just for the convenience of the user in matching up reply messages with specific requests sent. */ -error_t -trivfs_S_io_select (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - int *type) +static error_t +io_select_common (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec *tsp, int *type) { struct tunnel_device *tdev; + error_t err; if (!cred) return EOPNOTSUPP; @@ -497,15 +498,40 @@ trivfs_S_io_select (struct trivfs_protid *cred, ports_interrupt_self_on_port_death (cred, reply); tdev->read_blocked = 1; - if (pthread_hurd_cond_wait_np (&tdev->select_alert, &tdev->lock)) + err = pthread_hurd_cond_timedwait_np (&tdev->select_alert, &tdev->lock, + tsp); + if (err) { *type = 0; pthread_mutex_unlock (&tdev->lock); - return EINTR; + + if (err == ETIMEDOUT) + err = 0; + + return err; } } } +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int *type) +{ + return io_select_common (cred, reply, reply_type, NULL, type); +} + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec ts, + int *type) +{ + return io_select_common (cred, reply, reply_type, &ts, type); +} + /* Change current read/write offset */ error_t trivfs_S_io_seek (struct trivfs_protid *cred, diff --git a/pflocal/connq.c b/pflocal/connq.c index ab2b3a55..d88711e3 100644 --- a/pflocal/connq.c +++ b/pflocal/connq.c @@ -141,16 +141,19 @@ connq_destroy (struct connq *cq) /* ---------------------------------------------------------------- */ /* Return a connection request on CQ. If SOCK is NULL, the request is - left in the queue. If NOBLOCK is true, EWOULDBLOCK is returned - when there are no immediate connections available. */ + left in the queue. If TIMEOUT denotes a value of 0, EWOULDBLOCK is + returned when there are no immediate connections available. + Otherwise this value is used to limit the wait duration. If TIMEOUT + is NULL, the wait duration isn't bounded. */ error_t -connq_listen (struct connq *cq, int noblock, struct sock **sock) +connq_listen (struct connq *cq, struct timespec *tsp, struct sock **sock) { error_t err = 0; pthread_mutex_lock (&cq->lock); - if (noblock && cq->count == 0 && cq->num_connectors == 0) + if (tsp && tsp->tv_sec == 0 && tsp->tv_nsec == 0 && cq->count == 0 + && cq->num_connectors == 0) { pthread_mutex_unlock (&cq->lock); return EWOULDBLOCK; @@ -176,12 +179,14 @@ connq_listen (struct connq *cq, int noblock, struct sock **sock) pthread_cond_signal (&cq->connectors); do - if (pthread_hurd_cond_wait_np (&cq->listeners, &cq->lock)) + { + err = pthread_hurd_cond_timedwait_np (&cq->listeners, &cq->lock, tsp); + if (err) { cq->num_listeners--; - err = EINTR; goto out; } + } while (cq->count == 0); } diff --git a/pflocal/connq.h b/pflocal/connq.h index 9278d007..71583800 100644 --- a/pflocal/connq.h +++ b/pflocal/connq.h @@ -36,9 +36,12 @@ error_t connq_create (struct connq **cq); void connq_destroy (struct connq *cq); /* Return a connection request on CQ. If SOCK is NULL, the request is - left in the queue. If NOBLOCK is true, EWOULDBLOCK is returned - when there are no immediate connections available. */ -error_t connq_listen (struct connq *cq, int noblock, struct sock **sock); + left in the queue. If TIMEOUT denotes a value of 0, EWOULDBLOCK is + returned when there are no immediate connections available. + Otherwise this value is used to limit the wait duration. If TIMEOUT + is NULL, the wait duration isn't bounded. */ +error_t connq_listen (struct connq *cq, struct timespec *tsp, + struct sock **sock); /* Try to connect SOCK with the socket listening on CQ. If NOBLOCK is true, then return EWOULDBLOCK if there are no connections diff --git a/pflocal/io.c b/pflocal/io.c index 2301611e..0dec56e0 100644 --- a/pflocal/io.c +++ b/pflocal/io.c @@ -172,10 +172,10 @@ S_io_duplicate (struct sock_user *user, /* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG. Block until one of the indicated types of i/o can be done "quickly", and return the types that are then available. */ -error_t -S_io_select (struct sock_user *user, - mach_port_t reply, mach_msg_type_name_t reply_type, - int *select_type) +static error_t +io_select_common (struct sock_user *user, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec *tsp, int *select_type) { error_t err = 0; struct sock *sock; @@ -199,16 +199,24 @@ S_io_select (struct sock_user *user, if (*select_type & SELECT_READ) { + struct timespec noblock = {0, 0}; + /* Wait for a connect. Passing in NULL for SOCK means that the request won't be dequeued. */ - if (connq_listen (sock->listen_queue, 1, NULL) == 0) + if (connq_listen (sock->listen_queue, &noblock, NULL) == 0) /* We can satisfy this request immediately. */ return 0; else /* Gotta wait... */ { ports_interrupt_self_on_port_death (user, reply); - return connq_listen (sock->listen_queue, 0, NULL); + err = connq_listen (sock->listen_queue, tsp, NULL); + if (err == ETIMEDOUT) + { + *select_type = 0; + err = 0; + } + return err; } } } @@ -261,7 +269,7 @@ S_io_select (struct sock_user *user, /* Wait for something to change. */ { ports_interrupt_self_on_port_death (user, reply); - err = pipe_pair_select (read_pipe, write_pipe, select_type, 1); + err = pipe_pair_select (read_pipe, write_pipe, tsp, select_type, 1); } if (valid & SELECT_READ) @@ -272,6 +280,23 @@ S_io_select (struct sock_user *user, return err; } + +error_t +S_io_select (struct sock_user *user, + mach_port_t reply, mach_msg_type_name_t reply_type, + int *select_type) +{ + return io_select_common (user, reply, reply_type, NULL, select_type); +} + +error_t +S_io_select_timeout (struct sock_user *user, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec ts, + int *select_type) +{ + return io_select_common (user, reply, reply_type, &ts, select_type); +} /* Return the current status of the object. Not all the fields of the io_statuf_t are meaningful for all objects; however, the access and diff --git a/pflocal/socket.c b/pflocal/socket.c index 65da7487..ce1edd82 100644 --- a/pflocal/socket.c +++ b/pflocal/socket.c @@ -178,9 +178,11 @@ S_socket_accept (struct sock_user *user, err = ensure_connq (sock); if (!err) { + struct timespec noblock = {0, 0}; struct sock *peer_sock; - err = connq_listen (sock->listen_queue, sock->flags & SOCK_NONBLOCK, + err = connq_listen (sock->listen_queue, + (sock->flags & SOCK_NONBLOCK) ? &noblock : NULL, &peer_sock); if (!err) { diff --git a/storeio/io.c b/storeio/io.c index 508df77a..c79f6ba3 100644 --- a/storeio/io.c +++ b/storeio/io.c @@ -161,6 +161,15 @@ trivfs_S_io_select (struct trivfs_protid *cred, return 0; } +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec ts, + int *type) +{ + return trivfs_S_io_select (cred, reply, reply_type, type); +} + /* Truncate file. */ error_t trivfs_S_file_set_size (struct trivfs_protid *cred, diff --git a/term/ptyio.c b/term/ptyio.c index 0f5ddb01..44a215bb 100644 --- a/term/ptyio.c +++ b/term/ptyio.c @@ -459,9 +459,10 @@ pty_io_readable (size_t *amt) /* Validation has already been done by trivfs_S_io_select. */ error_t pty_io_select (struct trivfs_protid *cred, mach_port_t reply, - int *type) + struct timespec *tsp, int *type) { int avail = 0; + error_t err; if (*type == 0) return 0; @@ -488,13 +489,18 @@ pty_io_select (struct trivfs_protid *cred, mach_port_t reply, } ports_interrupt_self_on_port_death (cred, reply); - pty_read_blocked = 1; - if (pthread_hurd_cond_wait_np (&pty_select_wakeup, &global_lock)) + err = pthread_hurd_cond_timedwait_np (&pty_select_wakeup, &global_lock, + tsp); + if (err) { *type = 0; pthread_mutex_unlock (&global_lock); - return EINTR; + + if (err == ETIMEDOUT) + err = 0; + + return err; } } } diff --git a/term/term.h b/term/term.h index f154d4c0..df82b6c9 100644 --- a/term/term.h +++ b/term/term.h @@ -386,7 +386,8 @@ error_t pty_io_write (struct trivfs_protid *, char *, error_t pty_io_read (struct trivfs_protid *, char **, mach_msg_type_number_t *, mach_msg_type_number_t); error_t pty_io_readable (size_t *); -error_t pty_io_select (struct trivfs_protid *, mach_port_t, int *); +error_t pty_io_select (struct trivfs_protid *, mach_port_t, + struct timespec *, int *); error_t pty_open_hook (struct trivfs_control *, struct iouser *, int); error_t pty_po_create_hook (struct trivfs_peropen *); error_t pty_po_destroy_hook (struct trivfs_peropen *); diff --git a/term/users.c b/term/users.c index 9fc1dc23..529693e8 100644 --- a/term/users.c +++ b/term/users.c @@ -2002,17 +2002,17 @@ trivfs_S_io_async (struct trivfs_protid *cred, return 0; } -error_t -trivfs_S_io_select (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - int *type) +static error_t +io_select_common (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec *tsp, int *type) { if (!cred) return EOPNOTSUPP; if (cred->pi.class == pty_class) - return pty_io_select (cred, reply, type); + return pty_io_select (cred, reply, tsp, type); if ((cred->po->openmodes & O_READ) == 0) *type &= ~SELECT_READ; @@ -2024,6 +2024,8 @@ trivfs_S_io_select (struct trivfs_protid *cred, while (1) { int available = 0; + error_t err = 0; + if ((*type & SELECT_READ) && qsize (inputq)) available |= SELECT_READ; if ((*type & SELECT_WRITE) && qavail (outputq)) @@ -2032,16 +2034,41 @@ trivfs_S_io_select (struct trivfs_protid *cred, if (available == 0) { ports_interrupt_self_on_port_death (cred, reply); - if (pthread_hurd_cond_wait_np (&select_alert, &global_lock) == 0) + err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, + tsp); + if (!err) continue; } *type = available; pthread_mutex_unlock (&global_lock); - return available ? 0 : EINTR; + + if (err == ETIMEDOUT) + err = 0; + + return err; } } +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int *type) +{ + return io_select_common (cred, reply, reply_type, NULL, type); +} + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec ts, + int *type) +{ + return io_select_common (cred, reply, reply_type, &ts, type); +} + kern_return_t trivfs_S_io_map (struct trivfs_protid *cred, mach_port_t reply, diff --git a/trans/fifo.c b/trans/fifo.c index b40a50d1..e6fbd0e9 100644 --- a/trans/fifo.c +++ b/trans/fifo.c @@ -405,10 +405,10 @@ trivfs_S_io_seek (struct trivfs_protid *cred, return the types that are then available. ID_TAG is returned as passed; it is just for the convenience of the user in matching up reply messages with specific requests sent. */ -error_t -trivfs_S_io_select (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int *select_type) +static error_t +io_select_common (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec *tsp, int *select_type) { struct pipe *pipe; error_t err = 0; @@ -466,11 +466,28 @@ trivfs_S_io_select (struct trivfs_protid *cred, /* Wait for something to change. */ { ports_interrupt_self_on_port_death (cred, reply); - err = pipe_pair_select (pipe, pipe, select_type, 1); + err = pipe_pair_select (pipe, pipe, tsp, select_type, 1); } return err; } + +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + int *select_type) +{ + return io_select_common (cred, reply, reply_type, NULL, select_type); +} + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec ts, + int *select_type) +{ + return io_select_common (cred, reply, reply_type, &ts, select_type); +} /* ---------------------------------------------------------------- */ diff --git a/trans/firmlink.c b/trans/firmlink.c index 087e19d0..9c063c04 100644 --- a/trans/firmlink.c +++ b/trans/firmlink.c @@ -275,3 +275,12 @@ trivfs_S_io_select (struct trivfs_protid *cred, { return EOPNOTSUPP; } + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec ts, + int *type) +{ + return trivfs_S_io_select (cred, reply, reply_type, type); +} diff --git a/trans/new-fifo.c b/trans/new-fifo.c index 5cc44b58..dc3cc79e 100644 --- a/trans/new-fifo.c +++ b/trans/new-fifo.c @@ -591,10 +591,10 @@ trivfs_S_io_seek (struct trivfs_protid *cred, return the types that are then available. ID_TAG is returned as passed; it is just for the convenience of the user in matching up reply messages with specific requests sent. */ -error_t -trivfs_S_io_select (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int *select_type) +static error_t +io_select_common (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec *tsp, int *select_type) { struct pipe *pipe; error_t err = 0; @@ -652,11 +652,28 @@ trivfs_S_io_select (struct trivfs_protid *cred, /* Wait for something to change. */ { ports_interrupt_self_on_port_death (cred, reply); - err = pipe_pair_select (pipe, pipe, select_type, 1); + err = pipe_pair_select (pipe, pipe, tsp, select_type, 1); } return err; } + +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + int *select_type) +{ + return io_select_common (cred, reply, reply_type, NULL, select_type); +} + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec ts, + int *select_type) +{ + return io_select_common (cred, reply, reply_type, &ts, select_type); +} /* ---------------------------------------------------------------- */ diff --git a/trans/null.c b/trans/null.c index 9673a758..1f985b39 100644 --- a/trans/null.c +++ b/trans/null.c @@ -203,6 +203,15 @@ trivfs_S_io_select (struct trivfs_protid *cred, *type &= ~SELECT_URG; return 0; } + +kern_return_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t replytype, + struct timespec ts, + int *type) +{ + return trivfs_S_io_select (cred, reply, replytype, type); +} /* Write data to an IO object. If offset is -1, write at the object maintained file pointer. If the object is not seekable, offset is diff --git a/trans/streamio.c b/trans/streamio.c index 60b5d59c..8ff3dc63 100644 --- a/trans/streamio.c +++ b/trans/streamio.c @@ -538,12 +538,14 @@ trivfs_S_io_seek (struct trivfs_protid *cred, return ESPIPE; } -error_t -trivfs_S_io_select (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int *type) +static error_t +io_select_common (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec *tsp, + int *type) { int available; + error_t err; if (!cred) return EOPNOTSUPP; @@ -583,15 +585,37 @@ trivfs_S_io_select (struct trivfs_protid *cred, } ports_interrupt_self_on_port_death (cred, reply); - if (pthread_hurd_cond_wait_np (&select_alert, &global_lock)) + err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp); + if (err) { *type = 0; pthread_mutex_unlock (&global_lock); - return EINTR; + + if (err == ETIMEDOUT) + err = 0; + + return err; } } } +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + int *type) +{ + return io_select_common (cred, reply, reply_type, NULL, type); +} + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + struct timespec ts, + int *type) +{ + return io_select_common (cred, reply, reply_type, &ts, type); +} + error_t trivfs_S_file_set_size (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t reply_type, -- cgit v1.2.3