From 5c0b9b703c9d1a08d7219b057b809bda4bc89f8a Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 20 May 2013 17:54:07 +0900 Subject: primary mruby fiber implementation --- mrbgems/mruby-fiber/src/fiber.c | 179 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 mrbgems/mruby-fiber/src/fiber.c (limited to 'mrbgems/mruby-fiber/src') diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c new file mode 100644 index 000000000..01410e4b3 --- /dev/null +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -0,0 +1,179 @@ +#include "mruby.h" +#include "mruby/array.h" +#include "mruby/class.h" +#include "mruby/proc.h" + +#define FIBER_STACK_INIT_SIZE 64 +#define FIBER_CI_INIT_SIZE 8 + +/* + * call-seq: + * Fiber.new{...} -> obj + * + * Creates an fiber, whose execution is suspend until it explicitly + * resumed using Fibder#resume method. + * resume. Arguments passed to resume will be the value of + * the Fiber.yield expression or will be passed as block + * parameters to the fiber's block if this is the first resume. + * + * Alternatively, when resume is called it evaluates to the arguments passed + * 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 + */ +static mrb_value +fiber_init(mrb_state *mrb, mrb_value self) +{ + static const struct mrb_context mrb_context_zero = { 0 }; + struct RFiber *f = (struct RFiber*)self.value.p; + struct mrb_context *c; + struct RProc *p; + mrb_callinfo *ci; + mrb_value blk; + + mrb_get_args(mrb, "&", &blk); + + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block"); + } + p = mrb_proc_ptr(blk); + if (MRB_PROC_CFUNC_P(p)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber from C defined method"); + } + + f->cxt = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); + *f->cxt = mrb_context_zero; + + /* initialize VM stack */ + c = f->cxt; + c->stbase = (mrb_value *)mrb_calloc(mrb, FIBER_STACK_INIT_SIZE, sizeof(mrb_value)); + c->stend = c->stbase + FIBER_STACK_INIT_SIZE; + c->stack = c->stbase; + + /* copy receiver from a block */ + c->stack[0] = mrb->c->stack[0]; + + /* initialize callinfo stack */ + c->cibase = (mrb_callinfo *)mrb_calloc(mrb, FIBER_CI_INIT_SIZE, sizeof(mrb_callinfo)); + c->ciend = c->cibase + FIBER_CI_INIT_SIZE; + c->ci = c->cibase; + + /* adjust return callinfo */ + ci = c->ci; + ci->target_class = p->target_class; + ci->proc = p; + ci->pc = p->body.irep->iseq; + ci->nregs = p->body.irep->nregs; + ci[1] = ci[0]; + c->ci++; /* push dummy callinfo */ + + return self; +} + +static struct mrb_context* +fiber_check(mrb_state *mrb, mrb_value fib) +{ + struct RFiber *f = (struct RFiber*)fib.value.p; + + if (!f->cxt) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized Fiber"); + } + return f->cxt; +} + +static mrb_value +fiber_result(mrb_state *mrb, mrb_value *a, int len) +{ + if (len == 0) return mrb_nil_value(); + if (len == 1) return a[0]; + return mrb_ary_new_from_values(mrb, len, a); +} + +/* + * call-seq: + * fiber.resume(args, ...) -> obj + * + * Resumes the fiber from the point at which the last Fiber.yield + * was called, or starts running it if it is the first call to + * resume. Arguments passed to resume will be the value of + * the Fiber.yield expression or will be passed as block + * parameters to the fiber's block if this is the first resume. + * + * Alternatively, when resume is called it evaluates to the arguments passed + * 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 + */ +static mrb_value +fiber_resume(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->prev) { /* first call */ + mrb_value *b = c->stack+1; + mrb_value *e = b + len; + + while (bci->argc = len; + c->prev = mrb->c; + mrb->c = c; + + return c->ci->proc->env->stack[0]; + } + if (c->ci == c->cibase) { + mrb_raise(mrb, E_RUNTIME_ERROR, "resuming dead Fiber"); + } + c->prev = mrb->c; + mrb->c = c; + return fiber_result(mrb, a, len); +} + +/* + * call-seq: + * Fiber.yield(args, ...) -> obj + * + * Yields control back to the context that resumed the fiber, passing + * along any arguments that were passed to it. The fiber will resume + * processing at this point when resume is called next. + * Any arguments passed to the next resume will be the + * value that this Fiber.yield expression evaluates to. + */ +static mrb_value +fiber_yield(mrb_state *mrb, mrb_value self) +{ + struct mrb_context *c = mrb->c; + mrb_value *a; + int len; + + if (!c->prev) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "can't yield from root Fiber"); + } + mrb_get_args(mrb, "*", &a, &len); + + mrb->c = c->prev; + return fiber_result(mrb, a, len); +} + +void +mrb_mruby_fiber_gem_init(mrb_state* mrb) +{ + struct RClass *c; + + c = mrb_define_class(mrb, "Fiber", mrb->object_class); + MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER); + + mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE()); + mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY()); + + mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY()); +} + +void +mrb_mruby_fiber_gem_final(mrb_state* mrb) +{ +} -- cgit v1.2.3