diff --git a/include/mruby.h b/include/mruby.h
index 7a533cfa7..3ed86f8c7 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -1458,8 +1458,6 @@ MRB_API mrb_value mrb_fiber_new(mrb_state *mrb, const struct RProc *proc);
* Implemented in mruby-fiber
*
* Switches to the specified fiber and executes. Like the `Fiber#resume` method.
- *
- * @note It can only be called before entering the mruby VM (e.g. in the `main()` function).
*/
MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv);
diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c
index e91415dfe..4deb0d6a4 100644
--- a/mrbgems/mruby-fiber/src/fiber.c
+++ b/mrbgems/mruby-fiber/src/fiber.c
@@ -170,6 +170,17 @@ fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
}
}
+static void
+fiber_check_cfunc_recursive(mrb_state *mrb, struct mrb_context *c)
+{
+ for (;; c = c->prev) {
+ fiber_check_cfunc(mrb, c);
+ if (c == mrb->root_c || !c->prev) {
+ break;
+ }
+ }
+}
+
static void
fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
{
@@ -275,14 +286,17 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
else {
value = fiber_result(mrb, a, len);
if (vmexec) {
+ if (c->ci > c->cibase) c->ci--; /* pop dummy callinfo */
c->ci[1].stack[0] = value;
}
}
if (vmexec) {
+ int cci = old_c->ci->cci;
c->vmexec = TRUE;
value = mrb_vm_exec(mrb, c->ci->proc, c->ci->pc);
mrb->c = old_c;
+ old_c->ci->cci = cci; /* restore values as they may have changed in Fiber.yield */
}
else {
MARK_CONTEXT_MODIFY(c);
@@ -304,19 +318,19 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
* to the next Fiber.yield statement inside the fiber's block
* or to the block value if it runs to completion without any
* Fiber.yield
- *
- * This method cannot be called from C using mrb_funcall().
- * Use mrb_fiber_resume() function instead.
*/
static mrb_value
fiber_resume(mrb_state *mrb, mrb_value self)
{
const mrb_value *a;
mrb_int len;
+ mrb_bool vmexec = FALSE;
- fiber_check_cfunc(mrb, mrb->c);
mrb_get_args(mrb, "*!", &a, &len);
- return fiber_switch(mrb, self, len, a, TRUE, FALSE);
+ if (mrb->c->ci->cci > 0) {
+ vmexec = TRUE;
+ }
+ return fiber_switch(mrb, self, len, a, TRUE, vmexec);
}
MRB_API mrb_value
@@ -429,7 +443,7 @@ fiber_transfer(mrb_state *mrb, mrb_value self)
const mrb_value* a;
mrb_int len;
- fiber_check_cfunc(mrb, mrb->c);
+ fiber_check_cfunc_recursive(mrb, mrb->c);
mrb_get_args(mrb, "*!", &a, &len);
if (c->status == MRB_FIBER_RESUMED) {
@@ -472,7 +486,6 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
if (c->vmexec) {
c->vmexec = FALSE;
mrb->c->ci->cci = CINFO_RESUMED;
- c->ci--; /* pop callinfo for yield */
}
MARK_CONTEXT_MODIFY(mrb->c);
return fiber_result(mrb, a, len);
diff --git a/mrbgems/mruby-fiber/test/fiber.rb b/mrbgems/mruby-fiber/test/fiber.rb
index 64b9d8175..ea541acf3 100644
--- a/mrbgems/mruby-fiber/test/fiber.rb
+++ b/mrbgems/mruby-fiber/test/fiber.rb
@@ -1,204 +1,210 @@
-assert('Fiber.new') do
- f = Fiber.new{}
- assert_kind_of Fiber, f
-end
+begin
+ $fiber_test_activity = __FILE__
-assert('Fiber#resume') do
- f = Fiber.new{|x| x }
- assert_equal 2, f.resume(2)
-end
-
-assert('Fiber#transfer') do
- ary = []
- f2 = nil
- f1 = Fiber.new{
- ary << f2.transfer(:foo)
- :ok
- }
- f2 = Fiber.new{
- ary << f1.transfer(:baz)
- :ng
- }
- assert_equal(:ok, f1.transfer)
- assert_equal([:baz], ary)
- assert_false f1.alive?
-end
-
-assert('Fiber#alive?') do
- f = Fiber.new{ Fiber.yield }
- f.resume
- assert_true f.alive?
- f.resume
- assert_false f.alive?
-end
-
-assert('Fiber#==') do
- root = Fiber.current
- assert_equal root, root
- assert_equal root, Fiber.current
- assert_false root != Fiber.current
- f = Fiber.new {
- assert_false root == Fiber.current
- }
- f.resume
- assert_false f == root
- assert_true f != root
-end
-
-assert('Fiber.yield') do
- f = Fiber.new{|x| Fiber.yield x }
- assert_equal 3, f.resume(3)
- assert_true f.alive?
-end
-
-assert('FiberError') do
- assert_equal StandardError, FiberError.superclass
-end
-
-assert('Fiber iteration') do
- f1 = Fiber.new{
- [1,2,3].each{|x| Fiber.yield(x)}
- }
- f2 = Fiber.new{
- [9,8,7].each{|x| Fiber.yield(x)}
- }
- a = []
- 3.times {
- a << f1.resume
- a << f2.resume
- }
- assert_equal [1,9,2,8,3,7], a
-end
-
-assert('Fiber with splat in the block argument list') {
- assert_equal([1], Fiber.new{|*x|x}.resume(1))
-}
-
-assert('Fiber raises on resume when dead') do
- assert_raise(FiberError) do
+ assert('Fiber.new') do
f = Fiber.new{}
+ assert_kind_of Fiber, f
+ end
+
+ assert('Fiber#resume') do
+ f = Fiber.new{|x| x }
+ assert_equal 2, f.resume(2)
+ end
+
+ assert('Fiber#transfer') do
+ ary = []
+ f2 = nil
+ f1 = Fiber.new{
+ ary << f2.transfer(:foo)
+ :ok
+ }
+ f2 = Fiber.new{
+ ary << f1.transfer(:baz)
+ :ng
+ }
+ assert_equal(:ok, f1.transfer)
+ assert_equal([:baz], ary)
+ assert_false f1.alive?
+ end
+
+ assert('Fiber#alive?') do
+ f = Fiber.new{ Fiber.yield }
+ f.resume
+ assert_true f.alive?
f.resume
assert_false f.alive?
- f.resume
end
-end
-assert('Yield raises when called on root fiber') do
- assert_raise(FiberError) { Fiber.yield }
-end
+ assert('Fiber#==') do
+ root = Fiber.current
+ assert_equal root, root
+ assert_equal root, Fiber.current
+ assert_false root != Fiber.current
+ f = Fiber.new {
+ assert_false root == Fiber.current
+ }
+ f.resume
+ assert_false f == root
+ assert_true f != root
+ end
-assert('Double resume of Fiber') do
- f1 = Fiber.new {}
- f2 = Fiber.new {
- f1.resume
- assert_raise(FiberError) { f2.resume }
- Fiber.yield 0
+ assert('Fiber.yield') do
+ f = Fiber.new{|x| Fiber.yield x }
+ assert_equal 3, f.resume(3)
+ assert_true f.alive?
+ end
+
+ assert('FiberError') do
+ assert_equal StandardError, FiberError.superclass
+ end
+
+ assert('Fiber iteration') do
+ f1 = Fiber.new{
+ [1,2,3].each{|x| Fiber.yield(x)}
+ }
+ f2 = Fiber.new{
+ [9,8,7].each{|x| Fiber.yield(x)}
+ }
+ a = []
+ 3.times {
+ a << f1.resume
+ a << f2.resume
+ }
+ assert_equal [1,9,2,8,3,7], a
+ end
+
+ assert('Fiber with splat in the block argument list') {
+ assert_equal([1], Fiber.new{|*x|x}.resume(1))
}
- assert_equal 0, f2.resume
- f2.resume
- assert_false f1.alive?
- assert_false f2.alive?
-end
-assert('Recursive resume of Fiber') do
- f1, f2 = nil, nil
- f1 = Fiber.new { assert_raise(FiberError) { f2.resume } }
- f2 = Fiber.new {
- f1.resume
- Fiber.yield 0
- }
- f3 = Fiber.new {
+ assert('Fiber raises on resume when dead') do
+ assert_raise(FiberError) do
+ f = Fiber.new{}
+ f.resume
+ assert_false f.alive?
+ f.resume
+ end
+ end
+
+ assert('Yield raises when called on root fiber') do
+ assert_raise(FiberError) { Fiber.yield }
+ end
+
+ assert('Double resume of Fiber') do
+ f1 = Fiber.new {}
+ f2 = Fiber.new {
+ f1.resume
+ assert_raise(FiberError) { f2.resume }
+ Fiber.yield 0
+ }
+ assert_equal 0, f2.resume
f2.resume
- }
- assert_equal 0, f3.resume
- f2.resume
- assert_false f1.alive?
- assert_false f2.alive?
- assert_false f3.alive?
-end
+ assert_false f1.alive?
+ assert_false f2.alive?
+ end
-assert('Root fiber resume') do
- root = Fiber.current
- assert_raise(FiberError) { root.resume }
- f = Fiber.new {
+ assert('Recursive resume of Fiber') do
+ f1, f2 = nil, nil
+ f1 = Fiber.new { assert_raise(FiberError) { f2.resume } }
+ f2 = Fiber.new {
+ f1.resume
+ Fiber.yield 0
+ }
+ f3 = Fiber.new {
+ f2.resume
+ }
+ assert_equal 0, f3.resume
+ f2.resume
+ assert_false f1.alive?
+ assert_false f2.alive?
+ assert_false f3.alive?
+ end
+
+ assert('Root fiber resume') do
+ root = Fiber.current
assert_raise(FiberError) { root.resume }
- }
- f.resume
- assert_false f.alive?
-end
-
-assert('Fiber without block') do
- assert_raise(ArgumentError) { Fiber.new }
-end
-
-
-assert('Transfer to self.') do
- result = []
- f = Fiber.new { result << :start; f.transfer; result << :end }
- f.transfer
- assert_equal [:start, :end], result
-
- result = []
- f = Fiber.new { result << :start; f.transfer; result << :end }
- f.resume
- assert_equal [:start, :end], result
-end
-
-assert('Resume transferred fiber') do
- f = Fiber.new {
- assert_raise(FiberError) { f.resume }
- }
- f.transfer
-end
-
-assert('Root fiber transfer.') do
- result = nil
- root = Fiber.current
- f = Fiber.new {
- result = :ok
- root.transfer
- }
- f.transfer
- assert_true f.alive?
- assert_equal :ok, result
-end
-
-assert('Break nested fiber with root fiber transfer') do
- root = Fiber.current
-
- result = nil
- f2 = nil
- f1 = Fiber.new {
- root.transfer(f2.transfer)
- result = :f1
- }
- f2 = Fiber.new {
- result = :to_root
- root.transfer :from_f2
- result = :f2
- }
- assert_equal :from_f2, f1.transfer
- assert_equal :to_root, result
- assert_equal :f2, f2.transfer
- assert_equal :f2, result
- assert_false f2.alive?
- assert_equal nil, f1.transfer
- assert_equal :f1, f1.transfer
- assert_equal :f1, result
- assert_false f1.alive?
-end
-
-assert('CRuby Fiber#transfer test.') do
- ary = []
- f2 = nil
- f1 = Fiber.new{
- ary << f2.transfer(:foo)
- :ok
- }
- f2 = Fiber.new{
- ary << f1.transfer(:baz)
- :ng
- }
- assert_equal :ok, f1.transfer
- assert_equal [:baz], ary
+ f = Fiber.new {
+ assert_raise(FiberError) { root.resume }
+ }
+ f.resume
+ assert_false f.alive?
+ end
+
+ assert('Fiber without block') do
+ assert_raise(ArgumentError) { Fiber.new }
+ end
+
+
+ assert('Transfer to self.') do
+ result = []
+ f = Fiber.new { result << :start; f.transfer; result << :end }
+ f.transfer
+ assert_equal [:start, :end], result
+
+ result = []
+ f = Fiber.new { result << :start; f.transfer; result << :end }
+ f.resume
+ assert_equal [:start, :end], result
+ end
+
+ assert('Resume transferred fiber') do
+ f = Fiber.new {
+ assert_raise(FiberError) { f.resume }
+ }
+ f.transfer
+ end
+
+ assert('Root fiber transfer.') do
+ result = nil
+ root = Fiber.current
+ f = Fiber.new {
+ result = :ok
+ root.transfer
+ }
+ f.transfer
+ assert_true f.alive?
+ assert_equal :ok, result
+ end
+
+ assert('Break nested fiber with root fiber transfer') do
+ root = Fiber.current
+
+ result = nil
+ f2 = nil
+ f1 = Fiber.new {
+ root.transfer(f2.transfer)
+ result = :f1
+ }
+ f2 = Fiber.new {
+ result = :to_root
+ root.transfer :from_f2
+ result = :f2
+ }
+ assert_equal :from_f2, f1.transfer
+ assert_equal :to_root, result
+ assert_equal :f2, f2.transfer
+ assert_equal :f2, result
+ assert_false f2.alive?
+ assert_equal nil, f1.transfer
+ assert_equal :f1, f1.transfer
+ assert_equal :f1, result
+ assert_false f1.alive?
+ end
+
+ assert('CRuby Fiber#transfer test.') do
+ ary = []
+ f2 = nil
+ f1 = Fiber.new{
+ ary << f2.transfer(:foo)
+ :ok
+ }
+ f2 = Fiber.new{
+ ary << f1.transfer(:baz)
+ :ng
+ }
+ assert_equal :ok, f1.transfer
+ assert_equal [:baz], ary
+ end
+ensure
+ $fiber_test_activity = nil
end
diff --git a/mrbgems/mruby-fiber/test/fiber2.rb b/mrbgems/mruby-fiber/test/fiber2.rb
new file mode 100644
index 000000000..a6ff8a791
--- /dev/null
+++ b/mrbgems/mruby-fiber/test/fiber2.rb
@@ -0,0 +1,155 @@
+# This file tests fiber switching crossing C functions
+
+unless RUBY_ENGINE == "mruby"
+ class Fiber
+ alias resume_by_c_func resume
+ alias resume_by_c_method resume
+
+ class << self
+ alias yield_by_c_func yield
+
+ def yield_by_c_method(*args)
+ raise FiberError, "ycan't cross C function boundary"
+ end
+ end
+ end
+
+ def Proc.c_tunnel
+ yield
+ end
+end
+
+begin
+ $fiber_test_activity = __FILE__
+
+ assert('Call Fiber#resume nested with C') do
+ assert_equal "ok1", Fiber.new { Fiber.new { "ok1" }.resume_by_c_func }.resume_by_c_func
+ assert_equal "ok2", Fiber.new { Fiber.new { "ok2" }.resume_by_c_method }.resume_by_c_func
+ assert_equal "ok3", Fiber.new { Fiber.new { "ok3" }.resume_by_c_func }.resume_by_c_method
+ assert_equal "ok4", Fiber.new { Fiber.new { "ok4" }.resume_by_c_method }.resume_by_c_method
+ assert_equal "ok5", Fiber.new { Proc.c_tunnel { Fiber.new { "ok5" }.resume_by_c_func } }.resume_by_c_func
+ assert_equal "ok6", Fiber.new { Proc.c_tunnel { Fiber.new { "ok6" }.resume_by_c_method } }.resume_by_c_func
+ assert_equal "ok7", Fiber.new { Proc.c_tunnel { Fiber.new { "ok7" }.resume_by_c_func } }.resume_by_c_method
+ assert_equal "ok8", Fiber.new { Proc.c_tunnel { Fiber.new { "ok8" }.resume_by_c_method } }.resume_by_c_method
+ assert_equal "ok9", Fiber.new { Proc.c_tunnel { Fiber.new { "ok9" }.resume } }.resume_by_c_func
+ assert_equal "ok10", Fiber.new { Proc.c_tunnel { Fiber.new { "ok10" }.resume } }.resume_by_c_method
+ end
+
+ assert('Call Fiber#resume and Fiber.yield mixed with C.') do
+ assert_equal 1, Fiber.new { Fiber.yield 1 }.resume_by_c_func
+ assert_equal 2, Fiber.new { Fiber.yield 2 }.resume_by_c_method
+ assert_equal 3, Fiber.new { Fiber.yield_by_c_func 3 }.resume
+ assert_equal 4, Fiber.new { Fiber.yield_by_c_func 4 }.resume_by_c_func
+ assert_equal 5, Fiber.new { Fiber.yield_by_c_func 5 }.resume_by_c_method
+ assert_raise(FiberError) { Fiber.new { Fiber.yield_by_c_method "bad" }.resume }
+ assert_raise(FiberError) { Fiber.new { Fiber.yield_by_c_method "bad" }.resume_by_c_func }
+ assert_raise(FiberError) { Fiber.new { Fiber.yield_by_c_method "bad" }.resume_by_c_method }
+
+ result = []
+ f1 = Fiber.new { result << Fiber.new { Fiber.yield 1; "bad" }.resume_by_c_func; 2 }
+ f2 = Fiber.new { result << f1.resume; 3 }
+ result << f2.resume
+ assert_equal [1, 2, 3], result
+
+ f1 = Fiber.new {
+ -> {
+ Fiber.yield 1
+ Fiber.yield_by_c_func 2
+ f2 = Fiber.new {
+ -> {
+ Fiber.yield_by_c_func 3
+ Fiber.yield 4
+ Fiber.yield_by_c_func 5
+ Fiber.yield 6
+ }.call
+ 7
+ }
+ Fiber.yield f2.resume_by_c_func
+ Fiber.yield f2.resume
+ Fiber.yield f2.resume_by_c_method
+ Fiber.yield f2.resume
+ Fiber.yield f2.resume_by_c_func
+ Fiber.yield 8
+ }.call
+ Fiber.yield 9
+ 10
+ }
+ result = []
+ 10.times { result << f1.resume }
+ assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], result
+ end
+
+ assert('Call Fiber#resume and Fiber.yield mixed with C and raising exceptions') do
+ f = Fiber.new do
+ raise ZeroDivisionError
+ rescue
+ Fiber.yield "rescue"
+ "pass1"
+ ensure
+ Fiber.yield "ensure"
+ end
+ assert_equal "rescue", f.resume_by_c_method
+ assert_equal "ensure", f.resume_by_c_method
+ assert_equal "pass1", f.resume_by_c_method
+ assert_raise(FiberError) { f.resume_by_c_method }
+
+ f = Fiber.new do
+ raise ZeroDivisionError
+ rescue
+ Fiber.yield "rescue"
+ "pass2"
+ ensure
+ Fiber.yield "ensure"
+ end
+ assert_equal "rescue", f.resume_by_c_func
+ assert_equal "ensure", f.resume_by_c_func
+ assert_equal "pass2", f.resume_by_c_func
+ assert_raise(FiberError) { f.resume_by_c_func }
+
+ f2 = Fiber.new do
+ -> do
+ Fiber.yield 1
+ raise "3"
+ ensure
+ Fiber.yield 2
+ end.call
+ "NOT REACH 1"
+ end
+ f1 = Fiber.new do
+ Fiber.yield f2.resume_by_c_func
+ begin
+ Fiber.yield f2.resume
+ Fiber.yield f2.resume_by_c_method
+ Fiber.yield "NOT REACH 2"
+ rescue => e
+ Fiber.yield e.message
+ Fiber.yield 4
+ ensure
+ Fiber.yield 5
+ end
+ Fiber.yield 6
+ 7
+ end
+ result = []
+ 7.times { result << f1.resume }
+ assert_equal [1, 2, "3", 4, 5, 6, 7], result
+ end
+
+ assert('Call Fiber#transfer with C') do
+ assert_equal "ok1", Fiber.new { Fiber.new { "ok1" }.resume_by_c_method }.transfer
+ assert_equal "ok2", Fiber.new { Fiber.new { "ok2" }.resume_by_c_func }.transfer
+ assert_raise(FiberError) { Proc.c_tunnel { Fiber.new { "BAD!" }.transfer } }
+
+ b = Fiber.current
+ a = Fiber.new {
+ Proc.c_tunnel {
+ Fiber.new {
+ b.transfer
+ }.resume
+ }
+ }
+ assert_raise(FiberError) { a.transfer }
+ end
+ensure
+ $fiber_test_activity = nil
+end
diff --git a/mrbgems/mruby-fiber/test/fibertest.c b/mrbgems/mruby-fiber/test/fibertest.c
new file mode 100644
index 000000000..69680aecb
--- /dev/null
+++ b/mrbgems/mruby-fiber/test/fibertest.c
@@ -0,0 +1,87 @@
+#include
+#include
+#include
+#include
+#include
+
+static mrb_value
+fiber_s_yield_by_c_func(mrb_state *mrb, mrb_value self)
+{
+ mrb_value a = mrb_get_arg1(mrb);
+ return mrb_fiber_yield(mrb, 1, &a);
+}
+
+static mrb_value
+fiber_s_yield_by_c_method(mrb_state *mrb, mrb_value self)
+{
+ mrb_value a = mrb_get_arg1(mrb);
+ return mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "yield"), 1, &a);
+}
+
+static mrb_value
+fiber_resume_by_c_func(mrb_state *mrb, mrb_value self)
+{
+ int ci_index = mrb->c->ci - mrb->c->cibase;
+ mrb_value ret = mrb_fiber_resume(mrb, self, 0, NULL);
+ if (ci_index != mrb->c->ci - mrb->c->cibase) {
+ mrb_raisef(mrb, E_EXCEPTION,
+ "[BUG] INVALID CI POSITION (expected %d, but actual %d) [BUG]",
+ (int)ci_index, (int)(mrb->c->ci - mrb->c->cibase));
+ }
+ return ret;
+}
+
+static mrb_value
+fiber_resume_by_c_method(mrb_state *mrb, mrb_value self)
+{
+ int ci_index = mrb->c->ci - mrb->c->cibase;
+ mrb_value ret = mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "resume"), 0, NULL);
+ if (ci_index != mrb->c->ci - mrb->c->cibase) {
+ mrb_raisef(mrb, E_EXCEPTION,
+ "[BUG] INVALID CI POSITION (expected %d, but actual %d) [BUG]",
+ (int)ci_index, (int)(mrb->c->ci - mrb->c->cibase));
+ }
+ return ret;
+}
+
+static mrb_value
+fiber_transfer_by_c(mrb_state *mrb, mrb_value self)
+{
+ return mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "transfer"), 0, NULL);
+}
+
+static mrb_value
+proc_s_c_tunnel(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b;
+ mrb_get_args(mrb, "&!", &b);
+ return mrb_yield_argv(mrb, b, 0, NULL);
+}
+
+static void
+check_activity(mrb_state *mrb)
+{
+ mrb_value act = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$fiber_test_activity"));
+ if (mrb_test(act)) {
+ act = mrb_obj_as_string(mrb, act);
+ fprintf(stderr, "\n\t<<<%s%.*s>>>\n",
+ "mruby VM has an unexpected outage in ", (int)RSTRING_LEN(act), RSTRING_PTR(act));
+ abort();
+ }
+}
+
+void
+mrb_mruby_fiber_gem_test(mrb_state *mrb)
+{
+ struct RClass *fiber_class = mrb_class_get(mrb, "Fiber");
+ mrb_define_class_method(mrb, fiber_class, "yield_by_c_func", fiber_s_yield_by_c_func, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, fiber_class, "yield_by_c_method", fiber_s_yield_by_c_method, MRB_ARGS_ANY());
+ mrb_define_method(mrb, fiber_class, "resume_by_c_func", fiber_resume_by_c_func, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fiber_class, "resume_by_c_method", fiber_resume_by_c_method, MRB_ARGS_NONE());
+ mrb_define_method(mrb, fiber_class, "transfer_by_c", fiber_transfer_by_c, MRB_ARGS_NONE());
+
+ mrb_define_class_method(mrb, mrb->proc_class, "c_tunnel", proc_s_c_tunnel, MRB_ARGS_NONE() | MRB_ARGS_BLOCK());
+
+ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$fiber_test_activity"), mrb_nil_value());
+ mrb_state_atexit(mrb, check_activity);
+}
diff --git a/src/vm.c b/src/vm.c
index 2d2a919c4..35e1650f9 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -1839,6 +1839,7 @@ RETRY_TRY_BLOCK:
}
recv = MRB_METHOD_FUNC(m)(mrb, recv);
}
+ mrb_assert(mrb->c->ci > mrb->c->cibase);
mrb_gc_arena_shrink(mrb, ai);
if (mrb->exc) goto L_RAISE;
ci = mrb->c->ci;
@@ -2245,7 +2246,10 @@ RETRY_TRY_BLOCK:
mrb->c = c->prev;
if (!mrb->c) mrb->c = mrb->root_c;
else c->prev = NULL;
- goto L_RAISE;
+ if (!c->vmexec) goto L_RAISE;
+ mrb->jmp = prev_jmp;
+ if (!prev_jmp) return mrb_obj_value(mrb->exc);
+ MRB_THROW(prev_jmp);
}
}