dumping ground for random patches and texts
 help / color / mirror / Atom feed
* [PATCH] io.c: IO.copy_stream uses poll on Linux
@ 2015-07-20 19:58 Eric Wong
  0 siblings, 0 replies; 2+ messages in thread
From: Eric Wong @ 2015-07-20 19:58 UTC (permalink / raw)
  To: spew

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.  So
favor using poll in case IO.copy_stream encounteres 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 | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)

diff --git a/io.c b/io.c
index 3616d0a..b776f76 100644
--- a/io.c
+++ b/io.c
@@ -10087,6 +10087,48 @@ 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"
+#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, NULL);
+}
+
+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 +10156,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 +10164,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;
     }
-- 
EW


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH] io.c: IO.copy_stream uses poll on Linux
@ 2015-07-20 20:07 Eric Wong
  0 siblings, 0 replies; 2+ messages in thread
From: Eric Wong @ 2015-07-20 20:07 UTC (permalink / raw)
  To: spew

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.  So
favor using poll in case IO.copy_stream encounteres 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 | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)

diff --git a/io.c b/io.c
index 3616d0a..b776f76 100644
--- a/io.c
+++ b/io.c
@@ -10087,6 +10087,48 @@ 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"
+#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, NULL);
+}
+
+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 +10156,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 +10164,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;
     }
-- 
EW


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-07-20 20:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-20 19:58 [PATCH] io.c: IO.copy_stream uses poll on Linux Eric Wong
  -- strict thread matches above, loose matches on Subject: below --
2015-07-20 20:07 Eric Wong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).