From e6879401feba22e3657a231cbedc751998cb7176 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 18 Sep 2025 14:24:29 +1200 Subject: [PATCH 1/4] Use `ec->interrupt_mask` to prevent interrupts. (#14588) 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 --- depend | 1 + scheduler.c | 50 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/depend b/depend index b9d91faa2a05a2..5c3a03c96219d1 100644 --- a/depend +++ b/depend @@ -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 diff --git a/scheduler.c b/scheduler.c index ddb205da885cee..8351fc9945fd8f 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" @@ -647,18 +648,32 @@ 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 + // 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. See also `rb_fiber_scheduler_fiber_interrupt`. rb_execution_context_t *ec = GET_EC(); - if (RUBY_VM_INTERRUPTED(ec)) { - rb_bug("rb_fiber_scheduler_unblock called with pending interrupt"); + 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); } -#endif + EC_POP_TAG(); - VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber); + ec->interrupt_mask = saved_interrupt_mask; + + if (state) { + EC_JUMP_TAG(ec, state); + } + + RUBY_VM_CHECK_INTS(ec); errno = saved_errno; @@ -1080,14 +1095,29 @@ VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exc fiber, exception }; -#ifdef RUBY_DEBUG + VALUE result; + enum ruby_tag_type state; + + // We must prevent interrupts while invoking the fiber_interrupt method, because otherwise fibers can be left permanently blocked if an interrupt occurs during the execution of user code. See also `rb_fiber_scheduler_unblock`. rb_execution_context_t *ec = GET_EC(); - if (RUBY_VM_INTERRUPTED(ec)) { - rb_bug("rb_fiber_scheduler_fiber_interrupt called with pending interrupt"); + 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_check_funcall(scheduler, id_fiber_interrupt, 2, arguments); } -#endif + EC_POP_TAG(); + + ec->interrupt_mask = saved_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; } /* From f37d42fc7b9e66463caa17506cfd47f8b4e47983 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 18 Sep 2025 12:23:57 +0900 Subject: [PATCH 2/4] [ruby/erb] [DOC] Fix the description of #new_toplevel (https://github.com/ruby/erb/pull/78) https://github.com/ruby/erb/commit/de0f18579e --- lib/erb.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/erb.rb b/lib/erb.rb index d88cce9f4b1722..e72fe95572bdf9 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -999,8 +999,9 @@ def result_with_hash(hash) # See [Default Binding][default binding]. # # Argument `symbols` is an array of symbols; - # each symbol `symbol` is used to define (unless already defined) a variable in the binding - # whose name is `symbol` and whose value is `nil`. + # each symbol `symbol` is defined as a new variable to hide and + # prevent it from overwriting a variable of the same name already + # defined within the binding. # # [default binding]: rdoc-ref:ERB@Default+Binding def new_toplevel(vars = nil) From 3379dd44554ec3c745a38b4ef33c93aafb1dabf1 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 18 Sep 2025 13:57:58 +0900 Subject: [PATCH 3/4] [ruby/erb] [DOC] Fix the location of `url_encode` doc The documentation must be just before the definition. https://github.com/ruby/erb/commit/62282e32d9 --- lib/erb/util.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/erb/util.rb b/lib/erb/util.rb index 42c7a5762215b9..efa8ca1d137cbd 100644 --- a/lib/erb/util.rb +++ b/lib/erb/util.rb @@ -47,19 +47,19 @@ module ERB::Util alias h html_escape module_function :h - # - # A utility method for encoding the String _s_ as a URL. - # - # require "erb" - # include ERB::Util - # - # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide") - # - # _Generates_ - # - # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide - # if CGI.respond_to?(:escapeURIComponent) + # + # A utility method for encoding the String _s_ as a URL. + # + # require "erb" + # include ERB::Util + # + # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide") + # + # _Generates_ + # + # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide + # def url_encode(s) CGI.escapeURIComponent(s.to_s) end From 176b3b514d8f4bcea38a10f0567b760701e46058 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 18 Sep 2025 14:00:44 +0900 Subject: [PATCH 4/4] [ruby/erb] [DOC] `ERB::Version` https://github.com/ruby/erb/commit/7fed01c4de --- lib/erb/version.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/erb/version.rb b/lib/erb/version.rb index 0875dcb42a8227..1c2c6fe1641bbb 100644 --- a/lib/erb/version.rb +++ b/lib/erb/version.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true class ERB + # The version string VERSION = '5.0.2' end