diff options
author | KJ Tsanaktsidis <ktsanaktsidis@zendesk.com> | 2023-06-01 16:37:18 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-01 17:37:18 +0900 |
commit | edee9b6a12ac846d7b3de2d704e170bf28178cb3 (patch) | |
tree | f0b4af6454d65cc42eb22f58073691bfbd8667e3 /io.c | |
parent | d8f333491e4c26df7ca577f40d7708d5aedf764f (diff) | |
download | ruby-edee9b6a12ac846d7b3de2d704e170bf28178cb3.tar.gz |
Use a real Ruby mutex in rb_io_close_wait_list (#7884)
Because a thread calling IO#close now blocks in a native condvar wait,
it's possible for there to be _no_ threads left to actually handle
incoming signals/ubf calls/etc.
This manifested as failing tests on Solaris 10 (SPARC), because:
* One thread called IO#close, which sent a SIGVTALRM to the other
thread to interrupt it, and then waited on the condvar to be notified
that the reading thread was done.
* One thread was calling IO#read, but it hadn't yet reached the actual
call to select(2) when the SIGVTALRM arrived, so it never unblocked
itself.
This results in a deadlock.
The fix is to use a real Ruby mutex for the close lock; that way, the
closing thread goes into sigwait-sleep and can keep trying to interrupt
the select(2) thread.
See the discussion in: https://github.com/ruby/ruby/pull/7865/
Diffstat (limited to 'io.c')
-rw-r--r-- | io.c | 10 |
1 files changed, 1 insertions, 9 deletions
@@ -5423,14 +5423,6 @@ maygvl_fclose(FILE *file, int keepgvl) static void free_io_buffer(rb_io_buffer_t *buf); static void clear_codeconv(rb_io_t *fptr); -static void* -call_close_wait_nogvl(void *arg) -{ - struct rb_io_close_wait_list *busy = (struct rb_io_close_wait_list *)arg; - rb_notify_fd_close_wait(busy); - return NULL; -} - static void fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl, struct rb_io_close_wait_list *busy) @@ -5476,7 +5468,7 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl, // Ensure waiting_fd users do not hit EBADF. if (busy) { // Wait for them to exit before we call close(). - (void)rb_thread_call_without_gvl(call_close_wait_nogvl, busy, RUBY_UBF_IO, 0); + rb_notify_fd_close_wait(busy); } // Disable for now. |