summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-fiber
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2013-05-20 17:54:07 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2013-05-20 17:54:07 +0900
commit5c0b9b703c9d1a08d7219b057b809bda4bc89f8a (patch)
treeab610a2988ef928c02bfc50e31aedc5a707f008e /mrbgems/mruby-fiber
parent35ee85164dd19143d4b47a34f13ad2bb71fed369 (diff)
downloadmruby-5c0b9b703c9d1a08d7219b057b809bda4bc89f8a.tar.gz
mruby-5c0b9b703c9d1a08d7219b057b809bda4bc89f8a.zip
primary mruby fiber implementation
Diffstat (limited to 'mrbgems/mruby-fiber')
-rw-r--r--mrbgems/mruby-fiber/mrbgem.rake4
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c179
2 files changed, 183 insertions, 0 deletions
diff --git a/mrbgems/mruby-fiber/mrbgem.rake b/mrbgems/mruby-fiber/mrbgem.rake
new file mode 100644
index 000000000..cb258adcb
--- /dev/null
+++ b/mrbgems/mruby-fiber/mrbgem.rake
@@ -0,0 +1,4 @@
+MRuby::Gem::Specification.new('mruby-fiber') do |spec|
+ spec.license = 'MIT'
+ spec.authors = 'mruby developers'
+end
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 <code>Fibder#resume</code> method.
+ * <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_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 <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)
+{
+ 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 (b<e) {
+ *b++ = *a++;
+ }
+ c->ci->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 <code>resume</code> is called next.
+ * Any arguments passed to the next <code>resume</code> will be the
+ * value that this <code>Fiber.yield</code> 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)
+{
+}