@@ -5546,23 +5546,28 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl)
55465546 // Need to keep FILE objects of stdin, stdout and stderr, so we are done:
55475547 done = 1 ;
55485548 }
5549+ // The check for Qnil here is purely defensive, we should never invoke the fiber scheduler if there is no IO object.
5550+ else if (fptr -> self != Qnil && fptr -> closing_count == 0 ) {
5551+ VALUE scheduler = rb_fiber_scheduler_current ();
5552+ if (scheduler != Qnil ) {
5553+ fptr -> closing_count += 1 ;
5554+ VALUE result = rb_fiber_scheduler_io_close (scheduler , fptr -> self );
5555+ fptr -> closing_count -= 1 ;
5556+
5557+ if (!UNDEF_P (result )) {
5558+ // Scheduler handled it:
5559+ if (fptr -> fd == -1 ) return ;
5560+ }
5561+ }
5562+ }
55495563
55505564 fptr -> fd = -1 ;
55515565 fptr -> stdio_file = 0 ;
55525566 fptr -> mode &= ~(FMODE_READABLE |FMODE_WRITABLE );
55535567
5554- // wait for blocking operations to ensure they do not hit EBADF:
5568+ // Wait for blocking operations to ensure they do not hit EBADF:
55555569 rb_thread_io_close_wait (fptr );
55565570
5557- // Disable for now.
5558- // if (!done && fd >= 0) {
5559- // VALUE scheduler = rb_fiber_scheduler_current();
5560- // if (scheduler != Qnil) {
5561- // VALUE result = rb_fiber_scheduler_io_close(scheduler, fptr->self);
5562- // if (!UNDEF_P(result)) done = 1;
5563- // }
5564- // }
5565-
55665571 if (!done && stdio_file ) {
55675572 // stdio_file is deallocated anyway even if fclose failed.
55685573 if ((maygvl_fclose (stdio_file , noraise ) < 0 ) && NIL_P (error )) {
@@ -5724,10 +5729,12 @@ io_close_fptr(VALUE io)
57245729 if (!fptr ) return 0 ;
57255730 if (fptr -> fd < 0 ) return 0 ;
57265731
5732+ // This guards against multiple threads closing the same IO object:
57275733 if (rb_thread_io_close_interrupt (fptr )) {
57285734 /* calls close(fptr->fd): */
57295735 fptr_finalize_flush (fptr , FALSE, KEEPGVL );
57305736 }
5737+
57315738 rb_io_fptr_cleanup (fptr , FALSE);
57325739 return fptr ;
57335740}
@@ -8553,6 +8560,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
85538560 ccan_list_head_init (& fptr -> blocking_operations );
85548561 fptr -> closing_ec = NULL ;
85558562 fptr -> wakeup_mutex = Qnil ;
8563+ fptr -> closing_count = 0 ;
85568564 fptr -> fork_generation = GET_VM ()-> fork_gen ;
85578565
85588566 if (!NIL_P (orig -> pathv )) fptr -> pathv = orig -> pathv ;
@@ -9283,6 +9291,7 @@ rb_io_open_descriptor(VALUE klass, int descriptor, int mode, VALUE path, VALUE t
92839291 ccan_list_head_init (& io -> blocking_operations );
92849292 io -> closing_ec = NULL ;
92859293 io -> wakeup_mutex = Qnil ;
9294+ io -> closing_count = 0 ;
92869295 io -> fork_generation = GET_VM ()-> fork_gen ;
92879296
92889297 if (encoding ) {
@@ -9427,6 +9436,7 @@ rb_io_fptr_new(void)
94279436 ccan_list_head_init (& fp -> blocking_operations );
94289437 fp -> closing_ec = NULL ;
94299438 fp -> wakeup_mutex = Qnil ;
9439+ fp -> closing_count = 0 ;
94309440 fp -> fork_generation = GET_VM ()-> fork_gen ;
94319441 return fp ;
94329442}
@@ -9561,6 +9571,7 @@ io_initialize(VALUE io, VALUE fnum, VALUE vmode, VALUE opt)
95619571 ccan_list_head_init (& fp -> blocking_operations );
95629572 fp -> closing_ec = NULL ;
95639573 fp -> wakeup_mutex = Qnil ;
9574+ fp -> closing_count = 0 ;
95649575 fp -> fork_generation = GET_VM ()-> fork_gen ;
95659576 clear_codeconv (fp );
95669577 io_check_tty (fp );
0 commit comments