// Wait for an event (either XEvent, or an internal Message)
    bool sleepUntilEvent (const int timeoutMs)
    {
        if (! isEmpty())
            return true;

        if (display != 0)
        {
            ScopedXLock xlock;
            if (XPending (display))
                return true;
        }

        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = timeoutMs * 1000;
        int fd0 = getWaitHandle();
        int fdmax = fd0;

        fd_set readset;
        FD_ZERO (&readset);
        FD_SET (fd0, &readset);

        if (display != 0)
        {
            ScopedXLock xlock;
            int fd1 = XConnectionNumber (display);
            FD_SET (fd1, &readset);
            fdmax = jmax (fd0, fd1);
        }

        const int ret = select (fdmax + 1, &readset, 0, 0, &tv);
        return (ret > 0); // ret <= 0 if error or timeout
    }
Example #2
0
Object File::await(uint16_t events, double timeout) {
  if (isClosed()) {
    Cell closedResult;
    closedResult.m_type = KindOfInt64;
    closedResult.m_data.num = FileAwait::CLOSED;
    return c_StaticWaitHandle::CreateSucceeded(closedResult);
  }
  if (fd() < 0) {
    throw Object(SystemLib::AllocExceptionObject(
      "Unable to await on stream, invalid file descriptor"));
  }
  events = events & FileEventHandler::READ_WRITE;
  if (!events) {
    throw Object(SystemLib::AllocExceptionObject(
      "Must await for reading, writing, or both."));
  }

  auto ev = new FileAwait(fd(), events, timeout);
  try {
    return ev->getWaitHandle();
  } catch (...) {
    assert(false);
    ev->abandon();
    throw;
  }
}
Example #3
0
c_WaitableWaitHandle*
AsioBlockableChain::firstInContext(context_idx_t ctx_idx) {
  for (auto cur = m_firstParent; cur; cur = cur->getNextParent()) {
    auto const wh = cur->getWaitHandle();
    if (!wh->isFinished() && wh->getContextIdx() == ctx_idx) {
      return wh;
    }
  }
  return nullptr;
}
Example #4
0
Array AsioBlockableChain::toArray() {
  Array result = Array::Create();
  for (auto cur = m_firstParent; cur; cur = cur->getNextParent()) {
    auto const wh = cur->getWaitHandle();
    if (!wh->isFinished()) {
      result.append(wh);
    }
  }
  return result;
}
Example #5
0
Object MCRouter::issue(mcr::mcrouter_msg_t& msg) {
  auto ev = new MCRouterResult(this, msg);
  try {
    return Object{ev->getWaitHandle()};
  } catch (...) {
    assert(false);
    ev->abandon();
    throw;
  }
}
Example #6
0
c_ResumableWaitHandle* c_ResumableWaitHandle::getRunning(ActRec* fp) {
  for (; fp; fp = g_context->getPrevVMState(fp)) {
    if (fp->resumed() && fp->func()->isAsync()) {
      if (fp->func()->isGenerator()) {
        // async generator
        auto generator = frame_async_generator(fp);
        if (!generator->isEagerlyExecuted()) {
          return generator->getWaitHandle();
        }
      } else {
        // async function
        return frame_afwh(fp);
      }
    }
  }

  return nullptr;
}
Example #7
0
/**
 * Discard the current frame, assuming that a PHP exception given in
 * phpException argument, or C++ exception (phpException == nullptr)
 * is being thrown. Returns an exception to propagate, or nulltpr
 * if the VM execution should be resumed.
 */
