EV

 view release on metacpan or  search on metacpan

libev/ev_iouring.c  view on Meta::CPAN

ecb_cold
static void
iouring_fork (EV_P)
{
  iouring_internal_destroy (EV_A);

  while (iouring_internal_init (EV_A) < 0)
    ev_syserr ("(libev) io_uring_setup");

  fd_rearm_all (EV_A);

  ev_io_stop  (EV_A_ &iouring_tfd_w);
  ev_io_set   (EV_A_ &iouring_tfd_w, iouring_tfd, EV_READ);
  ev_io_start (EV_A_ &iouring_tfd_w);
}

/*****************************************************************************/

static void
iouring_modify (EV_P_ int fd, int oev, int nev)
{
  if (oev)
    {
      /* we assume the sqe's are all "properly" initialised */
      struct io_uring_sqe *sqe = iouring_sqe_get (EV_A);
      sqe->opcode    = IORING_OP_POLL_REMOVE;
      sqe->fd        = fd;
      /* Jens Axboe notified me that user_data is not what is documented, but is
       * some kind of unique ID that has to match, otherwise the request cannot
       * be removed. Since we don't *really* have that, we pass in the old
       * generation counter - if that fails, too bad, it will hopefully be removed
       * at close time and then be ignored. */
      sqe->addr      = (uint32_t)fd | ((__u64)(uint32_t)anfds [fd].egen << 32);
      sqe->user_data = (uint64_t)-1;
      iouring_sqe_submit (EV_A_ sqe);

      /* increment generation counter to avoid handling old events */
      ++anfds [fd].egen;
    }

  if (nev)
    {
      struct io_uring_sqe *sqe = iouring_sqe_get (EV_A);
      sqe->opcode      = IORING_OP_POLL_ADD;
      sqe->fd          = fd;
      sqe->addr        = 0;
      sqe->user_data   = (uint32_t)fd | ((__u64)(uint32_t)anfds [fd].egen << 32);
      sqe->poll_events =
        (nev & EV_READ ? POLLIN : 0)
        | (nev & EV_WRITE ? POLLOUT : 0);
      iouring_sqe_submit (EV_A_ sqe);
    }
}

inline_size
void
iouring_tfd_update (EV_P_ ev_tstamp timeout)
{
  ev_tstamp tfd_to = mn_now + timeout;

  /* we assume there will be many iterations per timer change, so
   * we only re-set the timerfd when we have to because its expiry
   * is too late.
   */
  if (ecb_expect_false (tfd_to < iouring_tfd_to))
    {
       struct itimerspec its;

       iouring_tfd_to = tfd_to;
       EV_TS_SET (its.it_interval, 0.);
       EV_TS_SET (its.it_value, tfd_to);

       if (timerfd_settime (iouring_tfd, TFD_TIMER_ABSTIME, &its, 0) < 0)
         assert (("libev: iouring timerfd_settime failed", 0));
    }
}

inline_size
void
iouring_process_cqe (EV_P_ struct io_uring_cqe *cqe)
{
  int      fd  = cqe->user_data & 0xffffffffU;
  uint32_t gen = cqe->user_data >> 32;
  int      res = cqe->res;

  /* user_data -1 is a remove that we are not atm. interested in */
  if (cqe->user_data == (uint64_t)-1)
    return;

  assert (("libev: io_uring fd must be in-bounds", fd >= 0 && fd < anfdmax));

  /* documentation lies, of course. the result value is NOT like
   * normal syscalls, but like linux raw syscalls, i.e. negative
   * error numbers. fortunate, as otherwise there would be no way
   * to get error codes at all. still, why not document this?
   */

  /* ignore event if generation doesn't match */
  /* other than skipping removal events, */
  /* this should actually be very rare */
  if (ecb_expect_false (gen != (uint32_t)anfds [fd].egen))
    return;

  if (ecb_expect_false (res < 0))
    {
      /*TODO: EINVAL handling (was something failed with this fd)*/

      if (res == -EBADF)
        {
          assert (("libev: event loop rejected bad fd", res != -EBADF));
          fd_kill (EV_A_ fd);
        }
      else
        {
          errno = -res;
          ev_syserr ("(libev) IORING_OP_POLL_ADD");
        }

      return;
    }



( run in 0.454 second using v1.01-cache-2.11-cpan-71847e10f99 )