void AsioSession::exitContext() { assert(isInContext()); assert(!getCurrentContext()->isRunning()); m_contexts.back()->exit(m_contexts.size()); delete m_contexts.back(); m_contexts.pop_back(); assert(!isInContext() || getCurrentContext()->isRunning()); }
void AsioSession::enterContext() { assert(!isInContext() || getCurrentContext()->isRunning()); if (UNLIKELY(getCurrentContextIdx() >= MAX_CONTEXT_DEPTH)) { Object e(SystemLib::AllocInvalidOperationExceptionObject( "Unable to enter asio context: too many contexts open")); throw e; } m_contexts.push_back(new AsioContext()); assert(static_cast<context_idx_t>(m_contexts.size()) == m_contexts.size()); assert(isInContext()); assert(!getCurrentContext()->isRunning()); }
void AsioSession::exitContext() { assert(isInContext()); m_contexts.back()->exit(m_contexts.size()); req::destroy_raw(m_contexts.back()); m_contexts.pop_back(); }
void AsioSession::exitContext() { assert(isInContext()); m_contexts.back()->exit(m_contexts.size()); delete m_contexts.back(); m_contexts.pop_back(); }
void c_RescheduleWaitHandle::exitContext(context_idx_t ctx_idx) { assert(AsioSession::Get()->getContext(ctx_idx)); // stop before corrupting unioned data if (isFinished()) { return; } // not in a context being exited assert(getContextIdx() <= ctx_idx); if (getContextIdx() != ctx_idx) { return; } if (UNLIKELY(getState() != STATE_SCHEDULED)) { raise_fatal_error("Invariant violation: encountered unexpected state"); } // move us to the parent context setContextIdx(getContextIdx() - 1); // reschedule if still in a context if (isInContext()) { scheduleInContext(); } // recursively move all wait handles blocked by us getParentChain().exitContext(ctx_idx); }
void c_RescheduleWaitHandle::exitContext(context_idx_t ctx_idx) { assert(AsioSession::Get()->getContext(ctx_idx)); // stop before corrupting unioned data if (isFinished()) { return; } // not in a context being exited assert(getContextIdx() <= ctx_idx); if (getContextIdx() != ctx_idx) { return; } if (UNLIKELY(getState() != STATE_SCHEDULED)) { throw FatalErrorException( "Invariant violation: encountered unexpected state"); } // move us to the parent context setContextIdx(getContextIdx() - 1); // reschedule if still in a context if (isInContext()) { getContext()->schedule(this, m_queue, m_priority); } // recursively move all wait handles blocked by us for (auto pwh = getFirstParent(); pwh; pwh = pwh->getNextParent()) { pwh->exitContextBlocked(ctx_idx); } }
void c_ExternalThreadEventWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } // clean up once event is processed auto exit_guard = folly::makeGuard([&] { destroyEvent(); }); Cell result; try { m_event->unserialize(result); } catch (const Object& exception) { setException(exception.get()); return; } catch (...) { setException(AsioSession::Get()->getAbruptInterruptException().get()); throw; } assert(cellIsPlausible(result)); setResult(result); tvRefcountedDecRefCell(&result); }
void c_AsyncGeneratorWaitHandle::onUnblocked() { setState(STATE_SCHEDULED); if (isInContext()) { getContext()->schedule(this); } else { decRefObj(this); } }
void c_SleepWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } setResult(make_tv<KindOfNull>()); }
void c_RescheduleWaitHandle::initialize(uint32_t queue, uint32_t priority) { m_queue = queue; m_priority = priority; setState(STATE_SCHEDULED); if (isInContext()) { getContext()->schedule(this, m_queue, m_priority); } }
void c_SessionScopedWaitHandle::enterContextImpl(context_idx_t ctx_idx) { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } setContextIdx(ctx_idx); registerToContext(); }
void c_RescheduleWaitHandle::initialize(uint32_t queue, int64_t priority) { setState(STATE_SCHEDULED); setContextIdx(AsioSession::Get()->getCurrentContextIdx()); m_queue = queue; m_priority = priority; if (isInContext()) { scheduleInContext(); } }
void c_ExternalThreadEventWaitHandle::enterContextImpl(context_idx_t ctx_idx) { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } setContextIdx(ctx_idx); registerToContext(); }
void AsioSession::enterContext(ActRec* savedFP) { if (UNLIKELY(getCurrentContextIdx() >= MAX_CONTEXT_DEPTH)) { SystemLib::throwInvalidOperationExceptionObject( "Unable to enter asio context: too many contexts open"); } m_contexts.push_back(req::make_raw<AsioContext>(savedFP)); assert(static_cast<context_idx_t>(m_contexts.size()) == m_contexts.size()); assert(isInContext()); }
void c_ExternalThreadEventWaitHandle::initialize(AsioExternalThreadEvent* event, ObjectData* priv_data) { // this wait handle is owned by existence of unprocessed event incRefCount(); m_event = event; m_privData = priv_data; setState(STATE_WAITING); if (isInContext()) { m_index = getContext()->registerExternalThreadEvent(this); } }
void c_ContinuationWaitHandle::initialize(c_Continuation* continuation, uint16_t depth) { m_continuation = continuation; m_child = nullptr; m_privData = nullptr; m_depth = depth; setState(STATE_SCHEDULED); if (isInContext()) { getContext()->schedule(this); } }
void c_ExternalThreadEventWaitHandle::abandon(bool sweeping) { assert(getState() == STATE_WAITING); assert(hasExactlyOneRef() || sweeping); if (isInContext()) { unregisterFromContext(); } // clean up destroyEvent(sweeping); }
void c_SleepWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } auto const parentChain = getFirstParent(); setState(STATE_SUCCEEDED); tvWriteNull(&m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); }
void c_AsyncFunctionWaitHandle::onUnblocked() { setState(STATE_READY); if (isInContext()) { if (isFastResumable()) { getContext()->scheduleFast(this); } else { getContext()->schedule(this); } } else { decRefObj(this); } }
void c_SleepWaitHandle::initialize(int64_t usecs) { m_waketime = AsioSession::TimePoint::clock::now() + std::chrono::microseconds(usecs); incRefCount(); AsioSession::Get()->getSleepEventQueue().push(this); setState(STATE_WAITING); if (isInContext()) { registerToContext(); } }
Object c_SetResultToRefWaitHandle::ti_create(CObjRef wait_handle, VRefParam ref) { TypedValue* var_or_cell = ref->asTypedValue(); if (wait_handle.isNull()) { tvSetNull(*var_or_cell); return wait_handle; } if (!wait_handle.get()->getAttribute(ObjectData::IsWaitHandle)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected wait_handle to be an instance of WaitHandle or null")); throw e; } auto wh = static_cast<c_WaitHandle*>(wait_handle.get()); // succeeded? set result to ref and give back succeeded wait handle if (wh->isSucceeded()) { tvSet(wh->getResult(), *var_or_cell); return wh; } // failed? reset ref and give back failed wait handle if (wh->isFailed()) { tvSetNull(*var_or_cell); return wh; } // it's still running so it must be WaitableWaitHandle auto child = static_cast<c_WaitableWaitHandle*>(wh); // import child into the current context, detect cross-context cycles auto session = AsioSession::Get(); if (session->isInContext()) { child->enterContext(session->getCurrentContextIdx()); } // make sure the reference is properly boxed so that we can store cell pointer if (UNLIKELY(var_or_cell->m_type != KindOfRef)) { tvBox(var_or_cell); } p_SetResultToRefWaitHandle my_wh = NEWOBJ(c_SetResultToRefWaitHandle)(); my_wh->initialize(child, var_or_cell->m_data.pref); if (UNLIKELY(session->hasOnSetResultToRefCreateCallback())) { session->onSetResultToRefCreate(my_wh.get(), child); } return my_wh; }
void c_ExternalThreadEventWaitHandle::abandon(bool sweeping) { assert(getState() == STATE_WAITING); assert(getCount() == 1 || sweeping); if (isInContext()) { getContext()->unregisterExternalThreadEvent(m_index); } // event is abandoned, destroy it, unregister sweepable and decref ownership m_event->release(); m_event = nullptr; unregister(); decRefObj(this); }
void c_GenMapWaitHandle::onUnblocked() { assert(getState() == STATE_BLOCKED); for (; m_deps->iter_valid(m_iterPos); m_iterPos = m_deps->iter_next(m_iterPos)) { auto* current = tvAssertCell(m_deps->iter_value(m_iterPos)); assert(current->m_type == KindOfObject); assert(current->m_data.pobj->instanceof(c_WaitHandle::classof())); auto child = static_cast<c_WaitHandle*>(current->m_data.pobj); if (child->isSucceeded()) { auto k = m_deps->iter_key(m_iterPos); m_deps->set(k.asCell(), &child->getResult()); } else if (child->isFailed()) { putException(m_exception, child->getException()); } else { assert(child->instanceof(c_WaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); try { if (isInContext()) { child_wh->enterContext(getContextIdx()); } detectCycle(child_wh); blockOn(child_wh); return; } catch (const Object& cycle_exception) { putException(m_exception, cycle_exception.get()); } } } auto const parentChain = getFirstParent(); if (m_exception.isNull()) { setState(STATE_SUCCEEDED); tvWriteObject(m_deps.get(), &m_resultOrException); } else { setState(STATE_FAILED); tvWriteObject(m_exception.get(), &m_resultOrException); m_exception = nullptr; } m_deps = nullptr; UnblockChain(parentChain); decRefObj(this); }
void c_SleepWaitHandle::exitContext(context_idx_t ctx_idx) { assert(AsioSession::Get()->getContext(ctx_idx)); assert(getState() == STATE_WAITING); assert(getContextIdx() == ctx_idx); // Move us to the parent context. setContextIdx(getContextIdx() - 1); // Re-register if still in a context. if (isInContext()) { registerToContext(); } // Recursively move all wait handles blocked by us. getParentChain().exitContext(ctx_idx); }
void c_AsyncFunctionWaitHandle::exitContext(context_idx_t ctx_idx) { assert(AsioSession::Get()->getContext(ctx_idx)); // stop before corrupting unioned data if (isFinished()) { decRefObj(this); return; } // not in a context being exited assert(getContextIdx() <= ctx_idx); if (getContextIdx() != ctx_idx) { decRefObj(this); return; } switch (getState()) { case STATE_BLOCKED: // we were already ran due to duplicit scheduling; the context will be // updated thru exitContext() call on the non-blocked wait handle we // recursively depend on decRefObj(this); break; case STATE_READY: // Recursively move all wait handles blocked by us. getParentChain().exitContext(ctx_idx); // Move us to the parent context. setContextIdx(getContextIdx() - 1); // Reschedule if still in a context. if (isInContext()) { if (isFastResumable()) { getContext()->scheduleFast(this); } else { getContext()->schedule(this); } } else { decRefObj(this); } break; default: assert(false); } }
void c_SleepWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } auto parentChain = getParentChain(); setState(STATE_SUCCEEDED); tvWriteNull(&m_resultOrException); parentChain.unblock(); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnSleepSuccess())) { session->onSleepSuccess(this); } }
void c_ExternalThreadEventWaitHandle::initialize(AsioExternalThreadEvent* event, ObjectData* priv_data) { setState(STATE_WAITING); m_event = event; m_privData = priv_data; // this wait handle is owned by existence of unprocessed event incRefCount(); if (isInContext()) { registerToContext(); } auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventCreateCallback())) { session->onExternalThreadEventCreate(this); } }
void c_ExternalThreadEventWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } // clean up once event is processed auto exit_guard = folly::makeGuard([&] { destroyEvent(); }); Cell result; try { m_event->unserialize(result); } catch (const Object& exception) { assert(exception->instanceof(SystemLib::s_ExceptionClass)); auto const parentChain = getFirstParent(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventFailCallback())) { session->onExternalThreadEventFail(this, exception); } return; } catch (...) { auto const parentChain = getFirstParent(); setState(STATE_FAILED); tvWriteObject(AsioSession::Get()->getAbruptInterruptException(), &m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); throw; } assert(cellIsPlausible(result)); auto const parentChain = getFirstParent(); setState(STATE_SUCCEEDED); cellCopy(result, m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventSuccessCallback())) { session->onExternalThreadEventSuccess(this, tvAsCVarRef(&result)); } }
void c_GenVectorWaitHandle::initialize(const Object& exception, c_Vector* deps, int64_t iter_pos, c_WaitableWaitHandle* child) { m_exception = exception; m_deps = deps; m_iterPos = iter_pos; if (isInContext()) { try { child->enterContext(getContextIdx()); } catch (const Object& cycle_exception) { putException(m_exception, cycle_exception.get()); ++m_iterPos; onUnblocked(); return; } } blockOn(child); }
void c_ExternalThreadEventWaitHandle::exitContext(context_idx_t ctx_idx) { assert(AsioSession::Get()->getContext(ctx_idx)); assert(getContextIdx() == ctx_idx); assert(getState() == STATE_WAITING); // Move us to the parent context. setContextIdx(getContextIdx() - 1); // Re-register if still in a context. if (isInContext()) { registerToContext(); } // Recursively move all wait handles blocked by us. for (auto pwh = getFirstParent(); pwh; pwh = pwh->getNextParent()) { pwh->exitContextBlocked(ctx_idx); } }