From 941e70ab38d01d067b7bbbcdf8553893a9ca8b49 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 5 Dec 2025 22:09:00 -0500 Subject: [PATCH 1/2] Sync doc/stringio in sync_default_gems.rb --- tool/sync_default_gems.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 9d62e954ce11f1..780f923b55598e 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -272,6 +272,7 @@ def lib((upstream, branch), gemspec_in_subdir: false) ["ext/stringio", "ext/stringio"], ["test/stringio", "test/stringio"], ["stringio.gemspec", "ext/stringio/stringio.gemspec"], + ["doc/stringio", "doc/stringio"], ], exclude: [ "ext/stringio/README.md", ]), From eafaff9443daebba1f4e1a5b2a217b993f066360 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 8 Dec 2025 00:55:13 +1300 Subject: [PATCH 2/2] Re-introduce support for `io_close` hook. (#15434) --- io.c | 22 ++++++++++++---------- test/fiber/scheduler.rb | 7 +++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/io.c b/io.c index 91537895d2b3a5..e0e7d40548ee69 100644 --- a/io.c +++ b/io.c @@ -5551,18 +5551,9 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl) fptr->stdio_file = 0; fptr->mode &= ~(FMODE_READABLE|FMODE_WRITABLE); - // wait for blocking operations to ensure they do not hit EBADF: + // Wait for blocking operations to ensure they do not hit EBADF: rb_thread_io_close_wait(fptr); - // Disable for now. - // if (!done && fd >= 0) { - // VALUE scheduler = rb_fiber_scheduler_current(); - // if (scheduler != Qnil) { - // VALUE result = rb_fiber_scheduler_io_close(scheduler, fptr->self); - // if (!UNDEF_P(result)) done = 1; - // } - // } - if (!done && stdio_file) { // stdio_file is deallocated anyway even if fclose failed. if ((maygvl_fclose(stdio_file, noraise) < 0) && NIL_P(error)) { @@ -5574,6 +5565,15 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl) done = 1; } + VALUE scheduler = rb_fiber_scheduler_current(); + if (!done && fd >= 0 && scheduler != Qnil) { + VALUE result = rb_fiber_scheduler_io_close(scheduler, RB_INT2NUM(fd)); + + if (!UNDEF_P(result)) { + done = RTEST(result); + } + } + if (!done && fd >= 0) { // fptr->fd may be closed even if close fails. POSIX doesn't specify it. // We assumes it is closed. @@ -5724,10 +5724,12 @@ io_close_fptr(VALUE io) if (!fptr) return 0; if (fptr->fd < 0) return 0; + // This guards against multiple threads closing the same IO object: if (rb_thread_io_close_interrupt(fptr)) { /* calls close(fptr->fd): */ fptr_finalize_flush(fptr, FALSE, KEEPGVL); } + rb_io_fptr_cleanup(fptr, FALSE); return fptr; } diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 60261d69e2213f..029c5043dc1b14 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -255,6 +255,13 @@ def io_select(...) end.value end + # This hook is invoked by `IO#close`. Using a separate IO object + # demonstrates that the close operation is asynchronous. + def io_close(descriptor) + Fiber.blocking{IO.for_fd(descriptor.to_i).close} + return true + end + # This hook is invoked by `Kernel#sleep` and `Thread::Mutex#sleep`. def kernel_sleep(duration = nil) # $stderr.puts [__method__, duration, Fiber.current].inspect