io.c: wait on FD readability w/o GVL reacquisition

Since non-blocking I/O is the default after [Bug #14968],
we will hit it more often and cause more acquisition/release
of GVL to wait on single FD.

This also lets us avoid touching the temporal string locking
as much and lets us clean up some test changes made for
[Bug #14968]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65948 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2018-11-24 08:23:26 +00:00
parent 8c8e72fb8b
commit c0e20037f3
2 changed files with 47 additions and 38 deletions

73
io.c
View File

@ -944,6 +944,7 @@ io_alloc(VALUE klass)
struct io_internal_read_struct { struct io_internal_read_struct {
int fd; int fd;
int nonblock;
void *buf; void *buf;
size_t capa; size_t capa;
}; };
@ -962,11 +963,24 @@ struct io_internal_writev_struct {
}; };
#endif #endif
static int nogvl_wait_for_single_fd(int fd, short events);
static VALUE static VALUE
internal_read_func(void *ptr) internal_read_func(void *ptr)
{ {
struct io_internal_read_struct *iis = ptr; struct io_internal_read_struct *iis = ptr;
return read(iis->fd, iis->buf, iis->capa); ssize_t r;
retry:
r = read(iis->fd, iis->buf, iis->capa);
if (r < 0 && !iis->nonblock) {
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK) {
if (nogvl_wait_for_single_fd(iis->fd, RB_WAITFD_IN) != -1) {
goto retry;
}
errno = e;
}
}
return r;
} }
#if defined __APPLE__ #if defined __APPLE__
@ -1004,7 +1018,9 @@ static ssize_t
rb_read_internal(int fd, void *buf, size_t count) rb_read_internal(int fd, void *buf, size_t count)
{ {
struct io_internal_read_struct iis; struct io_internal_read_struct iis;
iis.fd = fd; iis.fd = fd;
iis.nonblock = 0;
iis.buf = buf; iis.buf = buf;
iis.capa = count; iis.capa = count;
@ -2735,18 +2751,18 @@ rb_io_set_nonblock(rb_io_t *fptr)
} }
} }
struct read_internal_arg {
int fd;
char *str_ptr;
long len;
};
static VALUE static VALUE
read_internal_call(VALUE arg) read_internal_call(VALUE arg)
{ {
struct read_internal_arg *p = (struct read_internal_arg *)arg; struct io_internal_read_struct *iis = (struct io_internal_read_struct *)arg;
p->len = rb_read_internal(p->fd, p->str_ptr, p->len);
return Qundef; return rb_thread_io_blocking_region(internal_read_func, iis, iis->fd);
}
static long
read_internal_locktmp(VALUE str, struct io_internal_read_struct *iis)
{
return (long)rb_str_locktmp_ensure(str, read_internal_call, (VALUE)iis);
} }
static int static int
@ -2765,7 +2781,7 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
rb_io_t *fptr; rb_io_t *fptr;
VALUE length, str; VALUE length, str;
long n, len; long n, len;
struct read_internal_arg arg; struct io_internal_read_struct iis;
int shrinkable; int shrinkable;
rb_scan_args(argc, argv, "11", &length, &str); rb_scan_args(argc, argv, "11", &length, &str);
@ -2792,11 +2808,11 @@ io_getpartial(int argc, VALUE *argv, VALUE io, VALUE opts, int nonblock)
rb_io_set_nonblock(fptr); rb_io_set_nonblock(fptr);
} }
io_setstrbuf(&str, len); io_setstrbuf(&str, len);
arg.fd = fptr->fd; iis.fd = fptr->fd;
arg.str_ptr = RSTRING_PTR(str); iis.nonblock = nonblock;
arg.len = len; iis.buf = RSTRING_PTR(str);
rb_str_locktmp_ensure(str, read_internal_call, (VALUE)&arg); iis.capa = len;
n = arg.len; n = read_internal_locktmp(str, &iis);
if (n < 0) { if (n < 0) {
int e = errno; int e = errno;
if (!nonblock && fptr_wait_readable(fptr)) if (!nonblock && fptr_wait_readable(fptr))
@ -2906,7 +2922,7 @@ io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
{ {
rb_io_t *fptr; rb_io_t *fptr;
long n, len; long n, len;
struct read_internal_arg arg; struct io_internal_read_struct iis;
int shrinkable; int shrinkable;
if ((len = NUM2LONG(length)) < 0) { if ((len = NUM2LONG(length)) < 0) {
@ -2925,11 +2941,11 @@ io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex)
if (n <= 0) { if (n <= 0) {
rb_io_set_nonblock(fptr); rb_io_set_nonblock(fptr);
shrinkable |= io_setstrbuf(&str, len); shrinkable |= io_setstrbuf(&str, len);
arg.fd = fptr->fd; iis.fd = fptr->fd;
arg.str_ptr = RSTRING_PTR(str); iis.nonblock = 1;
arg.len = len; iis.buf = RSTRING_PTR(str);
rb_str_locktmp_ensure(str, read_internal_call, (VALUE)&arg); iis.capa = len;
n = arg.len; n = read_internal_locktmp(str, &iis);
if (n < 0) { if (n < 0) {
int e = errno; int e = errno;
if ((e == EWOULDBLOCK || e == EAGAIN)) { if ((e == EWOULDBLOCK || e == EAGAIN)) {
@ -5094,7 +5110,7 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
VALUE len, str; VALUE len, str;
rb_io_t *fptr; rb_io_t *fptr;
long n, ilen; long n, ilen;
struct read_internal_arg arg; struct io_internal_read_struct iis;
int shrinkable; int shrinkable;
rb_scan_args(argc, argv, "11", &len, &str); rb_scan_args(argc, argv, "11", &len, &str);
@ -5122,12 +5138,11 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
rb_io_check_closed(fptr); rb_io_check_closed(fptr);
io_setstrbuf(&str, ilen); io_setstrbuf(&str, ilen);
rb_str_locktmp(str); iis.fd = fptr->fd;
arg.fd = fptr->fd; iis.nonblock = 1; /* for historical reasons, maybe (see above) */
arg.str_ptr = RSTRING_PTR(str); iis.buf = RSTRING_PTR(str);
arg.len = ilen; iis.capa = ilen;
rb_ensure(read_internal_call, (VALUE)&arg, rb_str_unlocktmp, str); n = read_internal_locktmp(str, &iis);
n = arg.len;
if (n == -1) { if (n == -1) {
rb_sys_fail_path(fptr->pathv); rb_sys_fail_path(fptr->pathv);

View File

@ -1360,7 +1360,6 @@ class TestIO < Test::Unit::TestCase
def test_readpartial_lock def test_readpartial_lock
with_pipe do |r, w| with_pipe do |r, w|
s = "" s = ""
r.nonblock = false if have_nonblock?
t = Thread.new { r.readpartial(5, s) } t = Thread.new { r.readpartial(5, s) }
Thread.pass until t.stop? Thread.pass until t.stop?
assert_raise(RuntimeError) { s.clear } assert_raise(RuntimeError) { s.clear }
@ -3256,17 +3255,12 @@ __END__
assert_equal 100, buf.bytesize assert_equal 100, buf.bytesize
begin msg = /can't modify string; temporarily locked/
assert_raise_with_message(RuntimeError, msg) do
buf.replace("") buf.replace("")
rescue RuntimeError => e end
assert_match(/can't modify string; temporarily locked/, e.message)
Thread.pass
end until buf.empty?
assert_empty(buf, bug6099)
assert_predicate(th, :alive?) assert_predicate(th, :alive?)
w.write(data) w.write(data)
Thread.pass while th.alive?
th.join th.join
end end
assert_equal(data, buf, bug6099) assert_equal(data, buf, bug6099)