summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2025-09-18 15:24:02 +1200
committernagachika <nagachika@ruby-lang.org>2025-10-06 07:50:39 +0900
commit183e2b0a2389579a3a5b205113db72345b52dd85 (patch)
tree6635ead8552aaf25726ca39f703d9820b0bb3953
parent05f93fe6dc6f99fd2f728dd3c85dca944f1f4ba1 (diff)
Use `ec->interrupt_mask` to prevent interrupts. [Backport #21610]
Disallow pending interrupts to be checked during `FiberScheduler#unblock`. Ractors can send signals at any time, so the previous debug assertion can fail if a Ractor sends a signal. Co-authored-by: Luke Gruber <luke.gruber@shopify.com>
-rw-r--r--common.mk1
-rw-r--r--scheduler.c24
2 files changed, 24 insertions, 1 deletions
diff --git a/common.mk b/common.mk
index 8075d6a95e..470b95d020 100644
--- a/common.mk
+++ b/common.mk
@@ -15860,6 +15860,7 @@ scheduler.$(OBJEXT): {$(VPATH)}config.h
scheduler.$(OBJEXT): {$(VPATH)}constant.h
scheduler.$(OBJEXT): {$(VPATH)}defines.h
scheduler.$(OBJEXT): {$(VPATH)}encoding.h
+scheduler.$(OBJEXT): {$(VPATH)}eval_intern.h
scheduler.$(OBJEXT): {$(VPATH)}fiber/scheduler.h
scheduler.$(OBJEXT): {$(VPATH)}id.h
scheduler.$(OBJEXT): {$(VPATH)}id_table.h
diff --git a/scheduler.c b/scheduler.c
index 0906bc0101..e20fbb5983 100644
--- a/scheduler.c
+++ b/scheduler.c
@@ -9,6 +9,7 @@
**********************************************************************/
#include "vm_core.h"
+#include "eval_intern.h"
#include "ruby/fiber/scheduler.h"
#include "ruby/io.h"
#include "ruby/io/buffer.h"
@@ -403,11 +404,32 @@ rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
{
VM_ASSERT(rb_obj_is_fiber(fiber));
+ VALUE result;
+ enum ruby_tag_type state;
+
// `rb_fiber_scheduler_unblock` can be called from points where `errno` is expected to be preserved. Therefore, we should save and restore it. For example `io_binwrite` calls `rb_fiber_scheduler_unblock` and if `errno` is reset to 0 by user code, it will break the error handling in `io_write`.
+ //
// If we explicitly preserve `errno` in `io_binwrite` and other similar functions (e.g. by returning it), this code is no longer needed. I hope in the future we will be able to remove it.
int saved_errno = errno;
- VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
+ // We must prevent interrupts while invoking the unblock method, because otherwise fibers can be left permanently blocked if an interrupt occurs during the execution of user code.
+ rb_execution_context_t *ec = GET_EC();
+ int saved_interrupt_mask = ec->interrupt_mask;
+ ec->interrupt_mask |= PENDING_INTERRUPT_MASK;
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
+ }
+ EC_POP_TAG();
+
+ ec->interrupt_mask = saved_interrupt_mask;
+
+ if (state) {
+ EC_JUMP_TAG(ec, state);
+ }
+
+ RUBY_VM_CHECK_INTS(ec);
errno = saved_errno;