diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-07-20 20:33:50 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-07-20 20:33:50 +0000 |
commit | f7c3b6ff720e6084d578fe9e1a6c62bd80321d63 (patch) | |
tree | 3cc5b2607e85c5b02842cd087034593e6ff30a51 /io.c | |
parent | 8754f619d51a67fc4df255c189c8ff744433dd6e (diff) | |
download | ruby-f7c3b6ff720e6084d578fe9e1a6c62bd80321d63.tar.gz |
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.c | 50 |
1 files changed, 49 insertions, 1 deletions
@@ -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; } |