Bug #21918
openfiber scheduler: fiber interrupt triggers for IOs that haven't been closed
Description
I've been chasing an issue, which I believe is to with the recent introduction of the fiber_interrupt hook in ruby 4. As a reproduction I have the following snippet:
require "socket"
require_relative "test/fiber/scheduler" # use the one defined here: https://github.com/ruby/ruby/blob/master/test/fiber/scheduler.rb
udp = UDPSocket.new(Socket::AF_INET)
tcp = Socket.new(Socket::AF_INET, :STREAM, 0)
tcp.connect(Socket.sockaddr_in(80, "nghttp2"))
t = Thread.start do
Thread.current.abort_on_exception = true
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
5.times do
Fiber.schedule do
begin
puts "#{Fiber.current.object_id} -> wait udp"
udp.to_io.wait_readable(2)
rescue IOError => e
puts "#{Fiber.current.object_id} -> udp io error: #{e}"
end
begin
puts "#{Fiber.current.object_id} -> closing udp"
udp.close
puts "#{Fiber.current.object_id} -> closed udp"
puts "#{Fiber.current.object_id} -> wait tcp"
tcp.to_io.wait_readable(2)
puts "#{Fiber.current.object_id} -> done"
rescue => e
puts "#{Fiber.current.object_id} -> tcp io error: #{e}"
else
puts "#{Fiber.current.object_id} -> closing tcp"
tcp.close
end
end
end
end
t.join
This produces the following output to me:
1472 -> wait udp
1488 -> wait udp
1496 -> wait udp
1504 -> wait udp
1512 -> wait udp
1472 -> closing udp
1488 -> closing udp
1488 -> closed udp
1488 -> wait tcp
1496 -> closing udp
1496 -> closed udp
1496 -> wait tcp
1504 -> closing udp
1504 -> closed udp
1504 -> wait tcp
1512 -> closing udp
1512 -> closed udp
1512 -> wait tcp
1512 -> tcp io error: stream closed in another thread
1504 -> tcp io error: stream closed in another thread
1496 -> tcp io error: stream closed in another thread
1488 -> tcp io error: stream closed in another thread
1472 -> closed udp
1472 -> wait tcp
1472 -> done
1472 -> closing tcp
The thing that makes this program very confusing is that the "stream closed in another thread" related to the udp socket is raised while waiting for read readiness on the tcp socket. In such a situation, I'd expect the error to relate to the actual socket being waited on, not something else that fiber does not care about anymore.
One thing that could have helped would be to have the reference of which IO object the exception relates to in the exception (aka IOError#io would return the socket). Nevertheless, I'd still consider an issue that this triggers in the wrong "fiber.yield" moment. Therefore, I think there are two possible outcomes here:
- all fibers stop on
udp.closeuntil it is in fact closed (and then IOError is raised before the "closed udp" message can be printed) - exceptions related to IOs not actively being monitored by the current fiber are ignored.
No data to display