void Fiber::start_on_stack() { #ifdef FIBER_ENABLED VM* state = VM::current(); Fiber* fib = Fiber::current(state); // Affix this fiber to this thread now. fib->state_ = state; fib->starter()->send(state, NULL, G(sym_call)); // GC has run! Don't use stack vars! fib = Fiber::current(state); fib->status_ = Fiber::eDead; Fiber* dest = fib->prev(); assert(!dest->nil_p()); dest->run(); dest->value(state, Qnil); state->set_current_fiber(dest); if(setcontext(dest->ucontext()) != 0) assert(0 && "fatal swapcontext() error"); assert(0 && "fatal start_on_stack error"); #else abort(); #endif }
Object* Fiber::resume(STATE, Arguments& args, CallFrame* calling_environment) { #ifdef FIBER_ENABLED if(!prev_->nil_p() || root_) return Primitives::failure(); Object* val = Qnil; if(args.total() == 1) { val = args.get_argument(0); } else if(args.total() > 1) { val = args.as_array(state); } value(state, val); Fiber* cur = Fiber::current(state); prev(state, cur); cur->sleep(calling_environment); run(); state->set_current_fiber(this); if(swapcontext(cur->ucontext(), context_) != 0) assert(0 && "fatal swapcontext() error"); // Back here when someone yields back to us! // Beware here, because the GC has probably run so GC pointers on the C++ stack // can't be accessed. cur = Fiber::current(state); return cur->value(); #else return Primitives::failure(); #endif }
Object* Fiber::resume(STATE, Arguments& args, CallFrame* calling_environment) { #ifdef FIBER_ENABLED if(status_ == Fiber::eDead) { Exception::fiber_error(state, "dead fiber called"); } if(!prev_->nil_p()) { Exception::fiber_error(state, "double resume"); } Array* val = args.as_array(state); value(state, val); Fiber* cur = Fiber::current(state); prev(state, cur); cur->sleep(calling_environment); run(); state->set_current_fiber(this); if(swapcontext(cur->ucontext(), context_) != 0) assert(0 && "fatal swapcontext() error"); // Back here when someone yields back to us! // Beware here, because the GC has probably run so GC pointers on the C++ stack // can't be accessed. cur = Fiber::current(state); if(!cur->exception()->nil_p()) { state->thread_state()->raise_exception(cur->exception()); cur->exception(state, (Exception*)Qnil); return 0; } Array* ret = cur->value(); if(ret->nil_p()) return Qnil; switch(ret->size()) { case 0: return Qnil; case 1: return ret->get(state, 0); default: return ret; } #else return Primitives::failure(); #endif }
void Fiber::start_on_stack() { #ifdef FIBER_ENABLED VM* state = VM::current(); Fiber* fib = Fiber::current(state); // Affix this fiber to this thread now. fib->state_ = state; Array* result = (Array*)Qnil; Object* obj = fib->starter()->send(state, NULL, G(sym_call), fib->value(), Qnil, false); // GC has run! Don't use stack vars! fib = Fiber::current(state); fib->status_ = Fiber::eDead; fib->set_ivar(state, state->symbol("@dead"), Qtrue); Fiber* dest = fib->prev(); assert(!dest->nil_p()); // Box this up so it's in a standard format at the point // of returning, so we can deal with it in the same way // as *args from #yield, #resume, and #transfer if(obj) { result = Array::create(state, 1); result->set(state, 0, obj); } else { if(state->thread_state()->raise_reason() == cException) { dest->exception(state, state->thread_state()->current_exception()); } } dest->run(); dest->value(state, result); state->set_current_fiber(dest); if(setcontext(dest->ucontext()) != 0) assert(0 && "fatal swapcontext() error"); assert(0 && "fatal start_on_stack error"); #else abort(); #endif }
Object* Fiber::s_yield(STATE, Arguments& args, CallFrame* calling_environment) { #ifdef FIBER_ENABLED Fiber* cur = Fiber::current(state); Fiber* dest_fib = cur->prev(); assert(cur != dest_fib); if(cur->root_) { Exception::fiber_error(state, "can't yield from root fiber"); } cur->prev(state, (Fiber*)Qnil); Array* val = args.as_array(state); dest_fib->value(state, val); cur->sleep(calling_environment); dest_fib->run(); state->set_current_fiber(dest_fib); if(swapcontext(cur->ucontext(), dest_fib->ucontext()) != 0) assert(0 && "fatal swapcontext() error"); // Back here when someone yields back to us! // Beware here, because the GC has probably run so GC pointers on the C++ stack // can't be accessed. cur = Fiber::current(state); Array* ret = cur->value(); if(ret->nil_p()) return Qnil; switch(ret->size()) { case 0: return Qnil; case 1: return ret->get(state, 0); default: return ret; } #else return Primitives::failure(); #endif }
Fiber* Fiber::create(STATE, Integer* i_stack_size, Object* callable) { #ifdef FIBER_ENABLED int stack_size = i_stack_size->to_native(); if(stack_size < 64 * 1024) { stack_size = 64 * 1024; } Fiber* fib = state->new_object<Fiber>(G(fiber)); fib->starter(state, callable); fib->prev_ = (Fiber*)Qnil; fib->top_ = 0; fib->root_ = false; fib->state_ = 0; fib->status_ = Fiber::eSleeping; fib->stack_size_ = stack_size; fib->stack_ = malloc(stack_size); fib->context_ = new ucontext_t; state->om->needs_finalization(fib, (FinalizerFunction)&Fiber::finalize); ucontext_t* ctx = fib->ucontext(); if(getcontext(ctx) != 0) assert(0 && "fatal getcontext() error"); ctx->uc_link = 0; ctx->uc_stack.ss_sp = (char *) fib->stack_; ctx->uc_stack.ss_size = stack_size; ctx->uc_stack.ss_flags = 0; makecontext(ctx, start_on_stack, 0); return fib; #else return reinterpret_cast<Fiber*>(Primitives::failure()); #endif }