void c_AsyncGeneratorWaitHandle::enterContextImpl(context_idx_t ctx_idx) {
  switch (getState()) {
    case STATE_BLOCKED:
      // enter child into new context recursively
      assert(m_child);
      m_child->enterContext(ctx_idx);
      setContextIdx(ctx_idx);
      break;

    case STATE_SCHEDULED:
      // reschedule so that we get run
      setContextIdx(ctx_idx);
      getContext()->schedule(this);
      incRefCount();
      break;

    case STATE_RUNNING: {
      Object e(SystemLib::AllocInvalidOperationExceptionObject(
          "Detected cross-context dependency cycle. You are trying to depend "
          "on something that is running you serially."));
      throw e;
    }

    default:
      assert(false);
  }
}
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)) {
    raise_fatal_error("Invariant violation: encountered unexpected state");
  }

  // move us to the parent context
  setContextIdx(getContextIdx() - 1);

  // reschedule if still in a context
  if (isInContext()) {
    scheduleInContext();
  }

  // recursively move all wait handles blocked by us
  getParentChain().exitContext(ctx_idx);
}
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);
  }
}
示例#4
0
void c_AwaitAllWaitHandle::enterContextImpl(context_idx_t ctx_idx) {
  assert(getState() == STATE_BLOCKED);
  assert(m_cur >= 0);

  // recursively import current child
  m_children[m_cur]->enterContext(ctx_idx);

  // import ourselves
  setContextIdx(ctx_idx);

  // try to import other children
  auto cur = m_cur;
  auto child = &m_children[cur];

  while (cur > 0) {
    --cur;
    --child;

    if ((*child)->isFinished()) {
      continue;
    }

    try {
      (*child)->enterContext(ctx_idx);
    } catch (const Object& cycle_exception) {
      // exception will be eventually processed by onUnblocked()
    }
  }
}
void c_GenArrayWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  assert(getState() == STATE_BLOCKED);

  // recursively import current child
  {
    assert(m_iterPos != ArrayData::invalid_index);
    TypedValue* current = m_deps->nvGetValueRef(m_iterPos);

    assert(current->m_type == KindOfObject);
    assert(dynamic_cast<c_WaitableWaitHandle*>(current->m_data.pobj));
    auto child_wh = static_cast<c_WaitableWaitHandle*>(current->m_data.pobj);
    child_wh->enterContext(ctx_idx);
  }

  // import ourselves
  setContextIdx(ctx_idx);

  // try to import other children
  try {
    for (ssize_t iter_pos = m_deps->iter_advance(m_iterPos);
         iter_pos != ArrayData::invalid_index;
         iter_pos = m_deps->iter_advance(iter_pos)) {

      TypedValue* current = m_deps->nvGetValueRef(iter_pos);
      if (IS_NULL_TYPE(current->m_type)) {
        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->isFinished()) {
        continue;
      }

      assert(dynamic_cast<c_WaitableWaitHandle*>(child));
      auto child_wh = static_cast<c_WaitableWaitHandle*>(child);
      child_wh->enterContext(ctx_idx);
    }
  } catch (const Object& cycle_exception) {
    // exception will be eventually processed by onUnblocked()
  }
}
void c_ExternalThreadEventWaitHandle::enterContextImpl(context_idx_t ctx_idx) {
  assert(getState() == STATE_WAITING);

  if (isInContext()) {
    unregisterFromContext();
  }

  setContextIdx(ctx_idx);
  registerToContext();
}
void c_SessionScopedWaitHandle::enterContextImpl(context_idx_t ctx_idx) {
  assert(getState() == STATE_WAITING);

  if (isInContext()) {
    unregisterFromContext();
  }

  setContextIdx(ctx_idx);
  registerToContext();
}
void c_RescheduleWaitHandle::initialize(uint32_t queue, int64_t priority) {
  setState(STATE_SCHEDULED);
  setContextIdx(AsioSession::Get()->getCurrentContextIdx());
  m_queue = queue;
  m_priority = priority;

  if (isInContext()) {
    scheduleInContext();
  }
}
void c_AsyncGeneratorWaitHandle::initialize(AsyncGenerator* gen,
                                            c_WaitableWaitHandle* child) {
  setState(STATE_BLOCKED);
  setContextIdx(child->getContextIdx());
  m_generator = gen;
  m_generator->toObject()->incRefCount();
  m_child = child;
  m_child->getParentChain()
    .addParent(m_blockable, AsioBlockable::Kind::AsyncGeneratorWaitHandle);
  incRefCount();
}
示例#10
0
void c_GenMapWaitHandle::initialize(const Object& exception, c_Map* deps, ssize_t iter_pos, c_WaitableWaitHandle* child) {
  setState(STATE_BLOCKED);
  setContextIdx(child->getContextIdx());
  m_exception = exception;
  m_deps = deps;
  m_iterPos = iter_pos;

  child->getParentChain()
    .addParent(m_blockable, AsioBlockable::Kind::GenMapWaitHandle);
  incRefCount();
}
c_WaitableWaitHandle::c_WaitableWaitHandle(Class* cb)
    : c_WaitHandle(cb)
    , m_creator(AsioSession::Get()->getCurrentWaitHandle())
    , m_firstParent(nullptr) {
  setState(STATE_NEW);
  setContextIdx(AsioSession::Get()->getCurrentContextIdx());

  // ref creator
  if (m_creator) {
    m_creator->incRefCount();
  }
}
void c_GenVectorWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  assert(getState() == STATE_BLOCKED);

  // recursively import current child
  {
    assert(m_iterPos < m_deps->size());
    Cell* current = tvAssertCell(m_deps->at(m_iterPos));

    assert(current->m_type == KindOfObject);
    assert(dynamic_cast<c_WaitableWaitHandle*>(current->m_data.pobj));
    auto child_wh = static_cast<c_WaitableWaitHandle*>(current->m_data.pobj);
    child_wh->enterContext(ctx_idx);
  }

  // import ourselves
  setContextIdx(ctx_idx);

  // try to import other children
  try {
    for (int64_t iter_pos = m_iterPos + 1;
         iter_pos < m_deps->size();
         ++iter_pos) {

      Cell* current = tvAssertCell(m_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->isFinished()) {
        continue;
      }

      assert(dynamic_cast<c_WaitableWaitHandle*>(child));
      auto child_wh = static_cast<c_WaitableWaitHandle*>(child);
      child_wh->enterContext(ctx_idx);
    }
  } catch (const Object& cycle_exception) {
    // exception will be eventually processed by onUnblocked()
  }
}
void c_ContinuationWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  switch (getState()) {
    case STATE_BLOCKED:
      // enter child into new context recursively
      assert(dynamic_cast<c_WaitableWaitHandle*>(m_child.get()));
      static_cast<c_WaitableWaitHandle*>(m_child.get())->enterContext(ctx_idx);
      setContextIdx(ctx_idx);
      break;

    case STATE_SCHEDULED:
      // reschedule so that we get run
      setContextIdx(ctx_idx);
      getContext()->schedule(this);
      break;

    case STATE_RUNNING: {
      Object e(SystemLib::AllocInvalidOperationExceptionObject(
          "Detected cross-context dependency cycle. You are trying to depend "
          "on something that is running you serially."));
      throw e;
    }

    default:
      assert(false);
  }
}
示例#14
0
void c_AwaitAllWaitHandle::initialize(context_idx_t ctx_idx) {
  setState(STATE_BLOCKED);
  setContextIdx(ctx_idx);

  if (UNLIKELY(AsioSession::Get()->hasOnAwaitAllCreate())) {
    auto vector = req::make<c_Vector>();
    for (int32_t idx = m_cap - 1; idx >= 0; --idx) {
      TypedValue child = make_tv<KindOfObject>(m_children[idx].m_child);
      vector->add(&child);
    }
    AsioSession::Get()->onAwaitAllCreate(this, Variant(std::move(vector)));
  }

  incRefCount();
}
示例#15
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);
}
示例#16
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);
}
示例#17
0
void c_ConditionWaitHandle::initialize(c_WaitableWaitHandle* child) {
  assert(!child->isFinished());

  setState(STATE_BLOCKED);
  setContextIdx(child->getContextIdx());
  m_child = child;
  m_child->incRefCount();
  m_child->getParentChain()
    .addParent(m_blockable, AsioBlockable::Kind::ConditionWaitHandle);
  incRefCount();

  auto const session = AsioSession::Get();
  if (UNLIKELY(session->hasOnConditionCreate())) {
    session->onConditionCreate(this, child);
  }
}
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);
  }
}
示例#19
0
void c_GenVectorWaitHandle::initialize(
  const Object& exception,
  c_Vector* deps,
  int64_t iter_pos,
  context_idx_t ctx_idx,
  c_WaitableWaitHandle* child
) {
  setState(STATE_BLOCKED);
  setContextIdx(ctx_idx);
  m_exception = exception;
  m_deps = deps;
  m_iterPos = iter_pos;

  child->getParentChain()
    .addParent(m_blockable, AsioBlockable::Kind::GenVectorWaitHandle);
  incRefCount();
}
void c_AwaitAllWaitHandle::initialize(context_idx_t ctx_idx) {
  setState(STATE_BLOCKED);
  setContextIdx(ctx_idx);
  assert(m_cur >= 0);

  if (UNLIKELY(AsioSession::Get()->hasOnAwaitAllCreateCallback())) {
    auto vector = makeSmartPtr<c_Vector>();
    for (int32_t idx = m_cur; idx >= 0; --idx) {
      TypedValue child = make_tv<KindOfObject>(m_children[idx]);
      vector->add(&child);
    }
    AsioSession::Get()->onAwaitAllCreate(this, Variant(std::move(vector)));
  }

  blockOnCurrent<false>();
  incRefCount();
}
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_SetResultToRefWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  assert(getState() == STATE_BLOCKED);

  m_child->enterContext(ctx_idx);
  setContextIdx(ctx_idx);
}
void c_ExternalThreadEventWaitHandle::initialize(
  AsioExternalThreadEvent* event,
  ObjectData* priv_data
) {
  auto const session = AsioSession::Get();
  setState(STATE_WAITING);
  setContextIdx(session->getCurrentContextIdx());
  m_event = event;
  m_privData = priv_data;

  if (isInContext()) {
    registerToContext();
  }

  if (UNLIKELY(session->hasOnExternalThreadEventCreate())) {
    session->onExternalThreadEventCreate(this);
  }
}
void c_RescheduleWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  assert(getState() == STATE_SCHEDULED);

  setContextIdx(ctx_idx);
  getContext()->schedule(this, m_queue, m_priority);
}
示例#25
0
void c_SleepWaitHandle::initialize(int64_t usecs) {
  auto const session = AsioSession::Get();
  setState(STATE_WAITING);
  setContextIdx(session->getCurrentContextIdx());
  m_waketime =
    AsioSession::TimePoint::clock::now() +
    std::chrono::microseconds(usecs);

  incRefCount();

  session->enqueueSleepEvent(this);

  if (isInContext()) {
    registerToContext();
  }

  if (UNLIKELY(session->hasOnSleepCreate())) {
    session->onSleepCreate(this);
  }
}
void c_ContinuationWaitHandle::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;
  }

  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
      break;

    case STATE_SCHEDULED:
      // move us to the parent context
      setContextIdx(getContextIdx() - 1);

      // reschedule if still in a context
      if (isInContext()) {
        getContext()->schedule(this);
      }

      // recursively move all wait handles blocked by us
      for (auto pwh = getFirstParent(); pwh; pwh = pwh->getNextParent()) {
        pwh->exitContextBlocked(ctx_idx);
      }

      break;

    default:
      assert(false);
  }
}
示例#27
0
void c_GenVectorWaitHandle::enterContextImpl(context_idx_t ctx_idx) {
  assert(getState() == STATE_BLOCKED);

  // recursively import current child
  {
    assert(m_iterPos < m_deps->size());
    Cell* current = tvAssertCell(m_deps->at(m_iterPos));

    assert(current->m_type == KindOfObject);
    assert(current->m_data.pobj->instanceof(c_WaitableWaitHandle::classof()));
    auto child_wh = static_cast<c_WaitableWaitHandle*>(current->m_data.pobj);
    child_wh->enterContext(ctx_idx);
  }

  // import ourselves
  setContextIdx(ctx_idx);

  // try to import other children
  try {
    for (int64_t iter_pos = m_iterPos + 1;
         iter_pos < m_deps->size();
         ++iter_pos) {

      Cell* current = tvAssertCell(m_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->isFinished()) {
        continue;
      }

      assert(child->instanceof(c_WaitableWaitHandle::classof()));
      auto child_wh = static_cast<c_WaitableWaitHandle*>(child);
      child_wh->enterContext(ctx_idx);
    }
  } catch (const Object& cycle_exception) {
    // exception will be eventually processed by onUnblocked()
  }
}
void c_ExternalThreadEventWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  assert(getState() == STATE_WAITING);

  if (isInContext()) {
    getContext()->unregisterExternalThreadEvent(m_index);
  }

  setContextIdx(ctx_idx);
  m_index = getContext()->registerExternalThreadEvent(this);
}
void c_SetResultToRefWaitHandle::enterContextImpl(context_idx_t ctx_idx) {
  assert(getState() == STATE_BLOCKED);

  m_child->enterContext(ctx_idx);
  setContextIdx(ctx_idx);
}
void c_AsyncFunctionWaitHandle::initialize(c_WaitableWaitHandle* child) {
  setState(STATE_BLOCKED);
  setContextIdx(child->getContextIdx());
  m_children[0].setChild(child);
  incRefCount(); // account for child->this back-reference
}