[#121215] [Ruby master Bug#21166] Fiber Scheduler is unable to be interrupted by `IO#close`. — "ioquatix (Samuel Williams) via ruby-core" <ruby-core@...>

Issue #21166 has been reported by ioquatix (Samuel Williams).

13 messages 2025/03/02

[#121222] [Ruby master Bug#21167] Visual Studio 2022 17.13.x couldn't build ruby.exe — "hsbt (Hiroshi SHIBATA) via ruby-core" <ruby-core@...>

Issue #21167 has been reported by hsbt (Hiroshi SHIBATA).

8 messages 2025/03/03

[#121234] [Ruby master Bug#21168] Prism doesn't require argument parentheses (in some cases) when a block is present but parse.y does — "Earlopain (Earlopain _) via ruby-core" <ruby-core@...>

Issue #21168 has been reported by Earlopain (Earlopain _).

8 messages 2025/03/04

[#121389] [Ruby Bug#21187] Strings concatenated with `\` getting frozen with literal hashes (PRISM only) — LocoDelAssembly via ruby-core <ruby-core@...>

Issue #21187 has been reported by LocoDelAssembly (Hern=E1n Pereira).

12 messages 2025/03/17

[#121413] [Ruby Bug#21193] Inherited callback returns `nil` for `Object.const_source_location` — "eileencodes (Eileen Uchitelle) via ruby-core" <ruby-core@...>

Issue #21193 has been reported by eileencodes (Eileen Uchitelle).

15 messages 2025/03/20

[#121451] [Ruby Bug#21201] Performance regression when defining methods inside `refine` blocks — "alpaca-tc (Hiroyuki Ishii) via ruby-core" <ruby-core@...>

Issue #21201 has been reported by alpaca-tc (Hiroyuki Ishii).

8 messages 2025/03/27

[ruby-core:121221] [Ruby master Bug#21166] Fiber Scheduler is unable to be interrupted by `IO#close`.

From: "ioquatix (Samuel Williams) via ruby-core" <ruby-core@...>
Date: 2025-03-03 02:56:00 UTC
List: ruby-core #121221
Issue #21166 has been updated by ioquatix (Samuel Williams).


The fiber scheduler hook `rb_fiber_scheduler_io_wait` could be implemented like this:

```c
static VALUE
fiber_scheduler_io_wait(VALUE _argument) {
    VALUE *arguments = (VALUE*)_argument;

    rb_funcallv(arguments[0], id_io_wait, 3, arguments + 1);
}

VALUE
rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout)
{
    VALUE arguments[] = {
        scheduler, io, events, timeout
    };

    return rb_io_interruptible_operation(io, fiber_scheduler_io_wait, (VALUE)&arguments);
}
```

I'll consider how to modify `io_read` and `io_write` hooks.

----------------------------------------
Bug #21166: Fiber Scheduler is unable to be interrupted by `IO#close`.
https://bugs.ruby-lang.org/issues/21166#change-112164

* Author: ioquatix (Samuel Williams)
* Status: Open
* 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

This PR introduces some new functions:

- `VALUE rb_io_interruptible_operation(VALUE self, VALUE(*function)(VALUE), VALUE argument)` for wrapping user IO operations so they can be interrupted.
- `IO#interruptable_operation(&block)` the same as above.
- `VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception)` for interrupting a specific fiber on the fiber scheduler.

`rb_notify_fd_close` is modified so that it is fiber scheduler aware and uses `rb_fiber_scheduler_fiber_interrupt` to interrupt a fiber. In addition, we also change the internal `struct waiting_fd` to track the `rb_execution_context_t` rather than just the `rb_thread_t` instance, so that we can correctly wake up either the waiting thread or fiber.

The public interface `rb_io_interruptible_operation` and `IO#interruptible_operation` are introduced so that the scheduler implementation can wrap IO operations that should be interruptible, e.g.

```ruby
Fiber.schedule do
  io.interruptible_operation do
    io.wait_readable
  end
end

# Will interrupt above fiber:
io.close
```

See <https://github.com/ruby/ruby/pull/12585> for the proposed implementation and <https://github.com/socketry/io-event/pull/130> for example of how `io-event` gem uses both the C and Ruby interfaces.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- [email protected]
 To unsubscribe send an email to [email protected]
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/


In This Thread