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/