void c_ContinuationWaitHandle::Create(c_Continuation* continuation) { assert(continuation); assert(continuation->m_waitHandle.isNull()); AsioSession* session = AsioSession::Get(); uint16_t depth = session->getCurrentWaitHandleDepth(); if (UNLIKELY(depth >= MAX_DEPTH)) { Object e(SystemLib::AllocInvalidOperationExceptionObject( "Asio stack overflow")); throw e; } if (UNLIKELY(continuation->started())) { Object e(SystemLib::AllocInvalidOperationExceptionObject( continuation->running() ? "Encountered an attempt to start currently running continuation" : "Encountered an attempt to start tainted continuation")); throw e; } continuation->m_waitHandle = NEWOBJ(c_ContinuationWaitHandle)(); continuation->m_waitHandle->initialize(continuation, depth + 1); // needs to be called after continuation->m_waitHandle is set if (UNLIKELY(session->hasOnContinuationCreateCallback())) { session->onContinuationCreate(continuation->m_waitHandle.get()); } }
Object c_GenMapWaitHandle::ti_create(const Variant& dependencies) { if (UNLIKELY(!dependencies.isObject() || dependencies.getObjectData()->getCollectionType() != Collection::MapType)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be an instance of Map")); throw e; } assert(dependencies.getObjectData()->instanceof(c_Map::classof())); auto deps = p_Map::attach(c_Map::Clone(dependencies.getObjectData())); for (ssize_t iter_pos = deps->iter_begin(); deps->iter_valid(iter_pos); iter_pos = deps->iter_next(iter_pos)) { auto* current = tvAssertCell(deps->iter_value(iter_pos)); if (UNLIKELY(!c_WaitHandle::fromCell(current))) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be a map of WaitHandle instances")); throw e; } } Object exception; for (ssize_t iter_pos = deps->iter_begin(); deps->iter_valid(iter_pos); iter_pos = deps->iter_next(iter_pos)) { auto* current = tvAssertCell(deps->iter_value(iter_pos)); 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 = deps->iter_key(iter_pos); deps->set(k.asCell(), &child->getResult()); } else if (child->isFailed()) { putException(exception, child->getException()); } else { assert(child->instanceof(c_WaitableWaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); p_GenMapWaitHandle my_wh = NEWOBJ(c_GenMapWaitHandle)(); my_wh->initialize(exception, deps.get(), iter_pos, child_wh); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnGenMapCreateCallback())) { session->onGenMapCreate(my_wh.get(), dependencies); } return my_wh; } } if (exception.isNull()) { return Object::attach(c_StaticWaitHandle::CreateSucceeded( make_tv<KindOfObject>(deps.detach()))); } else { return Object::attach(c_StaticWaitHandle::CreateFailed(exception.detach())); } }
void c_AsyncFunctionWaitHandle::markAsSucceeded() { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionSuccessCallback())) { session->onAsyncFunctionSuccess(this, cellAsCVarRef(m_resultOrException)); } done(); }
void c_AsyncFunctionWaitHandle::markAsFailed(const Object& exception) { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionFailCallback())) { session->onAsyncFunctionFail(this, exception); } setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); done(); }
Object c_GenVectorWaitHandle::ti_create(const Variant& dependencies) { if (UNLIKELY(!dependencies.isObject() || dependencies.getObjectData()->getCollectionType() != Collection::VectorType)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be an instance of Vector")); throw e; } assert(dependencies.getObjectData()->instanceof(c_Vector::classof())); auto deps = SmartObject<c_Vector>::attach( c_Vector::Clone(dependencies.getObjectData())); for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { Cell* current = deps->at(iter_pos); if (UNLIKELY(!c_WaitHandle::fromCell(current))) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be a vector of WaitHandle instances")); throw e; } } Object exception; for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { auto current = tvAssertCell(deps->at(iter_pos)); 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 result = child->getResult(); deps->set(iter_pos, &result); } else if (child->isFailed()) { putException(exception, child->getException()); } else { assert(child->instanceof(c_WaitableWaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); SmartObject<c_GenVectorWaitHandle> my_wh(newobj<c_GenVectorWaitHandle>()); my_wh->initialize(exception, deps.get(), iter_pos, child_wh); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnGenVectorCreateCallback())) { session->onGenVectorCreate(my_wh.get(), dependencies); } return my_wh; } } if (exception.isNull()) { return Object::attach(c_StaticWaitHandle::CreateSucceeded( make_tv<KindOfObject>(deps.detach()))); } else { return Object::attach(c_StaticWaitHandle::CreateFailed(exception.detach())); } }
void c_ContinuationWaitHandle::markAsFailed(CObjRef exception) { AsioSession* session = AsioSession::Get(); session->onFailed(exception); if (UNLIKELY(session->hasOnContinuationFailCallback())) { session->onContinuationFail(this, exception); } setException(exception.get()); m_continuation = nullptr; m_child = nullptr; }
void c_ContinuationWaitHandle::markAsSucceeded(const TypedValue* result) { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnContinuationSuccessCallback())) { session->onContinuationSuccess(this, tvAsCVarRef(result)); } setResult(result); // free m_continuation / m_child later, result may be stored there m_continuation = nullptr; m_child = nullptr; }
void c_AsyncFunctionWaitHandle::fail(ObjectData* exception) { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionFailCallback())) { session->onAsyncFunctionFail(this, exception); } auto const parentChain = getFirstParent(); setState(STATE_FAILED); tvWriteObject(exception, &m_resultOrException); UnblockChain(parentChain); decRefObj(this); }
void c_AsyncGeneratorWaitHandle::fail(ObjectData* exception) { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnResumableFailCallback())) { session->onResumableFail(this, exception); } auto parentChain = getParentChain(); setState(STATE_FAILED); cellCopy(make_tv<KindOfObject>(exception), m_resultOrException); parentChain.unblock(); decRefObj(m_generator); decRefObj(this); }
Object c_GenVectorWaitHandle::ti_create(CVarRef dependencies) { if (UNLIKELY(!dependencies.instanceof(c_Vector::s_cls))) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be an instance of Vector")); throw e; } assert(dynamic_cast<c_Vector*>(dependencies.getObjectData())); p_Vector deps = static_cast<c_Vector*>(dependencies.getObjectData())->clone(); for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { Cell* current = deps->at(iter_pos); if (UNLIKELY(!c_WaitHandle::fromCell(current))) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be a vector of WaitHandle instances")); throw e; } } Object exception; for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { Cell* current = tvAssertCell(deps->at(iter_pos)); assert(current->m_type == KindOfObject); assert(dynamic_cast<c_WaitHandle*>(current->m_data.pobj)); auto child = static_cast<c_WaitHandle*>(current->m_data.pobj); if (child->isSucceeded()) { cellSet(child->getResult(), *current); } else if (child->isFailed()) { putException(exception, child->getException()); } else { assert(dynamic_cast<c_WaitableWaitHandle*>(child)); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); p_GenVectorWaitHandle my_wh = NEWOBJ(c_GenVectorWaitHandle)(); my_wh->initialize(exception, deps.get(), iter_pos, child_wh); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnGenVectorCreateCallback())) { session->onGenVectorCreate(my_wh.get(), dependencies); } return my_wh; } } if (exception.isNull()) { return c_StaticResultWaitHandle::Create(make_tv<KindOfObject>(deps.get())); } else { return c_StaticExceptionWaitHandle::Create(exception.get()); } }
/** * Mark the wait handle as failed due to PHP exception. * * - consumes reference of the given Exception object */ void c_AsyncFunctionWaitHandle::fail(ObjectData* exception) { assert(isRunning()); assert(exception); assert(exception->instanceof(SystemLib::s_ExceptionClass)); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnResumableFailCallback())) { session->onResumableFail(this, exception); } auto const parentChain = getFirstParent(); setState(STATE_FAILED); cellCopy(make_tv<KindOfObject>(exception), m_resultOrException); UnblockChain(parentChain); decRefObj(this); }
Object c_SetResultToRefWaitHandle::ti_create(CObjRef wait_handle, VRefParam ref) { TypedValue* var_or_cell = ref->asTypedValue(); if (wait_handle.isNull()) { tvSet(make_tv<KindOfNull>(), *var_or_cell); return wait_handle; } if (!wait_handle.get()->instanceof(c_WaitHandle::classof())) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected wait_handle to be an instance of WaitHandle or null")); throw e; } c_WaitHandle* 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()) { tvSet(make_tv<KindOfNull>(), *var_or_cell); return wh; } // it's still running so it must be WaitableWaitHandle c_WaitableWaitHandle* child_wh = static_cast<c_WaitableWaitHandle*>(wh); // 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_wh, var_or_cell->m_data.pref); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnSetResultToRefCreateCallback())) { session->onSetResultToRefCreate(my_wh.get(), child_wh); } return my_wh; }
/** * Mark the wait handle as failed due to PHP exception. * * - consumes reference of the given Exception object */ void c_AsyncFunctionWaitHandle::fail(ObjectData* exception) { assert(isRunning()); assert(exception); assert(exception->instanceof(SystemLib::s_ThrowableClass)); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnResumableFail())) { try { session->onResumableFail(this, Object{exception}); } catch (...) { // TODO(#4557954) Make unwinder able to deal with new exceptions better. handle_destructor_exception("AsyncFunctionWaitHandle fail callback"); } } auto parentChain = getParentChain(); setState(STATE_FAILED); cellCopy(make_tv<KindOfObject>(exception), m_resultOrException); parentChain.unblock(); }
void c_AsyncFunctionWaitHandle::Create(c_Continuation* continuation) { assert(continuation); assert(continuation->m_label > 0); assert(continuation->m_waitHandle.isNull()); AsioSession* session = AsioSession::Get(); uint16_t depth = session->getCurrentWaitHandleDepth(); if (UNLIKELY(depth >= MAX_DEPTH)) { Object e(SystemLib::AllocInvalidOperationExceptionObject( "Asio stack overflow")); throw e; } Cell* value = tvAssertCell(continuation->m_value.asTypedValue()); assert(dynamic_cast<c_WaitableWaitHandle*>(c_WaitHandle::fromCell(value))); auto child = static_cast<c_WaitableWaitHandle*>(value->m_data.pobj); assert(!child->isFinished()); // import child into the current context, detect cross-context cycles if (session->isInContext()) { child->enterContext(session->getCurrentContextIdx()); } continuation->m_waitHandle = NEWOBJ(c_AsyncFunctionWaitHandle)(); continuation->m_waitHandle->initialize(continuation, child, depth + 1); // needs to be called after continuation->m_waitHandle is set if (UNLIKELY(session->hasOnAsyncFunctionCreateCallback())) { session->onAsyncFunctionCreate(continuation->m_waitHandle.get()); } }
// throws on context depth level overflows and cross-context cycles void c_WaitableWaitHandle::join() { EagerVMRegAnchor _; auto const savedFP = vmfp(); assert(!isFinished()); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnJoinCallback())) { session->onJoin(this); } // enter new asio context and set up guard that will exit once we are done session->enterContext(savedFP); auto exit_guard = folly::makeGuard([&] { session->exitContext(); }); // import this wait handle to the newly created context // throws if cross-context cycle found enterContext(session->getCurrentContextIdx()); // run queues until we are finished session->getCurrentContext()->runUntil(this); assert(isFinished()); }
Object c_ContinuationWaitHandle::ti_start(const char* cls, CObjRef continuation) { AsioSession* session = AsioSession::Get(); if (UNLIKELY(!continuation.instanceof(SystemLib::s_ContinuationClass))) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected continuation to be an instance of Continuation")); throw e; } uint16_t depth = session->getCurrentWaitHandleDepth(); if (UNLIKELY(depth >= MAX_DEPTH)) { Object e(SystemLib::AllocInvalidOperationExceptionObject( "Asio stack overflow")); throw e; } c_Continuation* cont = static_cast<c_Continuation*>(continuation.get()); if (!cont->m_waitHandle.isNull()) { if (session->isInContext()) { // throws if cross-context cycle found cont->m_waitHandle->enterContext(session->getCurrentContextIdx()); } return cont->m_waitHandle; } if (UNLIKELY(cont->m_index != -1)) { assert(cont->m_running); Object e(SystemLib::AllocInvalidOperationExceptionObject( "Encountered an attempt to start currently running continuation")); throw e; } p_ContinuationWaitHandle wh = NEWOBJ(c_ContinuationWaitHandle)(); wh->start(cont, depth + 1); if (UNLIKELY(session->hasOnStartedCallback())) { session->onStarted(wh); } return wh; }
void c_AsyncFunctionWaitHandle::run() { // may happen if scheduled in multiple contexts if (getState() != STATE_SCHEDULED) { return; } try { setState(STATE_RUNNING); // iterate continuation if (LIKELY(m_child->isSucceeded())) { // child succeeded, pass the result to the continuation m_continuation->call_send(m_child->getResult()); } else if (m_child->isFailed()) { // child failed, raise the exception inside continuation m_continuation->call_raise(m_child->getException()); } else { throw FatalErrorException( "Invariant violation: child neither succeeded nor failed"); } // continuation finished, retrieve result from its m_value if (m_continuation->done()) { markAsSucceeded(*m_continuation->m_value.asCell()); return; } retry: // save child Cell* value = tvAssertCell(m_continuation->m_value.asTypedValue()); assert(dynamic_cast<c_WaitableWaitHandle*>(c_WaitHandle::fromCell(value))); m_child = static_cast<c_WaitableWaitHandle*>(value->m_data.pobj); assert(!m_child->isFinished()); // import child into the current context, detect cross-context cycles try { m_child->enterContext(getContextIdx()); } catch (Object& e) { m_continuation->call_raise(e.get()); goto retry; } // detect cycles if (UNLIKELY(isDescendantOf(m_child.get()))) { Object e(createCycleException(m_child.get())); m_continuation->call_raise(e.get()); goto retry; } // on await callback AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionAwaitCallback())) { session->onAsyncFunctionAwait(this, m_child.get()); } // set up dependency blockOn(m_child.get()); } catch (const Object& exception) { // process exception thrown by the async function markAsFailed(exception); } catch (...) { // process C++ exception markAsFailed(AsioSession::Get()->getAbruptInterruptException()); throw; } }
Object c_GenMapWaitHandle::ti_create(const Variant& dependencies) { ObjectData* obj; if (UNLIKELY(!dependencies.isObject() || !(obj = dependencies.getObjectData())->isCollection() || obj->collectionType() != CollectionType::Map)) { SystemLib::throwInvalidArgumentExceptionObject( "Expected dependencies to be an instance of Map"); } assertx(obj->collectionType() == CollectionType::Map); auto deps = req::ptr<c_Map>::attach(c_Map::Clone(obj)); auto ctx_idx = std::numeric_limits<context_idx_t>::max(); for (ssize_t iter_pos = deps->iter_begin(); deps->iter_valid(iter_pos); iter_pos = deps->iter_next(iter_pos)) { auto* current = tvAssertCell(deps->iter_value(iter_pos)); auto const child = c_WaitHandle::fromCell(current); if (UNLIKELY(!child)) { SystemLib::throwInvalidArgumentExceptionObject( "Expected dependencies to be a map of WaitHandle instances"); } if (!child->isFinished()) { ctx_idx = std::min( ctx_idx, static_cast<c_WaitableWaitHandle*>(child)->getContextIdx() ); } } Object exception; for (ssize_t iter_pos = deps->iter_begin(); deps->iter_valid(iter_pos); iter_pos = deps->iter_next(iter_pos)) { auto* current = tvAssertCell(deps->iter_value(iter_pos)); 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 = deps->iter_key(iter_pos); auto result = child->getResult(); deps->set(k.asCell(), &result); } else if (child->isFailed()) { putException(exception, child->getException()); } else { assert(child->instanceof(c_WaitableWaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); auto my_wh = req::make<c_GenMapWaitHandle>(); my_wh->initialize(exception, deps.get(), iter_pos, ctx_idx, child_wh); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnGenMapCreate())) { session->onGenMapCreate(my_wh.get(), dependencies); } return Object(std::move(my_wh)); } } if (exception.isNull()) { return Object::attach(c_StaticWaitHandle::CreateSucceeded( make_tv<KindOfObject>(deps.detach()))); } else { return Object::attach(c_StaticWaitHandle::CreateFailed(exception.detach())); } }
// throws on context depth level overflows and cross-context cycles void c_WaitableWaitHandle::join() { AsioSession* session = AsioSession::Get(); assert(!isFinished()); assert(!session->isInContext() || session->getCurrentContext()->isRunning()); if (UNLIKELY(session->hasOnJoinCallback())) { session->onJoin(this); } // enter new asio context and set up guard that will exit once we are done session->enterContext(); assert(session->isInContext()); assert(!session->getCurrentContext()->isRunning()); try { // import this wait handle to the newly created context // throws if cross-context cycle found enterContext(session->getCurrentContextIdx()); // run queues until we are finished session->getCurrentContext()->runUntil(this); } catch (const Object& exception) { // recover from PHP exceptions; HPHP internal exceptions are deliberately // ignored as there is no easy way to recover from them session->exitContext(); throw; } session->exitContext(); assert(isFinished()); }
Object c_GenArrayWaitHandle::ti_create(CArrRef dependencies) { Array deps = dependencies->copy(); for (ssize_t iter_pos = deps->iter_begin(); iter_pos != ArrayData::invalid_index; iter_pos = deps->iter_advance(iter_pos)) { TypedValue* current = deps->nvGetValueRef(iter_pos); if (UNLIKELY(current->m_type == KindOfRef)) { tvUnbox(current); } if (!c_WaitHandle::fromTypedValue(current) && !IS_NULL_TYPE(current->m_type)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be an array of WaitHandle instances")); throw e; } } Object exception; for (ssize_t iter_pos = deps->iter_begin(); iter_pos != ArrayData::invalid_index; iter_pos = deps->iter_advance(iter_pos)) { TypedValue* current = deps->nvGetValueRef(iter_pos); if (IS_NULL_TYPE(current->m_type)) { // {uninit,null} yields null tvWriteNull(current); continue; } assert(current->m_type == KindOfObject); assert(dynamic_cast<c_WaitHandle*>(current->m_data.pobj)); auto child = static_cast<c_WaitHandle*>(current->m_data.pobj); if (child->isSucceeded()) { tvSetIgnoreRef(child->getResult(), current); } else if (child->isFailed()) { putException(exception, child->getException()); } else { assert(dynamic_cast<c_WaitableWaitHandle*>(child)); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); p_GenArrayWaitHandle my_wh = NEWOBJ(c_GenArrayWaitHandle)(); my_wh->initialize(exception, deps, iter_pos, child_wh); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnGenArrayCreateCallback())) { session->onGenArrayCreate(my_wh.get(), dependencies); } return my_wh; } } if (exception.isNull()) { TypedValue tv; tv.m_type = KindOfArray; tv.m_data.parr = deps.get(); return c_StaticResultWaitHandle::Create(&tv); } else { return c_StaticExceptionWaitHandle::Create(exception.get()); } }
void c_ContinuationWaitHandle::run() { // may happen if scheduled in multiple contexts if (getState() != STATE_SCHEDULED) { return; } try { setState(STATE_RUNNING); do { // iterate continuation if (m_child.isNull()) { // first iteration or null dependency m_continuation->call_next(); } else if (m_child->isSucceeded()) { // child succeeded, pass the result to the continuation m_continuation->call_send(m_child->getResult()); } else if (m_child->isFailed()) { // child failed, raise the exception inside continuation m_continuation->call_raise(m_child->getException()); } else { throw FatalErrorException( "Invariant violation: child neither succeeded nor failed"); } // continuation finished, retrieve result from its m_value if (m_continuation->done()) { markAsSucceeded(m_continuation->m_value.asTypedValue()); return; } // set up dependency TypedValue* value = m_continuation->m_value.asTypedValue(); if (IS_NULL_TYPE(value->m_type)) { // null dependency m_child = nullptr; } else { c_WaitHandle* child = c_WaitHandle::fromTypedValue(value); if (UNLIKELY(!child)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected yield argument to be an instance of WaitHandle")); throw e; } AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnContinuationYieldCallback())) { session->onContinuationYield(this, child); } m_child = child; } } while (m_child.isNull() || m_child->isFinished()); // we are blocked on m_child so it must be WaitableWaitHandle assert(dynamic_cast<c_WaitableWaitHandle*>(m_child.get())); blockOn(static_cast<c_WaitableWaitHandle*>(m_child.get())); } catch (const Object& exception) { // process exception thrown by generator or blockOn cycle detection markAsFailed(exception); } catch (...) { // process C++ exception markAsFailed(AsioSession::Get()->getAbruptInterruptException()); throw; } }
void c_AsyncFunctionWaitHandle::run() { // may happen if scheduled in multiple contexts if (getState() != STATE_SCHEDULED) { return; } try { setState(STATE_RUNNING); // resume async function if (LIKELY(m_child->isSucceeded())) { // child succeeded, pass the result to the async function g_context->resumeAsyncFunc(resumable(), m_child, m_child->getResult()); } else if (m_child->isFailed()) { // child failed, raise the exception inside the async function g_context->resumeAsyncFuncThrow(resumable(), m_child, m_child->getException()); } else { throw FatalErrorException( "Invariant violation: child neither succeeded nor failed"); } retry: // async function reached RetC, which already set m_resultOrException if (isSucceeded()) { m_child = nullptr; markAsSucceeded(); return; } // async function reached AsyncSuspend, which already set m_child assert(!m_child->isFinished()); assert(m_child->instanceof(c_WaitableWaitHandle::classof())); // import child into the current context, detect cross-context cycles try { child()->enterContext(getContextIdx()); } catch (Object& e) { g_context->resumeAsyncFuncThrow(resumable(), m_child, e.get()); goto retry; } // detect cycles if (UNLIKELY(isDescendantOf(child()))) { Object e(createCycleException(child())); g_context->resumeAsyncFuncThrow(resumable(), m_child, e.get()); goto retry; } // on await callback AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionAwaitCallback())) { session->onAsyncFunctionAwait(this, m_child); } // set up dependency setState(STATE_BLOCKED); blockOn(child()); } catch (const Object& exception) { // process exception thrown by the async function m_child = nullptr; markAsFailed(exception); } catch (...) { // process C++ exception m_child = nullptr; markAsFailed(AsioSession::Get()->getAbruptInterruptException()); throw; } }