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));
  }
}
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()
    );
  }
}