Skip to content

Commit

Permalink
Fix handling of IO#timeout.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Jan 1, 2024
1 parent 087f78f commit e8a81b0
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 3 deletions.
28 changes: 26 additions & 2 deletions lib/async/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ def io_wait(io, events, timeout = nil)
timer = @timers.after(timeout) do
fiber.transfer
end
elsif timeout = io.timeout
timer = @timers.after(timeout) do
fiber.raise(::IO::TimeoutError, "Timeout while waiting for IO to become ready!")
end
end

return @selector.io_wait(fiber, io, events)
Expand All @@ -182,12 +186,32 @@ def io_wait(io, events, timeout = nil)

if ::IO::Event::Support.buffer?
def io_read(io, buffer, length, offset = 0)
@selector.io_read(Fiber.current, io, buffer, length, offset)
fiber = Fiber.current

if timeout = io.timeout
timer = @timers.after(timeout) do
fiber.raise(::IO::TimeoutError, "execution expired")
end
end

@selector.io_read(fiber, io, buffer, length, offset)
ensure
timer&.cancel
end

if RUBY_ENGINE != "ruby" || RUBY_VERSION >= "3.3.0"
def io_write(io, buffer, length, offset = 0)
@selector.io_write(Fiber.current, io, buffer, length, offset)
fiber = Fiber.current

if timeout = io.timeout
timer = @timers.after(timeout) do
fiber.raise(::IO::TimeoutError, "execution expired")
end
end

@selector.io_write(fiber, io, buffer, length, offset)
ensure
timer&.cancel
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion test/async/wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def after
it "can timeout if no event occurs" do
expect do
output.wait_readable(0.001)
end.to raise_exception(Async::TimeoutError)
end.to raise_exception(IO::TimeoutError)
end

it "can wait for readability in sequential tasks" do
Expand Down
26 changes: 26 additions & 0 deletions test/io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,31 @@
input.close
output.close
end

it "can read with timeout" do
input, output = IO.pipe
input.timeout = 0.001

expect do
line = input.gets
end.to raise_exception(::IO::TimeoutError)
end

it "can wait readable with default timeout" do
input, output = IO.pipe
input.timeout = 0.001

expect do
# This behaviour is not consistent with non-fiber scheduler IO.
# However, this is the best we can do without fixing CRuby.
input.wait_readable
end.to raise_exception(::IO::TimeoutError)
end

it "can wait readable with 0 timeout" do
input, output = IO.pipe

expect(input.wait_readable(0)).to be_nil
end
end
end

0 comments on commit e8a81b0

Please sign in to comment.