
Issue #21166 has been updated by ioquatix (Samuel Williams).
rb_thread_io_interruptible_operation why not rb_io prefix? is it public c-api?
It's not public API, and we can change the name. It is defined in `thread.c` and `thread.h` since that is where `waiting_fd` is defined/used. `waiting_fd` is not exposed outside of `thread.c`.
who/when/how unregister the hooks?
`rb_thread_io_interruptible_operation` uses a callback, so before entry to the callback, the operation is registered in `waiting_fds`, and on exit from the callback, it is removed. - Entry: https://github.com/ruby/ruby/pull/12839/files#diff-161b2a279f4c67a1ab075a789... - Exit: https://github.com/ruby/ruby/pull/12839/files#diff-161b2a279f4c67a1ab075a789...
could you clear how fiber scheduler use it? no io_close event (callback)?
The fiber scheduler implementation does not need to be changed, since we added this to the `scheduler.c` implementation.
which context the callback function is called?
It's used to wrap the execution of `io_read`/`io_write` and `io_wait` scheduler hooks: https://github.com/ruby/ruby/pull/12839/files#diff-29a83910395dea702ac0c02b4... ---------------------------------------- Bug #21166: Fiber Scheduler is unable to be interrupted by `IO#close`. https://bugs.ruby-lang.org/issues/21166#change-112248 * Author: ioquatix (Samuel Williams) * Status: Open * Assignee: ioquatix (Samuel Williams) * Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- ## Background Ruby's `IO#close` can cause `IO#read`, `IO#write`, `IO#wait`, `IO#wait_readable` and `IO#wait_writable` to be interrupted with an `IOError: stream closed in another thread`. For reference, `IO#select` cannot be interrupted in this way. ```ruby r, w = IO.pipe thread = Thread.new do r.read(1) end Thread.pass until thread.status == "sleep" r.close thread.join # ./test.rb:6:in 'IO#read': stream closed in another thread (IOError) ``` ## Problem The fiber scheduler provides hooks for `io_read`, `io_write` and `io_wait` which are used by `IO#read`, `IO#write`, `IO#wait`, `IO#wait_readable` and `IO#wait_writable`, but those hooks are not interrupted when `IO#close` is invoked. That is because `rb_notify_fd_close` is not scheduler aware, and the fiber scheduler is unable to register itself into the "waiting file descriptor" list. ```ruby #!/usr/bin/env ruby require 'async' r, w = IO.pipe thread = Thread.new do Async do r.wait_readable end end Thread.pass until thread.status == "sleep" r.close thread.join ``` In this test program, `rb_notify_fd_close` will incorrectly terminate the entire fiber scheduler thread: ``` #<Thread:0x00007faa5b161bf8 /home/samuel/Developer/socketry/io-event/test.rb:7 run> terminated with exception (report_on_exception is true): /home/samuel/Developer/socketry/io-event/lib/io/event/selector/select.rb:470:in 'IO.select': closed stream (IOError) from /home/samuel/Developer/socketry/io-event/lib/io/event/selector/select.rb:470:in 'block in IO::Event::Selector::Select#select' from /home/samuel/Developer/socketry/io-event/lib/io/event/selector/select.rb:468:in 'Thread.handle_interrupt' from /home/samuel/Developer/socketry/io-event/lib/io/event/selector/select.rb:468:in 'IO::Event::Selector::Select#select' from /home/samuel/.gem/ruby/3.4.1/gems/async-2.23.0/lib/async/scheduler.rb:396:in 'Async::Scheduler#run_once!' ... ``` ## Solution We need a mechanism to ensure fibers are treated the same as threads, and interrupted correctly. We do this by: 1. Introducing `VALUE rb_thread_io_interruptible_operation(VALUE self, VALUE(*function)(VALUE), VALUE argument)` which allows us to execute a callback that may be interrupted. Internally, this registers the current execution context into the existing `waiting_fds` list. 2. We update all the relevant fiber scheduler hooks to use `rb_thread_io_interruptible_operation`, e.g. `io_wait`, `io_read`, `io_write` and so on. 3. We introduce `VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception)` which can be used to interrupt a fiber, e.g. with an IOError exception. 4. `rb_notify_fd_close` is modified to correctly interrupt fibers using the new rb_fiber_scheduler_fiber_interrupt` function. See <https://github.com/ruby/ruby/pull/12839> for the proposed implementation. -- https://bugs.ruby-lang.org/