mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Merge pull request #6106 from dearblue/fiber-limits-ease
Ease fiber limitations
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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 <code>Fiber.yield</code> statement inside the fiber's block
|
||||
* or to the block value if it runs to completion without any
|
||||
* <code>Fiber.yield</code>
|
||||
*
|
||||
* This method cannot be called from C using <code>mrb_funcall()</code>.
|
||||
* Use <code>mrb_fiber_resume()</code> 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);
|
||||
|
||||
+197
-191
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,87 @@
|
||||
#include <mruby.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user