Object c_GenArrayWaitHandle::ti_create(const char* cls, CArrRef dependencies) {
    Array deps = dependencies->copy();
    for (ssize_t iter_pos = deps->iter_begin();
            iter_pos != ArrayData::invalid_index;
            iter_pos = deps->iter_advance(iter_pos)) {

        TypedValue* current = deps->nvGetValueRef(iter_pos);
        if (UNLIKELY(current->m_type == KindOfRef)) {
            tvUnbox(current);
        }

        if (!c_WaitHandle::fromTypedValue(current) &&
                !IS_NULL_TYPE(current->m_type)) {
            Object e(SystemLib::AllocInvalidArgumentExceptionObject(
                         "Expected dependencies to be an array of WaitHandle instances"));
            throw e;
        }
    }

    Object exception;
    for (ssize_t iter_pos = deps->iter_begin();
            iter_pos != ArrayData::invalid_index;
            iter_pos = deps->iter_advance(iter_pos)) {

        TypedValue* current = deps->nvGetValueRef(iter_pos);
        if (IS_NULL_TYPE(current->m_type)) {
            // {uninit,null} yields null
            tvWriteNull(current);
            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->isSucceeded()) {
            tvSetIgnoreRef(child->getResult(), current);
        } else if (child->isFailed()) {
            putException(exception, child->getException());
        } else {
            assert(dynamic_cast<c_WaitableWaitHandle*>(child));
            auto child_wh = static_cast<c_WaitableWaitHandle*>(child);

            c_GenArrayWaitHandle* my_wh = NEWOBJ(c_GenArrayWaitHandle)();
            my_wh->initialize(exception, deps, iter_pos, child_wh);
            return my_wh;
        }
    }

    if (exception.isNull()) {
        TypedValue tv;
        tv.m_type = KindOfArray;
        tv.m_data.parr = deps.get();
        return c_StaticResultWaitHandle::Create(&tv);
    } else {
        return c_StaticExceptionWaitHandle::Create(exception.get());
    }
}
void c_SetResultToRefWaitHandle::markAsFailed(CObjRef exception) {
  RefData* ref = m_ref;

  m_ref = nullptr;
  tvSetIgnoreRef(make_tv<KindOfNull>(), *ref->tv());
  decRefRef(ref);

  setException(exception.get());
  m_child = nullptr;
}
void c_SetResultToRefWaitHandle::markAsSucceeded(const TypedValue* result) {
  RefData* ref = m_ref;

  m_ref = nullptr;
  tvSetIgnoreRef(result, ref->tv());
  decRefRef(ref);

  setResult(result);
  m_child = nullptr;
}
void c_GenArrayWaitHandle::onUnblocked() {
  for (;
       m_iterPos != ArrayData::invalid_index;
       m_iterPos = m_deps->iter_advance(m_iterPos)) {

    TypedValue* current = m_deps->nvGetValueRef(m_iterPos);
    if (IS_NULL_TYPE(current->m_type)) {
      // {uninit,null} yields null
      tvWriteNull(current);
      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->isSucceeded()) {
      tvSetIgnoreRef(child->getResult(), current);
    } else if (child->isFailed()) {
      putException(m_exception, child->getException());
    } else {
      assert(dynamic_cast<c_WaitableWaitHandle*>(child));
      auto child_wh = static_cast<c_WaitableWaitHandle*>(child);

      try {
        blockOn(child_wh);
        return;
      } catch (const Object& cycle_exception) {
        putException(m_exception, cycle_exception.get());
      }
    }
  }

  if (m_exception.isNull()) {
    TypedValue result;
    result.m_type = KindOfArray;
    result.m_data.parr = m_deps.get();
    setResult(&result);
    m_deps = nullptr;
  } else {
    setException(m_exception.get());
    m_exception = nullptr;
    m_deps = nullptr;
  }
}