Object* Fiber::resume(STATE, Arguments& args) { #ifdef RBX_FIBER_ENABLED if(!data()) { data(state->vm()->new_fiber_data(stack_size()->to_native())); } if(status() == Fiber::eDead || data()->dead_p()) { Exception::raise_fiber_error(state, "dead fiber called"); } if(!prev()->nil_p()) { Exception::raise_fiber_error(state, "double resume"); } if(data()->thread() && data()->thread() != state->vm()) { Exception::raise_fiber_error(state, "cross thread fiber resuming is illegal"); } Array* val = args.as_array(state); value(state, val); Fiber* cur = Fiber::current(state); prev(state, cur); cur->sleep(state); run(state); data()->switch_to(state, cur->data()); // 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); // TODO: clean up this and the following conditional. if(state->vm()->thread_interrupted_p(state)) { return NULL; } if(!cur->exception()->nil_p()) { state->raise_exception(cur->exception()); cur->exception(state, nil<Exception>()); return NULL; } Array* ret = cur->value(); if(ret->nil_p()) return cNil; switch(ret->size()) { case 0: return cNil; case 1: return ret->get(state, 0); default: return ret; } #else Exception::raise_not_implemented_error(state, "Fibers not supported on this platform"); #endif }
Object* Fiber::resume(STATE, Arguments& args, CallFrame* calling_environment) { #ifdef RBX_FIBER_ENABLED if(!data_) { data_ = state->vm()->new_fiber_data(); } if(status_ == Fiber::eDead || data_->dead_p()) { Exception::fiber_error(state, "dead fiber called"); } if(!prev_->nil_p()) { Exception::fiber_error(state, "double resume"); } if(data_->thread() && data_->thread() != state->vm()) { Exception::fiber_error(state, "cross thread fiber resuming is illegal"); } Array* val = args.as_array(state); value(state, val); Fiber* cur = Fiber::current(state); prev(state, cur); cur->sleep(calling_environment); run(); state->vm()->set_current_fiber(this); data_->switch_to(state, cur->data_); // 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); state->set_call_frame(cur->call_frame()); if(!cur->exception()->nil_p()) { state->raise_exception(cur->exception()); cur->exception(state, nil<Exception>()); return 0; } Array* ret = cur->value(); if(ret->nil_p()) return cNil; switch(ret->size()) { case 0: return cNil; case 1: return ret->get(state, 0); default: return ret; } #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 RBX_FIBER_ENABLED VM* vm = VM::current(); State state_obj(vm), *state = &state_obj; Fiber* fib = Fiber::current(state); // Reset the current fiber again to reset the stack limits so // we can properly detect stack overflows vm->set_current_fiber(fib); Array* result = nil<Array>(); Object* obj = fib->starter()->send(state, G(sym_call), fib->value(), cNil, false); // GC has run! Don't use stack vars! fib = Fiber::current(state); fib->status(Fiber::eDead); fib->dead(cTrue); fib->set_call_frame(state, 0); Fiber* dest = fib->prev(); // If this fiber has already been cleaned up, just ignore this if(!dest->data()) return; 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->vm()->thread_state()->raise_reason() == cException) { dest->exception(state, state->vm()->thread_state()->current_exception()); } } vm->metrics().system.fibers_destroyed++; dest->run(state); dest->value(state, result); dest->data()->switch_and_orphan(state, fib->data()); // TODO: CallFrame: return from this function rubinius::bug("returning from Fiber::start_on_stack"); #else rubinius::bug("Fibers not supported on this platform"); #endif }
void Fiber::start_on_stack() { #ifdef RBX_FIBER_ENABLED VM* vm = VM::current(); State state(vm); Fiber* fib = Fiber::current(&state); // Reset the current fiber again to reset the stack limits so // we can properly detect stack overflows vm->set_current_fiber(fib); Array* result = nil<Array>(); Object* obj = fib->starter()->send(&state, NULL, state.globals().sym_call.get(), fib->value(), cNil, false); // GC has run! Don't use stack vars! fib = Fiber::current(&state); fib->status_ = Fiber::eDead; fib->dead_ = cTrue; Fiber* dest = fib->prev(); // If this fiber has already been cleaned up, just ignore this if(!dest->data()) return; 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.vm()->thread_state()->raise_reason() == cException) { dest->exception(&state, state.vm()->thread_state()->current_exception()); } } dest->run(); dest->value(&state, result); state.vm()->set_current_fiber(dest); dest->data_->switch_and_orphan(&state, fib->data_); assert(0 && "fatal start_on_stack error"); #else rubinius::bug("Fibers not supported on this platform"); #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 }