diff options
Diffstat (limited to 'mrbgems/mruby-fiber/src/fiber.c')
| -rw-r--r-- | mrbgems/mruby-fiber/src/fiber.c | 75 |
1 files changed, 54 insertions, 21 deletions
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)); |
