Beispiel #1
0
c_Generator *c_Generator::Clone(ObjectData* obj) {
  auto thiz = static_cast<c_Generator*>(obj);
  auto fp = thiz->actRec();

  c_Generator* cont = Create(fp, thiz->resumable()->resumeAddr(),
                                    thiz->resumable()->resumeOffset());
  cont->copyVars(fp);
  cont->setState(thiz->getState());
  cont->m_index  = thiz->m_index;
  cellSet(thiz->m_key, cont->m_key);
  cellSet(thiz->m_value, cont->m_value);

  return cont;
}
void c_AsyncFunctionWaitHandle::resume() {
  auto const child = m_children[0].getChild();
  assert(getState() == STATE_READY);
  assert(child->isFinished());
  setState(STATE_RUNNING);

  if (LIKELY(child->isSucceeded())) {
    // child succeeded, pass the result to the async function
    g_context->resumeAsyncFunc(resumable(), child, child->getResult());
  } else {
    // child failed, raise the exception inside the async function
    g_context->resumeAsyncFuncThrow(resumable(), child,
                                    child->getException());
  }
}
void delete_AsyncFunctionWaitHandle(ObjectData* od, const Class*) {
  auto const waitHandle = static_cast<c_AsyncFunctionWaitHandle*>(od);
  auto const size = waitHandle->resumable()->size();
  auto const base = (char*)(waitHandle + 1) - size;
  waitHandle->~c_AsyncFunctionWaitHandle();
  MM().objFreeLogged(base, size);
}
Beispiel #4
0
void delete_AsyncGenerator(ObjectData* od, const Class*) {
  auto const gen = static_cast<c_AsyncGenerator*>(od);
  auto const size = gen->resumable()->size();
  auto const base = (char*)(gen + 1) - size;
  gen->~c_AsyncGenerator();
  MM().objFreeLogged(base, size);
}
Beispiel #5
0
void delete_Generator(ObjectData* od, const Class*) {
  auto const cont = static_cast<c_Generator*>(od);
  auto const size = cont->resumable()->size();
  auto const base = (char*)(cont + 1) - size;
  cont->~c_Generator();
  MM().objFreeLogged(base, size);
}
Beispiel #6
0
static ActRec* getPrevActRec(const ActRec* fp, Offset* prevPc) {
  if (fp && fp->func() && fp->resumed() && fp->func()->isAsyncFunction()) {
    c_WaitableWaitHandle* currentWaitHandle = frame_afwh(fp);
    if (currentWaitHandle->isFinished()) {
      /*
       * It's possible in very rare cases (it will return a truncated stack):
       * 1) async function which WaitHandle is not referenced by anything
       *      else finishes
       * 2) its return value is an object with destructor
       * 3) this destructor gets called as part of destruction of the
       *      WaitHandleobject, which happens right before FP is adjusted
      */
      return nullptr;
    }

    auto const contextIdx = currentWaitHandle->getContextIdx();
    while (currentWaitHandle != nullptr) {
      auto p = currentWaitHandle->getParentChain().firstInContext(contextIdx);
      if (p == nullptr) break;

      if (p->getKind() == c_WaitHandle::Kind::AsyncFunction) {
        auto wh = p->asAsyncFunction();
        *prevPc = wh->resumable()->resumeOffset();
        return wh->actRec();
      }
      currentWaitHandle = p;
    }
    *prevPc = 0;
    return AsioSession::Get()->getContext(contextIdx)->getSavedFP();
  }
  return g_context->getPrevVMState(fp, prevPc);
}
// Get the line number on which execution will proceed when execution resumes.
int c_AsyncFunctionWaitHandle::getLineNumber() {
  if (isFinished()) {
    return -1;
  }

  always_assert(!isRunning());
  return actRec()->func()->unit()->getLineNumber(resumable()->resumeOffset());
}
Beispiel #8
0
void c_Generator::suspend(JIT::TCA resumeAddr, Offset resumeOffset,
                             const Cell& value) {
  assert(getState() == Running);
  resumable()->setResumeAddr(resumeAddr, resumeOffset);
  cellSet(make_tv<KindOfInt64>(++m_index), m_key);
  cellSet(value, m_value);
  setState(Started);
}
// Get the next execution offset
Offset c_AsyncFunctionWaitHandle::getNextExecutionOffset() {
  if (isFinished()) {
    return InvalidAbsoluteOffset;
  }

  always_assert(!isRunning());
  return resumable()->resumeOffset();
}
void c_AsyncFunctionWaitHandle::resume() {
  // may happen if scheduled in multiple contexts
  if (getState() != STATE_SCHEDULED) {
    decRefObj(this);
    return;
  }

  assert(getState() == STATE_SCHEDULED);
  assert(m_child->isFinished());
  setState(STATE_RUNNING);

  if (LIKELY(m_child->isSucceeded())) {
    // child succeeded, pass the result to the async function
    g_context->resumeAsyncFunc(resumable(), m_child, m_child->getResult());
  } else {
    // child failed, raise the exception inside the async function
    g_context->resumeAsyncFuncThrow(resumable(), m_child,
                                    m_child->getException());
  }
}
void c_AsyncFunctionWaitHandle::await(Offset resumeOffset,
                                      c_WaitableWaitHandle* child) {
  // Prepare child for establishing dependency. May throw.
  prepareChild(child);

  // Suspend the async function.
  resumable()->setResumeAddr(nullptr, resumeOffset);

  // Set up the dependency.
  setState(STATE_BLOCKED);
  m_children[0].setChild(child);
}
Beispiel #12
0
void c_Generator::suspend(JIT::TCA resumeAddr, Offset resumeOffset,
                             const Cell& key, const Cell& value) {
  assert(getState() == Running);
  resumable()->setResumeAddr(resumeAddr, resumeOffset);
  cellSet(key, m_key);
  cellSet(value, m_value);
  if (m_key.m_type == KindOfInt64) {
    int64_t new_index = m_key.m_data.num;
    m_index = new_index > m_index ? new_index : m_index;
  }
  setState(Started);
}
Beispiel #13
0
c_AsyncGeneratorWaitHandle*
AsyncGenerator::await(Offset resumeOffset, c_WaitableWaitHandle* child) {
  assert(isRunning());
  resumable()->setResumeAddr(nullptr, resumeOffset);

  if (m_waitHandle) {
    // Resumed execution.
    m_waitHandle->await(child);
    return nullptr;
  }
  // Eager executon.
  m_waitHandle = c_AsyncGeneratorWaitHandle::Create(this, child);
  return m_waitHandle.get();
}
Beispiel #14
0
Generator& Generator::operator=(const Generator& other) {
  auto const fp = other.actRec();
  const size_t numSlots = fp->func()->numSlotsInFrame();
  const size_t frameSz = Resumable::getFrameSize(numSlots);
  const size_t genSz = genSize(sizeof(Generator), frameSz);
  resumable()->initialize<true>(fp,
                                other.resumable()->resumeAddr(),
                                other.resumable()->resumeOffset(),
                                frameSz,
                                genSz);
  copyVars(fp);
  setState(other.getState());
  m_index = other.m_index;
  cellSet(other.m_key, m_key);
  cellSet(other.m_value, m_value);
  return *this;
}
Beispiel #15
0
ObjectData*
AsyncGenerator::Create(const ActRec* fp, size_t numSlots,
                       jit::TCA resumeAddr, Offset resumeOffset) {
  assert(fp);
  assert(!fp->resumed());
  assert(fp->func()->isAsyncGenerator());
  const size_t frameSz = Resumable::getFrameSize(numSlots);
  const size_t genSz = genSize(sizeof(AsyncGenerator), frameSz);
  auto const obj = BaseGenerator::Alloc<AsyncGenerator>(s_class, genSz);
  auto const genData = new (Native::data<AsyncGenerator>(obj)) AsyncGenerator();
  genData->resumable()->initialize<false>(fp,
                                          resumeAddr,
                                          resumeOffset,
                                          frameSz,
                                          genSz);
  genData->setState(State::Created);
  return obj;
}
Beispiel #16
0
void c_Generator::yield(Offset resumeOffset,
                        const Cell* key, const Cell& value) {
  assert(getState() == State::Running);
  resumable()->setResumeAddr(nullptr, resumeOffset);

  if (key) {
    cellSet(*key, m_key);
    tvRefcountedDecRefNZ(*key);
    if (m_key.m_type == KindOfInt64) {
      int64_t new_index = m_key.m_data.num;
      m_index = new_index > m_index ? new_index : m_index;
    }
  } else {
    cellSet(make_tv<KindOfInt64>(++m_index), m_key);
  }
  cellSet(value, m_value);
  tvRefcountedDecRefNZ(value);

  setState(State::Started);
}
Beispiel #17
0
static ActRec* getPrevActRec(
    const ActRec* fp, Offset* prevPc,
    folly::small_vector<c_WaitableWaitHandle*, 64>& visitedWHs) {
  if (fp && fp->func() && fp->resumed() && fp->func()->isAsyncFunction()) {
    c_WaitableWaitHandle* currentWaitHandle = frame_afwh(fp);
    if (currentWaitHandle->isFinished()) {
      /*
       * It's possible in very rare cases (it will return a truncated stack):
       * 1) async function which WaitHandle is not referenced by anything
       *      else finishes
       * 2) its return value is an object with destructor
       * 3) this destructor gets called as part of destruction of the
       *      WaitHandleobject, which happens right before FP is adjusted
      */
      return nullptr;
    }

    auto const contextIdx = currentWaitHandle->getContextIdx();
    while (currentWaitHandle != nullptr) {
      auto p = currentWaitHandle->getParentChain().firstInContext(contextIdx);
      if (p == nullptr ||
          UNLIKELY(std::find(visitedWHs.begin(), visitedWHs.end(), p)
          != visitedWHs.end())) {
        // If the parent exists in our backtrace, it means we have detected a
        // cycle. Fall back to savedFP in that case.
        break;
      }

      visitedWHs.push_back(p);
      if (p->getKind() == c_WaitHandle::Kind::AsyncFunction) {
        auto wh = p->asAsyncFunction();
        *prevPc = wh->resumable()->resumeOffset();
        return wh->actRec();
      }
      currentWaitHandle = p;
    }
    *prevPc = 0;
    return AsioSession::Get()->getContext(contextIdx)->getSavedFP();
  }
  return g_context->getPrevVMState(fp, prevPc);
}
Beispiel #18
0
c_StaticWaitHandle*
AsyncGenerator::yield(Offset resumeOffset,
                      const Cell* key, const Cell value) {
  assert(isRunning());
  resumable()->setResumeAddr(nullptr, resumeOffset);
  setState(State::Started);

  auto keyValueTuple = make_packed_array(
    key ? Variant(tvAsCVarRef(key), Variant::CellCopy()) : init_null_variant,
    Variant(tvAsCVarRef(&value), Variant::CellCopy()));
  auto keyValueTupleTV = make_tv<KindOfArray>(keyValueTuple.detach());

  if (m_waitHandle) {
    // Resumed execution.
    req::ptr<c_AsyncGeneratorWaitHandle> wh(std::move(m_waitHandle));
    wh->ret(*tvAssertCell(&keyValueTupleTV));
    return nullptr;
  }
  // Eager execution.
  return c_StaticWaitHandle::CreateSucceeded(keyValueTupleTV);
}
void delete_AsyncFunctionWaitHandle(ObjectData* od, const Class*) {
  auto wh = static_cast<c_AsyncFunctionWaitHandle*>(od);
  Resumable::Destroy(wh->resumable()->size(), wh);
}
void
c_AsyncFunctionWaitHandle::suspend(JIT::TCA resumeAddr, Offset resumeOffset,
                                   c_WaitableWaitHandle* child) {
  resumable()->setResumeAddr(resumeAddr, resumeOffset);
  m_child = child;
}
void c_AsyncFunctionWaitHandle::run() {
  // may happen if scheduled in multiple contexts
  if (getState() != STATE_SCHEDULED) {
    return;
  }

  try {
    setState(STATE_RUNNING);

    // resume async function
    if (LIKELY(m_child->isSucceeded())) {
      // child succeeded, pass the result to the async function
      g_context->resumeAsyncFunc(resumable(), m_child, m_child->getResult());
    } else if (m_child->isFailed()) {
      // child failed, raise the exception inside the async function
      g_context->resumeAsyncFuncThrow(resumable(), m_child,
                                      m_child->getException());
    } else {
      throw FatalErrorException(
          "Invariant violation: child neither succeeded nor failed");
    }

  retry:
    // async function reached RetC, which already set m_resultOrException
    if (isSucceeded()) {
      m_child = nullptr;
      markAsSucceeded();
      return;
    }

    // async function reached AsyncSuspend, which already set m_child
    assert(!m_child->isFinished());
    assert(m_child->instanceof(c_WaitableWaitHandle::classof()));

    // import child into the current context, detect cross-context cycles
    try {
      child()->enterContext(getContextIdx());
    } catch (Object& e) {
      g_context->resumeAsyncFuncThrow(resumable(), m_child, e.get());
      goto retry;
    }

    // detect cycles
    if (UNLIKELY(isDescendantOf(child()))) {
      Object e(createCycleException(child()));
      g_context->resumeAsyncFuncThrow(resumable(), m_child, e.get());
      goto retry;
    }

    // on await callback
    AsioSession* session = AsioSession::Get();
    if (UNLIKELY(session->hasOnAsyncFunctionAwaitCallback())) {
      session->onAsyncFunctionAwait(this, m_child);
    }

    // set up dependency
    setState(STATE_BLOCKED);
    blockOn(child());
  } catch (const Object& exception) {
    // process exception thrown by the async function
    m_child = nullptr;
    markAsFailed(exception);
  } catch (...) {
    // process C++ exception
    m_child = nullptr;
    markAsFailed(AsioSession::Get()->getAbruptInterruptException());
    throw;
  }
}