1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Let Fiber#raise work with transferring fibers

This automatically choosess whether to use transfer on a transferring
fiber or resume on a yielding fiber.  If the fiber is resuming, it
raises a FiberError.
This commit is contained in:
nicholas a. evans 2020-11-17 19:23:51 -05:00 committed by Samuel Williams
parent e795b63246
commit 31e8de2920
Notes: git 2020-12-12 06:18:45 +09:00
3 changed files with 58 additions and 4 deletions

27
cont.c
View file

@ -2330,6 +2330,8 @@ rb_fiber_m_resume(int argc, VALUE *argv, VALUE fiber)
return rb_fiber_resume_kw(fiber, argc, argv, rb_keyword_given_p());
}
static VALUE rb_fiber_transfer_kw(VALUE fiber_value, int argc, VALUE *argv, int kw_splat);
/*
* call-seq:
* fiber.raise -> obj
@ -2338,7 +2340,9 @@ rb_fiber_m_resume(int argc, VALUE *argv, VALUE fiber)
*
* Raises an exception in the fiber at the point at which the last
* +Fiber.yield+ was called. If the fiber has not been started or has
* already run to completion, raises +FiberError+.
* already run to completion, raises +FiberError+. If the fiber is
* yielding, it is resumed. If it is transferring, it is transferred into.
* But if it is resuming, raises +FiberError+.
*
* With no arguments, raises a +RuntimeError+. With a single +String+
* argument, raises a +RuntimeError+ with the string as a message. Otherwise,
@ -2350,10 +2354,19 @@ rb_fiber_m_resume(int argc, VALUE *argv, VALUE fiber)
* blocks.
*/
static VALUE
rb_fiber_raise(int argc, VALUE *argv, VALUE fiber)
rb_fiber_raise(int argc, VALUE *argv, VALUE fiber_value)
{
rb_fiber_t *fiber = fiber_ptr(fiber_value);
VALUE exc = rb_make_exception(argc, argv);
return rb_fiber_resume_kw(fiber, -1, &exc, RB_NO_KEYWORDS);
if (RTEST(fiber->resuming_fiber)) {
rb_raise(rb_eFiberError, "attempt to raise a resuming fiber");
}
else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
return rb_fiber_transfer_kw(fiber_value, -1, &exc, RB_NO_KEYWORDS);
}
else {
return rb_fiber_resume_kw(fiber_value, -1, &exc, RB_NO_KEYWORDS);
}
}
static VALUE
@ -2422,6 +2435,12 @@ rb_fiber_backtrace_locations(int argc, VALUE *argv, VALUE fiber)
*/
static VALUE
rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fiber_value)
{
return rb_fiber_transfer_kw(fiber_value, argc, argv, rb_keyword_given_p());
}
static VALUE
rb_fiber_transfer_kw(VALUE fiber_value, int argc, VALUE *argv, int kw_splat)
{
rb_fiber_t *fiber = fiber_ptr(fiber_value);
if (RTEST(fiber->resuming_fiber)) {
@ -2430,7 +2449,7 @@ rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fiber_value)
if (fiber->yielding) {
rb_raise(rb_eFiberError, "attempt to transfer to a yielding fiber");
}
return fiber_switch(fiber, argc, argv, rb_keyword_given_p(), Qfalse, false);
return fiber_switch(fiber, argc, argv, kw_splat, Qfalse, false);
}
/*

View file

@ -73,4 +73,29 @@ ruby_version_is "2.7" do
-> { fiber.resume }.should raise_error(FiberError, /dead fiber called|attempt to resume a terminated fiber/)
end
end
end
ruby_version_is "2.7"..."3.0" do
describe "Fiber#raise" do
it "raises a FiberError if invoked on a transferring Fiber" do
require "fiber"
root = Fiber.current
fiber = Fiber.new { root.transfer }
fiber.transfer
-> { fiber.raise }.should raise_error(FiberError, "cannot resume transferred Fiber")
end
end
end
ruby_version_is "3.0" do
describe "Fiber#raise" do
it "transfers and raises on a transferring fiber" do
require "fiber"
root = Fiber.current
fiber = Fiber.new { root.transfer }
fiber.transfer
-> { fiber.raise "msg" }.should raise_error(RuntimeError, "msg")
end
end
end

View file

@ -169,6 +169,16 @@ class TestFiber < Test::Unit::TestCase
assert_equal(:ok, fib.raise)
end
def test_raise_transferring_fiber
root = Fiber.current
fib = Fiber.new { root.transfer }
fib.transfer
assert_raise(RuntimeError){
fib.raise "can raise with transfer: true"
}
assert_not_predicate(fib, :alive?)
end
def test_transfer
ary = []
f2 = nil