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
This commit is contained in:
normal 2015-07-20 20:33:50 +00:00
Родитель 8754f619d5
Коммит f7c3b6ff72
2 изменённых файлов: 56 добавлений и 1 удалений

Просмотреть файл

@ -1,3 +1,10 @@
Tue Jul 21 05:20:21 2015 Eric Wong <e@80x24.org>
* 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]
Mon Jul 20 15:04:30 2015 Eric Wong <e@80x24.org>
* parse.y (parser_initialize): avoid redundant zero-ing

50
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;
}