summaryrefslogtreecommitdiff
path: root/io.c
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-07-20 20:33:50 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-07-20 20:33:50 +0000
commitf7c3b6ff720e6084d578fe9e1a6c62bd80321d63 (patch)
tree3cc5b2607e85c5b02842cd087034593e6ff30a51 /io.c
parent8754f619d51a67fc4df255c189c8ff744433dd6e (diff)
io.c: IO.copy_stream uses poll on Linux
poll and ppoll have a superior API which doesn't require the kernel to scan a potentially large bitmap to find a high-numbered FD [ruby-core:35572]. So favor using poll in case IO.copy_stream encounters a non-blocking FD. We cannot reliably use poll on most OSes, because file types (e.g. FIFOs) which work with select may not work with poll. Fortunately, Linux uses a common notification mechanism between all select/poll/epoll variants, so all file types are equally supported between the notification mechanisms. Verified by watching strace on the following scripts: *** maygvl_copy_stream_wait_read *** require 'io/nonblock' r, w = IO.pipe r.nonblock = true IO.copy_stream(r, "/dev/null") *** nogvl_copy_stream_wait_write *** require 'io/nonblock' r, w = IO.pipe w.nonblock = true IO.copy_stream("/dev/zero", w) * io.c (nogvl_wait_for_single_fd): new function for Linux (maygvl_copy_stream_wait_read): Linux-specific version (nogvl_copy_stream_wait_write): use nogvl_wait_for_single_fd [ruby-core:70051] [Feature #11377] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51305 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'io.c')
-rw-r--r--io.c50
1 files changed, 49 insertions, 1 deletions
diff --git a/io.c b/io.c
index 3616d0a..24fe983 100644
--- a/io.c
+++ b/io.c
@@ -10087,6 +10087,49 @@ maygvl_copy_stream_continue_p(int has_gvl, struct copy_stream_struct *stp)
return FALSE;
}
+/* non-Linux poll may not work on all FDs */
+#if defined(HAVE_POLL) && defined(__linux__)
+# define USE_POLL 1
+# define IOWAIT_SYSCALL "poll"
+#else
+# define IOWAIT_SYSCALL "select"
+# define USE_POLL 0
+#endif
+
+#if USE_POLL
+static int
+nogvl_wait_for_single_fd(int fd, short events)
+{
+ struct pollfd fds;
+
+ fds.fd = fd;
+ fds.events = events;
+
+ return poll(&fds, 1, 0);
+}
+
+static int
+maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
+{
+ int ret;
+
+ do {
+ if (has_gvl) {
+ ret = rb_wait_for_single_fd(stp->src_fd, RB_WAITFD_IN, NULL);
+ }
+ else {
+ ret = nogvl_wait_for_single_fd(stp->src_fd, POLLIN);
+ }
+ } while (ret == -1 && maygvl_copy_stream_continue_p(has_gvl, stp));
+
+ if (ret == -1) {
+ stp->syserr = "poll";
+ stp->error_no = errno;
+ return -1;
+ }
+ return 0;
+}
+#else /* !USE_POLL */
static int
maygvl_select(int has_gvl, int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
{
@@ -10114,6 +10157,7 @@ maygvl_copy_stream_wait_read(int has_gvl, struct copy_stream_struct *stp)
}
return 0;
}
+#endif /* !USE_POLL */
static int
nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
@@ -10121,13 +10165,17 @@ nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
int ret;
do {
+#if USE_POLL
+ ret = nogvl_wait_for_single_fd(stp->dst_fd, POLLOUT);
+#else
rb_fd_zero(&stp->fds);
rb_fd_set(stp->dst_fd, &stp->fds);
ret = rb_fd_select(rb_fd_max(&stp->fds), NULL, &stp->fds, NULL, NULL);
+#endif
} while (ret == -1 && maygvl_copy_stream_continue_p(0, stp));
if (ret == -1) {
- stp->syserr = "select";
+ stp->syserr = IOWAIT_SYSCALL;
stp->error_no = errno;
return -1;
}