// throws on context depth level overflows and cross-context cycles void c_WaitableWaitHandle::join() { JIT::EagerVMRegAnchor _; 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(); auto exit_guard = folly::makeGuard([&] { session->exitContext(); }); assert(session->isInContext()); assert(!session->getCurrentContext()->isRunning()); // 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()); }
// 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()); }
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()); } }
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; }