/* * call-seq: * fiber.transfer(args, ...) -> obj * * Transfers control to receiver fiber of the method call. * Unlike <code>resume</code> the receiver wouldn't be pushed to call * stack of fibers. Instead it will switch to the call stack of * transferring fiber. * When resuming a fiber that was transferred to another fiber it would * cause double resume error. Though when the fiber is re-transferred * and <code>Fiber.yield</code> is called, the fiber would be resumable. */ static mrb_value fiber_transfer(mrb_state *mrb, mrb_value self) { struct mrb_context *c = fiber_check(mrb, self); mrb_value* a; mrb_int len; fiber_check_cfunc(mrb, mrb->c); 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); mrb_write_barrier(mrb, (struct RBasic*)c->fib); return fiber_result(mrb, a, len); } if (c == mrb->c) { return fiber_result(mrb, a, len); } return fiber_switch(mrb, self, len, a, FALSE, FALSE); }
void fiber_exit(int exit_code) { fiber_check(); __thread_fiber->exitcode = exit_code; __thread_fiber->running->status = FIBER_STATUS_EXITING; acl_fiber_switch(); }
/* see /usr/include/bits/errno.h for __errno_location */ int *__errno_location(void) { if (!acl_var_hook_sys_api) return __sys_errno(); if (__thread_fiber == NULL) fiber_check(); if (__thread_fiber->running) return &__thread_fiber->running->errnum; else return &__thread_fiber->original.errnum; }
/* * 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_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { if (ci->acc < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "can't cross C function boundary"); } } if (c->status == MRB_FIBER_RESUMED) { mrb_raise(mrb, E_RUNTIME_ERROR, "double resume"); } if (c->status == MRB_FIBER_TERMINATED) { mrb_raise(mrb, E_RUNTIME_ERROR, "resuming dead fiber"); } mrb_get_args(mrb, "*", &a, &len); mrb->c->status = MRB_FIBER_RESUMED; if (c->status == MRB_FIBER_CREATED) { mrb_value *b = c->stack+1; mrb_value *e = b + len; while (b<e) { *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); c->status = MRB_FIBER_RUNNING; mrb->c = c; MARK_CONTEXT_MODIFY(c); 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); c->status = MRB_FIBER_RUNNING; mrb->c = c; return fiber_result(mrb, a, len); }
static mrb_value fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume) { struct mrb_context *c = fiber_check(mrb, self); mrb_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { if (ci->acc < 0) { 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"); } if (c->status == MRB_FIBER_TERMINATED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); } 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; while (b<e) { *b++ = *a++; } c->cibase->argc = len; 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); c->status = MRB_FIBER_RUNNING; mrb->c = c; MARK_CONTEXT_MODIFY(c); return c->ci->proc->env->stack[0]; } MARK_CONTEXT_MODIFY(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); c->status = MRB_FIBER_RUNNING; mrb->c = c; return fiber_result(mrb, a, len); }
static mrb_value fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec) { struct mrb_context *c = fiber_check(mrb, self); struct mrb_context *old_c = mrb->c; mrb_value value; fiber_check_cfunc(mrb, c); if (resume && c->status == MRB_FIBER_TRANSFERRED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber"); } if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) { mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)"); } if (c->status == MRB_FIBER_TERMINATED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); } mrb->c->status = resume ? MRB_FIBER_RESUMED : 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; while (b<e) { *b++ = *a++; } c->cibase->argc = len; value = c->stack[0] = c->ci->proc->env->stack[0]; } else { value = fiber_result(mrb, a, len); } mrb_write_barrier(mrb, (struct RBasic*)c->fib); c->status = MRB_FIBER_RUNNING; mrb->c = c; if (vmexec) { c->vmexec = TRUE; value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc); mrb->c = old_c; } else { MARK_CONTEXT_MODIFY(c); } return value; }
void fiber_save_errno(void) { ACL_FIBER *curr; if (__thread_fiber == NULL) fiber_check(); if ((curr = __thread_fiber->running) == NULL) curr = &__thread_fiber->original; if (curr->flag & FIBER_F_SAVE_ERRNO) { //curr->flag &= ~FIBER_F_SAVE_ERRNO; return; } if (__sys_errno != NULL) acl_fiber_set_errno(curr, *__sys_errno()); else acl_fiber_set_errno(curr, errno); }
/* * call-seq: * fiber.alive? -> true or false * * Returns true if the fiber can still be resumed. After finishing * execution of the fiber block this method will always return false. */ static mrb_value fiber_alive_p(mrb_state *mrb, mrb_value self) { struct mrb_context *c = fiber_check(mrb, self); return mrb_bool_value(c->status != MRB_FIBER_TERMINATED); }
static ACL_FIBER *fiber_alloc(void (*fn)(ACL_FIBER *, void *), void *arg, size_t size) { ACL_FIBER *fiber; sigset_t zero; union cc_arg carg; ACL_RING *head; fiber_check(); #define APPL ACL_RING_TO_APPL /* try to reuse the fiber memory in dead queue */ head = acl_ring_pop_head(&__thread_fiber->dead); if (head == NULL) { fiber = (ACL_FIBER *) acl_mycalloc(1, sizeof(ACL_FIBER)); fiber->buff = (char *) acl_mymalloc(size); } else if ((fiber = APPL(head, ACL_FIBER, me))->size < size) fiber->buff = (char *) acl_myrealloc(fiber->buff, size); else size = fiber->size; fiber->errnum = 0; fiber->fn = fn; fiber->arg = arg; fiber->size = size; fiber->id = ++__thread_fiber->idgen; fiber->flag = 0; fiber->status = FIBER_STATUS_READY; carg.p = fiber; if (fiber->context == NULL) fiber->context = (ucontext_t *) acl_mymalloc(sizeof(ucontext_t)); sigemptyset(&zero); sigprocmask(SIG_BLOCK, &zero, &fiber->context->uc_sigmask); if (getcontext(fiber->context) < 0) acl_msg_fatal("%s(%d), %s: getcontext error: %s", __FILE__, __LINE__, __FUNCTION__, acl_last_serror()); fiber->context->uc_stack.ss_sp = fiber->buff + 8; fiber->context->uc_stack.ss_size = fiber->size - 64; #ifdef USE_JMP fiber->context->uc_link = NULL; #else fiber->context->uc_link = __thread_fiber->original.context; #endif #ifdef USE_VALGRIND /* avoding the valgrind's warning */ fiber->vid = VALGRIND_STACK_REGISTER(fiber->context->uc_stack.ss_sp, fiber->context->uc_stack.ss_sp + fiber->context->uc_stack.ss_size); #endif makecontext(fiber->context, (void(*)(void)) fiber_start, 2, carg.i[0], carg.i[1]); return fiber; }
ACL_FIBER *acl_fiber_running(void) { fiber_check(); return __thread_fiber->running; }