mbed I/F binding for mruby
Dependents: mruby_mbed_web mirb_mbed
fiber.c
00001 #include "mruby.h" 00002 #include "mruby/array.h" 00003 #include "mruby/class.h" 00004 #include "mruby/proc.h" 00005 00006 #define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o)) 00007 00008 #define FIBER_STACK_INIT_SIZE 64 00009 #define FIBER_CI_INIT_SIZE 8 00010 00011 /* 00012 * call-seq: 00013 * Fiber.new{...} -> obj 00014 * 00015 * Creates a fiber, whose execution is suspend until it is explicitly 00016 * resumed using <code>Fiber#resume</code> method. 00017 * The code running inside the fiber can give up control by calling 00018 * <code>Fiber.yield</code> in which case it yields control back to caller 00019 * (the caller of the <code>Fiber#resume</code>). 00020 * 00021 * Upon yielding or termination the Fiber returns the value of the last 00022 * executed expression 00023 * 00024 * For instance: 00025 * 00026 * fiber = Fiber.new do 00027 * Fiber.yield 1 00028 * 2 00029 * end 00030 * 00031 * puts fiber.resume 00032 * puts fiber.resume 00033 * puts fiber.resume 00034 * 00035 * <em>produces</em> 00036 * 00037 * 1 00038 * 2 00039 * resuming dead fiber (FiberError) 00040 * 00041 * The <code>Fiber#resume</code> method accepts an arbitrary number of 00042 * parameters, if it is the first call to <code>resume</code> then they 00043 * will be passed as block arguments. Otherwise they will be the return 00044 * value of the call to <code>Fiber.yield</code> 00045 * 00046 * Example: 00047 * 00048 * fiber = Fiber.new do |first| 00049 * second = Fiber.yield first + 2 00050 * end 00051 * 00052 * puts fiber.resume 10 00053 * puts fiber.resume 14 00054 * puts fiber.resume 18 00055 * 00056 * <em>produces</em> 00057 * 00058 * 12 00059 * 14 00060 * resuming dead fiber (FiberError) 00061 * 00062 */ 00063 static mrb_value 00064 fiber_init(mrb_state *mrb, mrb_value self) 00065 { 00066 static const struct mrb_context mrb_context_zero = { 0 }; 00067 struct RFiber *f = fiber_ptr(self); 00068 struct mrb_context *c; 00069 struct RProc *p; 00070 mrb_callinfo *ci; 00071 mrb_value blk; 00072 size_t slen; 00073 00074 mrb_get_args(mrb, "&", &blk); 00075 00076 if (mrb_nil_p(blk)) { 00077 mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block"); 00078 } 00079 p = mrb_proc_ptr(blk); 00080 if (MRB_PROC_CFUNC_P(p)) { 00081 mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method"); 00082 } 00083 00084 f->cxt = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); 00085 *f->cxt = mrb_context_zero; 00086 c = f->cxt; 00087 00088 /* initialize VM stack */ 00089 slen = FIBER_STACK_INIT_SIZE; 00090 if (p->body.irep->nregs > slen) { 00091 slen += p->body.irep->nregs; 00092 } 00093 c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value)); 00094 c->stend = c->stbase + slen; 00095 c->stack = c->stbase; 00096 00097 #ifdef MRB_NAN_BOXING 00098 { 00099 mrb_value *p = c->stbase; 00100 mrb_value *pend = c->stend; 00101 00102 while (p < pend) { 00103 SET_NIL_VALUE(*p); 00104 p++; 00105 } 00106 } 00107 #else 00108 memset(c->stbase, 0, slen * sizeof(mrb_value)); 00109 #endif 00110 00111 /* copy receiver from a block */ 00112 c->stack[0] = mrb->c->stack[0]; 00113 00114 /* initialize callinfo stack */ 00115 c->cibase = (mrb_callinfo *)mrb_calloc(mrb, FIBER_CI_INIT_SIZE, sizeof(mrb_callinfo)); 00116 c->ciend = c->cibase + FIBER_CI_INIT_SIZE; 00117 c->ci = c->cibase; 00118 c->ci->stackent = c->stack; 00119 00120 /* adjust return callinfo */ 00121 ci = c->ci; 00122 ci->target_class = p->target_class; 00123 ci->proc = p; 00124 ci->pc = p->body.irep->iseq; 00125 ci->nregs = p->body.irep->nregs; 00126 ci[1] = ci[0]; 00127 c->ci++; /* push dummy callinfo */ 00128 00129 c->fib = f; 00130 c->status = MRB_FIBER_CREATED; 00131 00132 return self; 00133 } 00134 00135 static struct mrb_context* 00136 fiber_check(mrb_state *mrb, mrb_value fib) 00137 { 00138 struct RFiber *f = fiber_ptr(fib); 00139 00140 mrb_assert(f->tt == MRB_TT_FIBER); 00141 if (!f->cxt) { 00142 mrb_raise(mrb, E_FIBER_ERROR, "uninitialized Fiber"); 00143 } 00144 return f->cxt; 00145 } 00146 00147 static mrb_value 00148 fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len) 00149 { 00150 if (len == 0) return mrb_nil_value(); 00151 if (len == 1) return a[0]; 00152 return mrb_ary_new_from_values(mrb, len, a); 00153 } 00154 00155 /* mark return from context modifying method */ 00156 #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL 00157 00158 static mrb_value 00159 fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume) 00160 { 00161 struct mrb_context *c = fiber_check(mrb, self); 00162 mrb_callinfo *ci; 00163 00164 for (ci = c->ci; ci >= c->cibase; ci--) { 00165 if (ci->acc < 0) { 00166 mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); 00167 } 00168 } 00169 if (resume && c->status == MRB_FIBER_TRANSFERRED) { 00170 mrb_raise(mrb, E_FIBER_ERROR, "resuming transfered fiber"); 00171 } 00172 if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) { 00173 mrb_raise(mrb, E_FIBER_ERROR, "double resume"); 00174 } 00175 if (c->status == MRB_FIBER_TERMINATED) { 00176 mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); 00177 } 00178 mrb->c->status = resume ? MRB_FIBER_RESUMING : MRB_FIBER_TRANSFERRED; 00179 c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); 00180 if (c->status == MRB_FIBER_CREATED) { 00181 mrb_value *b = c->stack+1; 00182 mrb_value *e = b + len; 00183 00184 while (b<e) { 00185 *b++ = *a++; 00186 } 00187 c->cibase->argc = len; 00188 if (c->prev->fib) 00189 mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); 00190 mrb_write_barrier(mrb, (struct RBasic*)c->fib); 00191 c->status = MRB_FIBER_RUNNING; 00192 mrb->c = c; 00193 00194 MARK_CONTEXT_MODIFY(c); 00195 return c->ci->proc->env->stack[0]; 00196 } 00197 MARK_CONTEXT_MODIFY(c); 00198 if (c->prev->fib) 00199 mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); 00200 mrb_write_barrier(mrb, (struct RBasic*)c->fib); 00201 c->status = MRB_FIBER_RUNNING; 00202 mrb->c = c; 00203 return fiber_result(mrb, a, len); 00204 } 00205 00206 /* 00207 * call-seq: 00208 * fiber.resume(args, ...) -> obj 00209 * 00210 * Resumes the fiber from the point at which the last <code>Fiber.yield</code> 00211 * was called, or starts running it if it is the first call to 00212 * <code>resume</code>. Arguments passed to resume will be the value of 00213 * the <code>Fiber.yield</code> expression or will be passed as block 00214 * parameters to the fiber's block if this is the first <code>resume</code>. 00215 * 00216 * Alternatively, when resume is called it evaluates to the arguments passed 00217 * to the next <code>Fiber.yield</code> statement inside the fiber's block 00218 * or to the block value if it runs to completion without any 00219 * <code>Fiber.yield</code> 00220 */ 00221 static mrb_value 00222 fiber_resume(mrb_state *mrb, mrb_value self) 00223 { 00224 mrb_value *a; 00225 mrb_int len; 00226 00227 mrb_get_args(mrb, "*", &a, &len); 00228 return fiber_switch(mrb, self, len, a, TRUE); 00229 } 00230 00231 /* 00232 * call-seq: 00233 * fiber.alive? -> true or false 00234 * 00235 * Returns true if the fiber can still be resumed. After finishing 00236 * execution of the fiber block this method will always return false. 00237 */ 00238 static mrb_value 00239 fiber_alive_p(mrb_state *mrb, mrb_value self) 00240 { 00241 struct mrb_context *c = fiber_check(mrb, self); 00242 return mrb_bool_value(c->status != MRB_FIBER_TERMINATED); 00243 } 00244 00245 static mrb_value 00246 fiber_eq(mrb_state *mrb, mrb_value self) 00247 { 00248 mrb_value other; 00249 mrb_get_args(mrb, "o", &other); 00250 00251 if (mrb_type(other) != MRB_TT_FIBER) { 00252 return mrb_false_value(); 00253 } 00254 return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other)); 00255 } 00256 00257 /* 00258 * call-seq: 00259 * fiber.transfer(args, ...) -> obj 00260 * 00261 * Transfers control to reciever fiber of the method call. 00262 * Unlike <code>resume</code> the reciever wouldn't be pushed to call 00263 * stack of fibers. Instead it will switch to the call stack of 00264 * transferring fiber. 00265 * When resuming a fiber that was transferred to another fiber it would 00266 * cause double resume error. Though when the fiber is re-transferred 00267 * and <code>Fiber.yield</code> is called, the fiber would be resumable. 00268 */ 00269 static mrb_value 00270 fiber_transfer(mrb_state *mrb, mrb_value self) 00271 { 00272 struct mrb_context *c = fiber_check(mrb, self); 00273 mrb_value* a; 00274 mrb_int len; 00275 00276 mrb_get_args(mrb, "*", &a, &len); 00277 00278 if (c == mrb->root_c) { 00279 mrb->c->status = MRB_FIBER_TRANSFERRED; 00280 mrb->c = c; 00281 c->status = MRB_FIBER_RUNNING; 00282 MARK_CONTEXT_MODIFY(c); 00283 mrb_write_barrier(mrb, (struct RBasic*)c->fib); 00284 return fiber_result(mrb, a, len); 00285 } 00286 00287 if (c == mrb->c) { 00288 return fiber_result(mrb, a, len); 00289 } 00290 00291 return fiber_switch(mrb, self, len, a, FALSE); 00292 } 00293 00294 MRB_API mrb_value 00295 mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) 00296 { 00297 struct mrb_context *c = mrb->c; 00298 mrb_callinfo *ci; 00299 00300 for (ci = c->ci; ci >= c->cibase; ci--) { 00301 if (ci->acc < 0) { 00302 mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); 00303 } 00304 } 00305 if (!c->prev) { 00306 mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber"); 00307 } 00308 00309 c->prev->status = MRB_FIBER_RUNNING; 00310 c->status = MRB_FIBER_SUSPENDED; 00311 mrb->c = c->prev; 00312 c->prev = NULL; 00313 MARK_CONTEXT_MODIFY(mrb->c); 00314 mrb_write_barrier(mrb, (struct RBasic*)c->fib); 00315 return fiber_result(mrb, a, len); 00316 } 00317 00318 /* 00319 * call-seq: 00320 * Fiber.yield(args, ...) -> obj 00321 * 00322 * Yields control back to the context that resumed the fiber, passing 00323 * along any arguments that were passed to it. The fiber will resume 00324 * processing at this point when <code>resume</code> is called next. 00325 * Any arguments passed to the next <code>resume</code> will be the 00326 * value that this <code>Fiber.yield</code> expression evaluates to. 00327 */ 00328 static mrb_value 00329 fiber_yield(mrb_state *mrb, mrb_value self) 00330 { 00331 mrb_value *a; 00332 mrb_int len; 00333 00334 mrb_get_args(mrb, "*", &a, &len); 00335 return mrb_fiber_yield(mrb, len, a); 00336 } 00337 00338 /* 00339 * call-seq: 00340 * Fiber.current() -> fiber 00341 * 00342 * Returns the current fiber. If you are not running in the context of 00343 * a fiber this method will return the root fiber. 00344 */ 00345 static mrb_value 00346 fiber_current(mrb_state *mrb, mrb_value self) 00347 { 00348 if (!mrb->c->fib) { 00349 struct RFiber *f = (struct RFiber*)mrb_obj_alloc(mrb, MRB_TT_FIBER, mrb_class_ptr(self)); 00350 00351 f->cxt = mrb->c; 00352 mrb->c->fib = f; 00353 } 00354 return mrb_obj_value(mrb->c->fib); 00355 } 00356 00357 void 00358 mrb_mruby_fiber_gem_init(mrb_state* mrb) 00359 { 00360 struct RClass *c; 00361 00362 c = mrb_define_class(mrb, "Fiber", mrb->object_class); 00363 MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER); 00364 00365 mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE()); 00366 mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY()); 00367 mrb_define_method(mrb, c, "transfer", fiber_transfer, MRB_ARGS_ANY()); 00368 mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE()); 00369 mrb_define_method(mrb, c, "==", fiber_eq, MRB_ARGS_REQ(1)); 00370 00371 mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY()); 00372 mrb_define_class_method(mrb, c, "current", fiber_current, MRB_ARGS_NONE()); 00373 00374 mrb_define_class(mrb, "FiberError", mrb->eStandardError_class); 00375 } 00376 00377 void 00378 mrb_mruby_fiber_gem_final(mrb_state* mrb) 00379 { 00380 } 00381
Generated on Tue Jul 12 2022 18:00:34 by 1.7.2