summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornicholas a. evans <nicholas.evans@gmail.com>2020-11-17 19:23:51 -0500
committerSamuel Williams <samuel.williams@oriontransfer.co.nz>2020-12-12 10:18:19 +1300
commit31e8de2920935d500105949bda931f3ca22cdbff (patch)
treec2ec39c48a2930ecbbb4ed58175cf3d587270165
parente795b632467317cdea07d0599bf2c5d08b403f00 (diff)
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.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3795
-rw-r--r--cont.c27
-rw-r--r--spec/ruby/core/fiber/raise_spec.rb25
-rw-r--r--test/ruby/test_fiber.rb10
3 files changed, 58 insertions, 4 deletions
diff --git a/cont.c b/cont.c
index efa734d03a..bdc29e14c1 100644
--- a/cont.c
+++ b/cont.c
@@ -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
@@ -2423,6 +2436,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)) {
rb_raise(rb_eFiberError, "attempt to transfer to a 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);
}
/*
diff --git a/spec/ruby/core/fiber/raise_spec.rb b/spec/ruby/core/fiber/raise_spec.rb
index fd1cc911b5..f56219e565 100644
--- a/spec/ruby/core/fiber/raise_spec.rb
+++ b/spec/ruby/core/fiber/raise_spec.rb
@@ -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
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index 96ffc79950..e0cc59bd3c 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -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