Skip to content

Commit

Permalink
Let Fiber#raise work with transferring fibers
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nevans authored and ioquatix committed Dec 11, 2020
1 parent e795b63 commit 31e8de2
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 4 deletions.
27 changes: 23 additions & 4 deletions cont.c
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
}

/*
Expand Down
25 changes: 25 additions & 0 deletions spec/ruby/core/fiber/raise_spec.rb
Expand Up @@ -73,4 +73,29 @@
-> { 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
10 changes: 10 additions & 0 deletions test/ruby/test_fiber.rb
Expand Up @@ -169,6 +169,16 @@ def test_raise
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
Expand Down

0 comments on commit 31e8de2

Please sign in to comment.