Skip to content
Merged
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
5 changes: 3 additions & 2 deletions lib/erb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
24 changes: 12 additions & 12 deletions lib/erb/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/erb/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true
class ERB
# The version string
VERSION = '5.0.2'
end
50 changes: 40 additions & 10 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,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;

Expand Down Expand Up @@ -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;
}

/*
Expand Down