Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions depend
Original file line number Diff line number Diff line change
Expand Up @@ -15019,6 +15019,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
Expand Down
45 changes: 34 additions & 11 deletions scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -647,18 +648,29 @@ rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
{
RUBY_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;

#ifdef RUBY_DEBUG
rb_execution_context_t *ec = GET_EC();
if (RUBY_VM_INTERRUPTED(ec)) {
rb_bug("rb_fiber_scheduler_unblock called with pending interrupt");
int old_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);
}
#endif
EC_POP_TAG();

VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber);
ec->interrupt_mask = old_interrupt_mask;

if (state) {
EC_JUMP_TAG(ec, state);
}

RUBY_VM_CHECK_INTS(ec);

errno = saved_errno;

Expand Down Expand Up @@ -1079,15 +1091,26 @@ VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exc
VALUE arguments[] = {
fiber, exception
};

#ifdef RUBY_DEBUG
VALUE result;
enum ruby_tag_type state;
rb_execution_context_t *ec = GET_EC();
if (RUBY_VM_INTERRUPTED(ec)) {
rb_bug("rb_fiber_scheduler_fiber_interrupt called with pending interrupt");
int old_interrupt_mask = ec->interrupt_mask;
ec->interrupt_mask |= PENDING_INTERRUPT_MASK;

EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
result = rb_check_funcall(scheduler, id_fiber_interrupt, 2, arguments);
}
#endif
EC_POP_TAG();

ec->interrupt_mask = old_interrupt_mask;

return rb_check_funcall(scheduler, id_fiber_interrupt, 2, arguments);
if (state) {
EC_JUMP_TAG(ec, state);
}

RUBY_VM_CHECK_INTS(ec);
return result;
}

/*
Expand Down
Loading