summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--io.c22
-rw-r--r--test/fiber/scheduler.rb7
2 files changed, 19 insertions, 10 deletions
diff --git a/io.c b/io.c
index 91537895d2..e0e7d40548 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 60261d69e2..029c5043dc 100644
--- a/test/fiber/scheduler.rb
+++ b/test/fiber/scheduler.rb
@@ -255,6 +255,13 @@ class Scheduler
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