Better handling of timeout in rb_io_maybe_wait_*. (#9531)

This commit is contained in:
Samuel Williams 2024-10-04 19:36:06 +13:00 committed by GitHub
parent 96d69d2df2
commit c878843b2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2024-10-04 06:36:25 +00:00
Merged-By: ioquatix <samuel@codeotaku.com>
3 changed files with 30 additions and 15 deletions

View File

@ -975,6 +975,7 @@ VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout);
* @exception rb_eRangeError `timeout` is out of range. * @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason. * @exception rb_eSystemCallError `select(2)` failed for some reason.
* @retval RUBY_Qfalse Operation timed out. * @retval RUBY_Qfalse Operation timed out.
* @retval RUBY_Qnil Operation failed for some other reason (errno).
* @retval Otherwise Actual events reached. * @retval Otherwise Actual events reached.
* *
*/ */
@ -982,8 +983,11 @@ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout);
/** /**
* Blocks until the passed IO is ready for reading, if that makes sense for the * Blocks until the passed IO is ready for reading, if that makes sense for the
* passed errno. This is a special case of rb_io_maybe_wait() that only * passed errno. This is a special case of rb_io_maybe_wait() that is
* concerns for reading. * only concerned with reading and handles the timeout.
*
* If you do not want the default timeout handling, consider using
* ::rb_io_maybe_wait directly.
* *
* @param[in] error System errno. * @param[in] error System errno.
* @param[in] io An IO object to wait. * @param[in] io An IO object to wait.
@ -991,15 +995,18 @@ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout);
* @exception rb_eIOError `io` is not open. * @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range. * @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason. * @exception rb_eSystemCallError `select(2)` failed for some reason.
* @retval 0 Operation timed out. * @exception rb_eIOTimeoutError The wait operation timed out.
* @retval Otherwise Always returns ::RUBY_IO_READABLE. * @retval Otherwise Always returns ::RUBY_IO_READABLE.
*/ */
int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout); int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
/** /**
* Blocks until the passed IO is ready for writing, if that makes sense for the * Blocks until the passed IO is ready for writing, if that makes sense for the
* passed errno. This is a special case of rb_io_maybe_wait() that only * passed errno. This is a special case of rb_io_maybe_wait() that is
* concernsfor writing. * only concerned with writing, and handles the timeout.
*
* If you do not want the default timeout handling, consider using
* ::rb_io_maybe_wait directly.
* *
* @param[in] error System errno. * @param[in] error System errno.
* @param[in] io An IO object to wait. * @param[in] io An IO object to wait.
@ -1007,7 +1014,7 @@ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
* @exception rb_eIOError `io` is not open. * @exception rb_eIOError `io` is not open.
* @exception rb_eRangeError `timeout` is out of range. * @exception rb_eRangeError `timeout` is out of range.
* @exception rb_eSystemCallError `select(2)` failed for some reason. * @exception rb_eSystemCallError `select(2)` failed for some reason.
* @retval 0 Operation timed out. * @exception rb_eIOTimeoutError The wait operation timed out.
* @retval Otherwise Always returns ::RUBY_IO_WRITABLE. * @retval Otherwise Always returns ::RUBY_IO_WRITABLE.
*/ */
int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout); int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout);

14
io.c
View File

@ -1623,7 +1623,7 @@ rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout)
default: default:
// Non-specific error, no event is ready: // Non-specific error, no event is ready:
return Qfalse; return Qnil;
} }
} }
@ -1635,9 +1635,11 @@ rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout)
if (RTEST(result)) { if (RTEST(result)) {
return RB_NUM2INT(result); return RB_NUM2INT(result);
} }
else { else if (result == RUBY_Qfalse) {
return 0; rb_raise(rb_eIOTimeoutError, "Timed out waiting for IO to become readable!");
} }
return 0;
} }
int int
@ -1648,9 +1650,11 @@ rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout)
if (RTEST(result)) { if (RTEST(result)) {
return RB_NUM2INT(result); return RB_NUM2INT(result);
} }
else { else if (result == RUBY_Qfalse) {
return 0; rb_raise(rb_eIOTimeoutError, "Timed out waiting for IO to become writable!");
} }
return 0;
} }
static void static void

View File

@ -458,10 +458,6 @@ describe "C-API IO function" do
@o.rb_io_maybe_wait(Errno::EINTR::Errno, @w_io, IO::WRITABLE, nil).should == IO::WRITABLE @o.rb_io_maybe_wait(Errno::EINTR::Errno, @w_io, IO::WRITABLE, nil).should == IO::WRITABLE
end end
it "returns false if there is no error condition" do
@o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == false
end
it "raises an IOError if the IO is closed" do it "raises an IOError if the IO is closed" do
@w_io.close @w_io.close
-> { @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil) }.should raise_error(IOError, "closed stream") -> { @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil) }.should raise_error(IOError, "closed stream")
@ -521,6 +517,14 @@ describe "C-API IO function" do
end end
end end
end end
ruby_version_is "3.4" do
describe "rb_io_maybe_wait" do
it "returns nil if there is no error condition" do
@o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == nil
end
end
end
end end
describe "rb_fd_fix_cloexec" do describe "rb_fd_fix_cloexec" do