ObjectData* tearDownFrame(ActRec*& fp, Stack& stack, PC& pc,
                          ObjectData* phpException) {
  auto const func = fp->func();
  auto const curOp = peek_op(pc);
  auto const prevFp = fp->sfp();
  auto const soff = fp->m_soff;

  ITRACE(1, "tearDownFrame: {} ({})\n",
         func->fullName()->data(),
         func->unit()->filepath()->data());
  ITRACE(1, "  fp {} prevFp {}\n",
         implicit_cast<void*>(fp),
         implicit_cast<void*>(prevFp));

  // When throwing from a constructor, we normally want to avoid running the
  // destructor on an object that hasn't been fully constructed yet. But if
  // we're unwinding through the constructor's RetC, the constructor has
  // logically finished and we're unwinding for some internal reason (timeout
  // or user profiler, most likely). More importantly, fp->m_this may have
  // already been destructed and/or overwritten due to sharing space with
  // fp->m_r.
  if (curOp != OpRetC &&
      fp->hasThis() &&
      fp->getThis()->getVMClass()->getCtor() == func &&
      fp->getThis()->getVMClass()->getDtor()) {
    /*
     * Looks like an FPushCtor call, but it could still have been called
     * directly. Check the fpi region to be sure.
     */
    Offset prevPc;
    auto outer = g_context->getPrevVMState(fp, &prevPc);
    if (outer) {
      auto fe = outer->func()->findPrecedingFPI(prevPc);
      if (fe && isFPushCtor(outer->func()->unit()->getOp(fe->m_fpushOff))) {
        fp->getThis()->setNoDestruct();
      }
    }
  }

  auto const decRefLocals = [&] {
    /*
     * It is possible that locals have already been decref'd.
     *
     * Here's why:
     *
     *   - If a destructor for any of these things throws a php
     *     exception, it's swallowed at the dtor boundary and we keep
     *     running php.
     *
     *   - If the destructor for any of these things throws a fatal,
     *     it's swallowed, and we set surprise flags to throw a fatal
     *     from now on.
     *
     *   - If the second case happened and we have to run another
     *     destructor, its enter hook will throw, but it will be
     *     swallowed again.
     *
     *   - Finally, the exit hook for the returning function can
     *     throw, but this happens last so everything is destructed.
     *
     *   - When that happens, exit hook sets localsDecRefd flag.
     */
    if (!fp->localsDecRefd()) {
      try {
        // Note that we must convert locals and the $this to
        // uninit/zero during unwind.  This is because a backtrace
        // from another destructing object during this unwind may try
        // to read them.
        frame_free_locals_unwind(fp, func->numLocals(), phpException);
      } catch (...) {}
    }
  };

  if (LIKELY(!fp->resumed())) {
    decRefLocals();
    if (UNLIKELY(func->isAsyncFunction()) &&
        phpException &&
        !fp->isFCallAwait()) {
      // If in an eagerly executed async function, wrap the user exception
      // into a failed StaticWaitHandle and return it to the caller.
      auto const waitHandle = c_StaticWaitHandle::CreateFailed(phpException);
      phpException = nullptr;
      stack.ndiscard(func->numSlotsInFrame());
      stack.ret();
      assert(stack.topTV() == &fp->m_r);
      cellCopy(make_tv<KindOfObject>(waitHandle), fp->m_r);
    } else {
      // Free ActRec.
      stack.ndiscard(func->numSlotsInFrame());
      stack.discardAR();
    }
  } else if (func->isAsyncFunction()) {
    auto const waitHandle = frame_afwh(fp);
    if (phpException) {
      // Handle exception thrown by async function.
      decRefLocals();
      waitHandle->fail(phpException);
      phpException = nullptr;
    } else if (waitHandle->isRunning()) {
      // Let the C++ exception propagate. If the current frame represents async
      // function that is running, mark it as abruptly interrupted. Some opcodes
      // like Await may change state of the async function just before exit hook
      // decides to throw C++ exception.
      decRefLocals();
      waitHandle->failCpp();
    }
  } else if (func->isAsyncGenerator()) {
    auto const gen = frame_async_generator(fp);
    if (phpException) {
      // Handle exception thrown by async generator.
      decRefLocals();
      auto eagerResult = gen->fail(phpException);
      phpException = nullptr;
      if (eagerResult) {
        stack.pushObjectNoRc(eagerResult);
      }
    } else if (gen->isEagerlyExecuted() || gen->getWaitHandle()->isRunning()) {
      // Fail the async generator and let the C++ exception propagate.
      decRefLocals();
      gen->failCpp();
    }
  } else if (func->isNonAsyncGenerator()) {
    // Mark the generator as finished.
    decRefLocals();
    frame_generator(fp)->fail();
  } else {
    not_reached();
  }

  /*
   * At the final ActRec in this nesting level.
   */
  if (UNLIKELY(!prevFp)) {
    pc = nullptr;
    fp = nullptr;
    return phpException;
  }

  assert(stack.isValidAddress(reinterpret_cast<uintptr_t>(prevFp)) ||
         prevFp->resumed());
  auto const prevOff = soff + prevFp->func()->base();
  pc = prevFp->func()->unit()->at(prevOff);
  fp = prevFp;
  return phpException;
}