diff --git a/ext/io/event/selector/uring.c b/ext/io/event/selector/uring.c index f40c92b0..c61520a5 100644 --- a/ext/io/event/selector/uring.c +++ b/ext/io/event/selector/uring.c @@ -707,6 +707,20 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b off_t from = io_seekable(descriptor); size_t maximum_size = size - offset; + + // Are we performing a non-blocking read? + if (!length) { + // If the (maximum) length is zero, that indicates we just want to read whatever is available without blocking. + // If we schedule this read into the URing, it will block until data is available, rather than returning immediately. + int state = IO_Event_Selector_nonblock_set(descriptor); + + int result = read(descriptor, (char*)base+offset, maximum_size); + int error = errno; + + IO_Event_Selector_nonblock_restore(descriptor, state); + return rb_fiber_scheduler_io_result(result, errno); + } + while (maximum_size) { int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from); @@ -1093,7 +1107,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) { } } - if (DEBUG && completed > 0) fprintf(stderr, "select_process_completions(completed=%d)\n", completed); + if (DEBUG && completed > 0) fprintf(stderr, "select_process_completions: completed=%d\n", completed); return completed; } diff --git a/test/io/event/selector/buffered_io.rb b/test/io/event/selector/buffered_io.rb index 68867ec6..f03d1447 100644 --- a/test/io/event/selector/buffered_io.rb +++ b/test/io/event/selector/buffered_io.rb @@ -77,6 +77,46 @@ writer.transfer selector.select(0) end + + it "can perform non-blocking read" do + skip_if_ruby_platform(/mswin|mingw|cygwin/) + + buffer = IO::Buffer.new(64) + result = nil + + output.puts "Hello World\n" + output.close + + reader = Fiber.new do + result = selector.io_read(Fiber.current, input, buffer, 0) + end + + reader.transfer + selector.select(0) + + expect(buffer.get_string(0, 12)).to be == "Hello World\n" + end + + # Whether the given error code indicates that the operation should be retried. + def be_again? + (be == -Errno::EAGAIN::Errno).or(be == -Errno::EWOULDBLOCK::Errno) + end + + it "can perform non-blocking read with empty input" do + skip_if_ruby_platform(/mswin|mingw|cygwin/) + + buffer = IO::Buffer.new(64) + result = nil + + reader = Fiber.new do + result = selector.io_read(Fiber.current, input, buffer, 0) + end + + reader.transfer + selector.select(0) + + expect(result).to be_again? + end end end