Array c_WaitableWaitHandle::t_getdependencystack() { Array result = Array::Create(); if (isFinished()) return result; hphp_hash_set<int64_t> visited; auto wait_handle = this; while (wait_handle != nullptr) { result.append(wait_handle); visited.insert(wait_handle->t_getid()); auto context_idx = wait_handle->getContextIdx(); // 1. find parent in the same context auto p = wait_handle->getFirstParent(); while (p) { if ((p->getContextIdx() == context_idx) && visited.find(p->t_getid()) == visited.end()) { wait_handle = p; break; } p = p->getNextParent(); } if (p) continue; // 2. cross the context boundary result.append(null_object); wait_handle = (context_idx > 1) ? AsioSession::Get()->getContext(context_idx - 1)->getCurrent() : nullptr; } return result; }
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_AsyncFunctionWaitHandle::ret(Cell& result) { auto const parentChain = getFirstParent(); setState(STATE_SUCCEEDED); cellCopy(result, m_resultOrException); UnblockChain(parentChain); decRefObj(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)); } }
/** * Mark the wait handle as failed due to unexpected abrupt interrupt. */ void c_AsyncFunctionWaitHandle::failCpp() { assert(isRunning()); auto const exception = AsioSession::Get()->getAbruptInterruptException(); auto const parentChain = getFirstParent(); setState(STATE_FAILED); tvWriteObject(exception, &m_resultOrException); UnblockChain(parentChain); decRefObj(this); }
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_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_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); }
/** * 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); }
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_SCHEDULED: // Recursively move all wait handles blocked by us. for (auto pwh = getFirstParent(); pwh; pwh = pwh->getNextParent()) { pwh->exitContextBlocked(ctx_idx); } // Move us to the parent context. setContextIdx(getContextIdx() - 1); // Reschedule if still in a context. if (isInContext()) { getContext()->schedule(this); } else { decRefObj(this); } break; default: assert(false); } }
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); } }
void c_BlockableWaitHandle::exitContextBlocked(context_idx_t ctx_idx) { assert(getState() == STATE_BLOCKED); assert(AsioSession::Get()->getContext(ctx_idx)); // not in a context being exited assert(getContextIdx() <= ctx_idx); if (getContextIdx() != ctx_idx) { return; } // move us to the parent context setContextIdx(getContextIdx() - 1); // recursively move all wait handles blocked by us for (auto pwh = getFirstParent(); pwh; pwh = pwh->getNextParent()) { pwh->exitContextBlocked(ctx_idx); } }
Array c_WaitableWaitHandle::t_getdependencystack() { Array result = Array::Create(); if (isFinished()) return result; hphp_hash_set<int64_t> visited; auto wait_handle = this; auto session = AsioSession::Get(); while (wait_handle != nullptr) { result.append(wait_handle); visited.insert(wait_handle->t_getid()); auto context_idx = wait_handle->getContextIdx(); // 1. find parent in the same context auto p = wait_handle->getFirstParent(); while (p) { if ((p->getContextIdx() == context_idx) && visited.find(p->t_getid()) == visited.end()) { wait_handle = p; break; } p = p->getNextParent(); } if (p) continue; // 2. cross the context boundary auto context = session->getContext(context_idx); if (!context) { break; } wait_handle = c_ResumableWaitHandle::getRunning(context->getSavedFP()); auto target_context_idx = wait_handle ? wait_handle->getContextIdx() : 0; while (context_idx > target_context_idx) { --context_idx; result.append(null_object); } } return result; }