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);
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 4
0
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);
}
Exemplo n.º 5
0
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);
}
Exemplo n.º 6
0
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);
  }
}
Exemplo n.º 8
0
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);
  }
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
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();
}
Exemplo n.º 11
0
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();
}
Exemplo n.º 13
0
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();
}
Exemplo n.º 14
0
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;
}
Exemplo n.º 15
0
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>();
}
Exemplo n.º 16
0
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()
    );
  }
}