Skip to content

Commit e900e9f

Browse files
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 <[email protected]>
1 parent 6882012 commit e900e9f

File tree

2 files changed

+24
-1
lines changed

2 files changed

+24
-1
lines changed

common.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16575,6 +16575,7 @@ scheduler.$(OBJEXT): {$(VPATH)}config.h
1657516575
scheduler.$(OBJEXT): {$(VPATH)}constant.h
1657616576
scheduler.$(OBJEXT): {$(VPATH)}defines.h
1657716577
scheduler.$(OBJEXT): {$(VPATH)}encoding.h
16578+
scheduler.$(OBJEXT): {$(VPATH)}eval_intern.h
1657816579
scheduler.$(OBJEXT): {$(VPATH)}fiber/scheduler.h
1657916580
scheduler.$(OBJEXT): {$(VPATH)}id.h
1658016581
scheduler.$(OBJEXT): {$(VPATH)}id_table.h

scheduler.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
**********************************************************************/
1010

1111
#include "vm_core.h"
12+
#include "eval_intern.h"
1213
#include "ruby/fiber/scheduler.h"
1314
#include "ruby/io.h"
1415
#include "ruby/io/buffer.h"
@@ -412,11 +413,32 @@ rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
412413
{
413414
RUBY_ASSERT(rb_obj_is_fiber(fiber));
414415

416+
VALUE result;
417+
enum ruby_tag_type state;
418+
415419
// `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`.
420+
//
416421
// 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.
417422
int saved_errno = errno;
418423

419-
VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
424+
// 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.
425+
rb_execution_context_t *ec = GET_EC();
426+
int saved_interrupt_mask = ec->interrupt_mask;
427+
ec->interrupt_mask |= PENDING_INTERRUPT_MASK;
428+
429+
EC_PUSH_TAG(ec);
430+
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
431+
result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
432+
}
433+
EC_POP_TAG();
434+
435+
ec->interrupt_mask = saved_interrupt_mask;
436+
437+
if (state) {
438+
EC_JUMP_TAG(ec, state);
439+
}
440+
441+
RUBY_VM_CHECK_INTS(ec);
420442

421443
errno = saved_errno;
422444

0 commit comments

Comments
 (0)