void c_AwaitAllWaitHandle::markAsFailed(const Object& exception) { auto child = &m_children[m_cur]; while (m_cur-- >= 0) { decRefObj(*(child--)); } auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); parentChain.unblock(); decRefObj(this); }
void HHVM_METHOD(ConditionWaitHandle, succeed, const Variant& result) { auto obj = wait_handle<c_ConditionWaitHandle>(this_); if (obj->isFinished()) { failAlreadyFinished(); } assert(obj->getState() == c_ConditionWaitHandle::STATE_BLOCKED); auto parentChain = obj->getParentChain(); obj->setState(c_ConditionWaitHandle::STATE_SUCCEEDED); cellDup(*result.asCell(), obj->m_resultOrException); parentChain.unblock(); }
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); }
void c_AwaitAllWaitHandle::blockOnCurrent() { auto child = m_children[m_cur]; assert(!child->isFinished()); if (checkCycle) { try { detectCycle(child); } catch (const Object& cycle_exception) { markAsFailed(cycle_exception); return; } } child->getParentChain() .addParent(m_blockable, AsioBlockable::Kind::AwaitAllWaitHandle); }
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_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 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_GenVectorWaitHandle::onUnblocked() { assert(getState() == STATE_BLOCKED); for (; m_iterPos < m_deps->size(); ++m_iterPos) { Cell* current = tvAssertCell(m_deps->at(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 result = child->getResult(); m_deps->set(m_iterPos, &result); } else if (child->isFailed()) { putException(m_exception, child->getException()); } else { assert(child->instanceof(c_WaitableWaitHandle::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 parentChain = getParentChain(); 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; parentChain.unblock(); decRefObj(this); }
void HHVM_METHOD(ConditionWaitHandle, fail, const Object& exception) { if (!exception->instanceof(SystemLib::s_ThrowableClass)) { SystemLib::throwInvalidArgumentExceptionObject( "Expected exception to be an instance of Throwable"); } auto obj = wait_handle<c_ConditionWaitHandle>(this_); if (obj->isFinished()) { failAlreadyFinished(); } assert(obj->getState() == c_ConditionWaitHandle::STATE_BLOCKED); auto parentChain = obj->getParentChain(); obj->setState(c_ConditionWaitHandle::STATE_FAILED); cellDup(make_tv<KindOfObject>(exception.get()), obj->m_resultOrException); parentChain.unblock(); }
void c_ConditionWaitHandle::onUnblocked() { decRefObj(m_child); m_child = nullptr; // Unblocked after notification. if (LIKELY(isFinished())) { decRefObj(this); return; } auto parentChain = getParentChain(); setState(STATE_FAILED); tvCopy( make_tv<KindOfObject>(getNotNotifiedException().detach()), m_resultOrException ); parentChain.unblock(); 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_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_ConditionWaitHandle::t_fail(const Variant& exception) { auto const cell = exception.asCell(); if (UNLIKELY( cell->m_type != KindOfObject || !cell->m_data.pobj->instanceof(SystemLib::s_ExceptionClass) )) { SystemLib::throwInvalidArgumentExceptionObject( "Expected exception to be an instance of Exception"); } if (isFinished()) { failAlreadyFinished(); } assert(getState() == STATE_BLOCKED); auto parentChain = getParentChain(); setState(STATE_FAILED); cellDup(make_tv<KindOfObject>(cell->m_data.pobj), m_resultOrException); parentChain.unblock(); }
bool c_SleepWaitHandle::cancel(const Object& exception) { if (getState() != STATE_WAITING) { return false; // already finished } if (isInContext()) { unregisterFromContext(); } auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); parentChain.unblock(); // this is technically a lie, since sleep failed auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnSleepSuccess())) { session->onSleepSuccess(this); } return true; }
void c_AwaitAllWaitHandle::onUnblocked() { assert(m_cur >= 0); assert(m_children[m_cur]->isFinished()); auto child = &m_children[m_cur]; do { decRefObj(*child); if (m_cur == 0) { auto parentChain = getParentChain(); setState(STATE_SUCCEEDED); tvWriteNull(&m_resultOrException); parentChain.unblock(); decRefObj(this); return; } --m_cur; --child; } while ((*child)->isFinished()); assert(child == &m_children[m_cur]); blockOnCurrent<true>(); }
bool c_SleepWaitHandle::process() { if (getState() == STATE_FAILED) { // sleep handle was cancelled, everything is taken care of return false; } 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); } return true; }
bool c_ExternalThreadEventWaitHandle::cancel(const Object& exception) { if (getState() != STATE_WAITING) { return false; // already finished } if (!m_event->cancel()) { return false; } // canceled; the processing thread will take care of cleanup if (isInContext()) { unregisterFromContext(); } // clean up once we finish canceling event auto exit_guard = folly::makeGuard([&] { // unregister Sweepable m_sweepable.unregister(); m_privData.reset(); // drop ownership by pending event (see initialize()) decRefObj(this); }); auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); parentChain.unblock(); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventFail())) { session->onExternalThreadEventFail(this, exception, 0); } return true; }
void c_ExternalThreadEventWaitHandle::process() { assertx(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } // Store the finish time of the underlying IO operation // So we can pass it in the finish callbacks // clean up once event is processed auto exit_guard = folly::makeGuard([&] { destroyEvent(); }); Cell result; try { try { m_event->unserialize(result); } catch (ExtendedException& exception) { exception.recomputeBacktraceFromWH(this); throw exception; } } catch (const Object& exception) { assertx(exception->instanceof(SystemLib::s_ThrowableClass)); throwable_recompute_backtrace_from_wh(exception.get(), this); auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); parentChain.unblock(); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventFail())) { session->onExternalThreadEventFail( this, exception, std::chrono::duration_cast<std::chrono::nanoseconds>( m_event->getFinishTime().time_since_epoch() ).count() ); } return; } catch (...) { auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(AsioSession::Get()->getAbruptInterruptException(), &m_resultOrException); parentChain.unblock(); throw; } assertx(cellIsPlausible(result)); auto parentChain = getParentChain(); setState(STATE_SUCCEEDED); cellCopy(result, m_resultOrException); parentChain.unblock(); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventSuccess())) { session->onExternalThreadEventSuccess( this, tvAsCVarRef(&result), std::chrono::duration_cast<std::chrono::nanoseconds>( m_event->getFinishTime().time_since_epoch() ).count() ); } }