diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2014-03-21 23:15:07 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2014-03-21 23:15:07 +0900 |
| commit | 73eac77b70c55ced8e7d51bfec58566bdede1b3d (patch) | |
| tree | f32ee8af53b605f28fc6b4bb04c0e7586f9d4cfa | |
| parent | 9bf983ae02b66c6acbae3768e26dedd41847ae6d (diff) | |
| parent | 6a458ab5a546461e04cd9a87174f6dd532d41926 (diff) | |
| download | mruby-73eac77b70c55ced8e7d51bfec58566bdede1b3d.tar.gz mruby-73eac77b70c55ced8e7d51bfec58566bdede1b3d.zip | |
Merge branch 'master' of github.com:mruby/mruby
| -rw-r--r-- | include/mruby.h | 1 | ||||
| -rw-r--r-- | mrbgems/mruby-fiber/src/fiber.c | 75 | ||||
| -rw-r--r-- | mrbgems/mruby-fiber/test/fiber.rb | 92 |
3 files changed, 147 insertions, 21 deletions
diff --git a/include/mruby.h b/include/mruby.h index 3e0e796d2..5de47ae84 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -72,6 +72,7 @@ enum mrb_fiber_state { MRB_FIBER_RUNNING, MRB_FIBER_RESUMING, MRB_FIBER_SUSPENDED, + MRB_FIBER_TRANSFERRED, MRB_FIBER_TERMINATED, }; diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index 433b11693..b077def8b 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -136,27 +136,10 @@ fiber_result(mrb_state *mrb, mrb_value *a, int len) /* mark return from context modifying method */ #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL -/* - * call-seq: - * fiber.resume(args, ...) -> obj - * - * Resumes the fiber from the point at which the last <code>Fiber.yield</code> - * was called, or starts running it if it is the first call to - * <code>resume</code>. Arguments passed to resume will be the value of - * the <code>Fiber.yield</code> expression or will be passed as block - * parameters to the fiber's block if this is the first <code>resume</code>. - * - * Alternatively, when resume is called it evaluates to the arguments passed - * 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> - */ static mrb_value -fiber_resume(mrb_state *mrb, mrb_value self) +fiber_switch(mrb_state *mrb, mrb_value self, int len, const mrb_value *a, mrb_bool resume) { struct mrb_context *c = fiber_check(mrb, self); - mrb_value *a; - int len; mrb_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { @@ -164,6 +147,9 @@ fiber_resume(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); } } + if (resume && c->status == MRB_FIBER_TRANSFERRED) { + mrb_raise(mrb, E_FIBER_ERROR, "resuming transfered fiber"); + } if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) { mrb_raise(mrb, E_FIBER_ERROR, "double resume"); } @@ -171,7 +157,8 @@ fiber_resume(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); } mrb_get_args(mrb, "*", &a, &len); - mrb->c->status = MRB_FIBER_RESUMING; + mrb->c->status = resume ? MRB_FIBER_RESUMING : MRB_FIBER_TRANSFERRED; + c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); if (c->status == MRB_FIBER_CREATED) { mrb_value *b = c->stack+1; mrb_value *e = b + len; @@ -180,7 +167,6 @@ fiber_resume(mrb_state *mrb, mrb_value self) *b++ = *a++; } c->cibase->argc = len; - c->prev = mrb->c; if (c->prev->fib) mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); @@ -191,7 +177,6 @@ fiber_resume(mrb_state *mrb, mrb_value self) return c->ci->proc->env->stack[0]; } MARK_CONTEXT_MODIFY(c); - c->prev = mrb->c; if (c->prev->fib) mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); @@ -202,6 +187,30 @@ fiber_resume(mrb_state *mrb, mrb_value self) /* * call-seq: + * fiber.resume(args, ...) -> obj + * + * Resumes the fiber from the point at which the last <code>Fiber.yield</code> + * was called, or starts running it if it is the first call to + * <code>resume</code>. Arguments passed to resume will be the value of + * the <code>Fiber.yield</code> expression or will be passed as block + * parameters to the fiber's block if this is the first <code>resume</code>. + * + * Alternatively, when resume is called it evaluates to the arguments passed + * 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> + */ +static mrb_value +fiber_resume(mrb_state *mrb, mrb_value self) +{ + mrb_value *a; + int len; + mrb_get_args(mrb, "*", &a, &len); + return fiber_switch(mrb, self, len, a, TRUE); +} + +/* + * call-seq: * fiber.alive? -> true or false * * Returns true if the fiber can still be resumed. After finishing @@ -226,6 +235,29 @@ fiber_eq(mrb_state *mrb, mrb_value self) return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other)); } +static mrb_value +fiber_transfer(mrb_state *mrb, mrb_value self) +{ + struct mrb_context *c = fiber_check(mrb, self); + mrb_value* a; + int len; + + mrb_get_args(mrb, "*", &a, &len); + + if (c == mrb->root_c) { + mrb->c->status = MRB_FIBER_TRANSFERRED; + mrb->c = c; + c->status = MRB_FIBER_RUNNING; + MARK_CONTEXT_MODIFY(c); + return fiber_result(mrb, a, len); + } + + if (c == mrb->c) { + return fiber_result(mrb, a, len); + } + + return fiber_switch(mrb, self, len, a, FALSE); +} mrb_value mrb_fiber_yield(mrb_state *mrb, int len, mrb_value *a) @@ -300,6 +332,7 @@ mrb_mruby_fiber_gem_init(mrb_state* mrb) mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE()); mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY()); + mrb_define_method(mrb, c, "transfer", fiber_transfer, MRB_ARGS_ANY()); mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE()); mrb_define_method(mrb, c, "==", fiber_eq, MRB_ARGS_REQ(1)); diff --git a/mrbgems/mruby-fiber/test/fiber.rb b/mrbgems/mruby-fiber/test/fiber.rb index e10878bf4..c2bae2259 100644 --- a/mrbgems/mruby-fiber/test/fiber.rb +++ b/mrbgems/mruby-fiber/test/fiber.rb @@ -8,6 +8,27 @@ assert('Fiber#resume') { f.resume(2) } +assert('Fiber#transfer') do + f2 = nil + f1 = Fiber.new do |v| + Fiber.yield v + f2.transfer + end + f2 = Fiber.new do + f1.transfer(1) + f1.transfer(1) + Fiber.yield 2 + end + assert_equal 1, f2.resume + assert_raise(FiberError) { f2.resume } + assert_equal 2, f2.transfer + assert_raise(FiberError) { f1.resume } + f1.transfer + f2.resume + assert_false f1.alive? + assert_false f2.alive? +end + assert('Fiber#alive?') { f = Fiber.new{ Fiber.yield } f.resume @@ -114,3 +135,74 @@ 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.resume + 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 { + Fiber.yield f2.resume + result = :f1 + } + f2 = Fiber.new { + result = :to_root + root.transfer :from_f2 + result = :f2 + } + assert_equal :from_f2, f1.resume + assert_equal :to_root, result + assert_equal :f2, f2.transfer + assert_equal :f2, result + assert_false f2.alive? + assert_equal :f1, f1.resume + 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 |
