[前][次][番号順一覧][スレッド一覧]

ruby-changes:64103

From: nicholas <ko1@a...>
Date: Sat, 12 Dec 2020 06:18:48 +0900 (JST)
Subject: [ruby-changes:64103] 31e8de2920 (master): Let Fiber#raise work with transferring fibers

https://git.ruby-lang.org/ruby.git/commit/?id=31e8de2920

From 31e8de2920935d500105949bda931f3ca22cdbff Mon Sep 17 00:00:00 2001
From: "nicholas a. evans" <nicholas.evans@g...>
Date: Tue, 17 Nov 2020 19:23:51 -0500
Subject: 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.

diff --git a/cont.c b/cont.c
index efa734d..bdc29e1 100644
--- a/cont.c
+++ b/cont.c
@@ -2330,6 +2330,8 @@ rb_fiber_m_resume(int argc, VALUE *argv, VALUE fiber) https://github.com/ruby/ruby/blob/trunk/cont.c#L2330
     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) https://github.com/ruby/ruby/blob/trunk/cont.c#L2340
  *
  *  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) https://github.com/ruby/ruby/blob/trunk/cont.c#L2354
  *  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) https://github.com/ruby/ruby/blob/trunk/cont.c#L2436
 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) https://github.com/ruby/ruby/blob/trunk/cont.c#L2449
     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 fd1cc91..f56219e 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 https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/fiber/raise_spec.rb#L73
       -> { 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 96ffc79..e0cc59b 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -169,6 +169,16 @@ class TestFiber < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_fiber.rb#L169
     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
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]