[ruby-core:124851] [Ruby Bug#21918] fiber scheduler: fiber interrupt triggers for IOs that haven't been closed
Issue #21918 has been reported by chucke (Tiago Cardoso). ---------------------------------------- Bug #21918: fiber scheduler: fiber interrupt triggers for IOs that haven't been closed https://bugs.ruby-lang.org/issues/21918 * Author: chucke (Tiago Cardoso) * Status: Open * ruby -v: ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux] * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- 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: ```ruby 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: 1. all fibers stop on `udp.close` until it is in fact closed (and then IOError is raised before the "closed udp" message can be printed) 2. exceptions related to IOs not actively being monitored by the current fiber are ignored. -- https://bugs.ruby-lang.org/
participants (1)
-
chucke (Tiago Cardoso)