merge revision(s) 611e711085c7e3984555a79626d025c8b876eced,a9c5c2d614f30a616970245fef3e7ffc151e2ecf: [Backport #17527]

Test incorrect behaviour of `rb_io_wait_readable/writable`.

	---
	 test/fiber/test_io.rb | 35 +++++++++++++++++++++++++++++++++++
	 1 file changed, 35 insertions(+)

	Check errno before invoking scheduler in
	 `rb_io_wait_readable/writable`.

	See <https://bugs.ruby-lang.org/issues/17527> for more details.
	---
	 io.c | 66 +++++++++++++++++++++++++++++++++++-------------------------------
	 1 file changed, 35 insertions(+), 31 deletions(-)
This commit is contained in:
nagachika 2021-04-24 14:04:04 +09:00
parent 5a9b5b8e95
commit 13f93ad16d
3 changed files with 74 additions and 33 deletions

68
io.c
View file

@ -1306,71 +1306,77 @@ rb_io_from_fd(int fd)
int int
rb_io_wait_readable(int f) rb_io_wait_readable(int f)
{ {
VALUE scheduler = rb_scheduler_current(); VALUE scheduler;
if (scheduler != Qnil) {
return RTEST(
rb_scheduler_io_wait_readable(scheduler, rb_io_from_fd(f))
);
}
io_fd_check_closed(f); io_fd_check_closed(f);
scheduler = rb_scheduler_current();
switch (errno) { switch (errno) {
case EINTR: case EINTR:
#if defined(ERESTART) #if defined(ERESTART)
case ERESTART: case ERESTART:
#endif #endif
rb_thread_check_ints(); rb_thread_check_ints();
return TRUE; return TRUE;
case EAGAIN: case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK: case EWOULDBLOCK:
#endif #endif
rb_thread_wait_fd(f); if (scheduler != Qnil) {
return TRUE; return RTEST(
rb_scheduler_io_wait_readable(scheduler, rb_io_from_fd(f))
);
} else {
rb_thread_wait_fd(f);
}
return TRUE;
default: default:
return FALSE; return FALSE;
} }
} }
int int
rb_io_wait_writable(int f) rb_io_wait_writable(int f)
{ {
VALUE scheduler = rb_scheduler_current(); VALUE scheduler;
if (scheduler != Qnil) {
return RTEST(
rb_scheduler_io_wait_writable(scheduler, rb_io_from_fd(f))
);
}
io_fd_check_closed(f); io_fd_check_closed(f);
scheduler = rb_scheduler_current();
switch (errno) { switch (errno) {
case EINTR: case EINTR:
#if defined(ERESTART) #if defined(ERESTART)
case ERESTART: case ERESTART:
#endif #endif
/* /*
* In old Linux, several special files under /proc and /sys don't handle * In old Linux, several special files under /proc and /sys don't handle
* select properly. Thus we need avoid to call if don't use O_NONBLOCK. * select properly. Thus we need avoid to call if don't use O_NONBLOCK.
* Otherwise, we face nasty hang up. Sigh. * Otherwise, we face nasty hang up. Sigh.
* e.g. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8 * e.g. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8
* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8 * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8
* In EINTR case, we only need to call RUBY_VM_CHECK_INTS_BLOCKING(). * In EINTR case, we only need to call RUBY_VM_CHECK_INTS_BLOCKING().
* Then rb_thread_check_ints() is enough. * Then rb_thread_check_ints() is enough.
*/ */
rb_thread_check_ints(); rb_thread_check_ints();
return TRUE; return TRUE;
case EAGAIN: case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK: case EWOULDBLOCK:
#endif #endif
rb_thread_fd_writable(f); if (scheduler != Qnil) {
return TRUE; return RTEST(
rb_scheduler_io_wait_writable(scheduler, rb_io_from_fd(f))
);
} else {
rb_thread_fd_writable(f);
}
return TRUE;
default: default:
return FALSE; return FALSE;
} }
} }

View file

@ -62,4 +62,39 @@ class TestFiberIO < Test::Unit::TestCase
end end
end.each(&:join) end.each(&:join)
end end
def test_epipe_on_read
skip "UNIXSocket is not defined!" unless defined?(UNIXSocket)
i, o = UNIXSocket.pair
unless i.nonblock? && o.nonblock?
i.close
o.close
skip "I/O is not non-blocking!"
end
error = nil
thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
begin
i.close
o.write(MESSAGE)
rescue => error
# Saved into error.
end
end
end
thread.join
i.close
o.close
assert_kind_of Errno::EPIPE, error
end
end end

View file

@ -12,11 +12,11 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 2 #define RUBY_VERSION_TEENY 2
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
#define RUBY_PATCHLEVEL 71 #define RUBY_PATCHLEVEL 72
#define RUBY_RELEASE_YEAR 2021 #define RUBY_RELEASE_YEAR 2021
#define RUBY_RELEASE_MONTH 4 #define RUBY_RELEASE_MONTH 4
#define RUBY_RELEASE_DAY 18 #define RUBY_RELEASE_DAY 24
#include "ruby/version.h" #include "ruby/version.h"