The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
static pe_ring Prepare, Check, AsyncCheck, Callback;

static void boot_hook()
{
  PE_RING_INIT(&Prepare, 0);
  PE_RING_INIT(&Check, 0);
  PE_RING_INIT(&AsyncCheck, 0);
  PE_RING_INIT(&Callback, 0);
}

static pe_qcallback *
pe_add_hook(char *which, int is_perl, void *cb, void *ext_data)
{
  pe_qcallback *qcb;
  EvNew(2, qcb, 1, pe_qcallback);
  PE_RING_INIT(&qcb->ring, qcb);
  qcb->is_perl = is_perl;
  if (is_perl) {
    qcb->callback = SvREFCNT_inc((SV*)cb);
    qcb->ext_data = 0;
  }
  else {
    qcb->callback = cb;
    qcb->ext_data = ext_data;
  }
  if (strEQ(which, "prepare"))
    PE_RING_UNSHIFT(&qcb->ring, &Prepare);
  else if (strEQ(which, "check"))
    PE_RING_UNSHIFT(&qcb->ring, &Check);
  else if (strEQ(which, "asynccheck"))
    PE_RING_UNSHIFT(&qcb->ring, &AsyncCheck);
  else if (strEQ(which, "callback"))
    PE_RING_UNSHIFT(&qcb->ring, &Callback);
  else
    croak("Unknown hook '%s' in pe_add_hook", which);
  return qcb;
}

static pe_qcallback *capi_add_hook(char *which, void *cb, void *ext_data)
{ return pe_add_hook(which, 0, cb, ext_data); }

static void pe_cancel_hook(pe_qcallback *qcb)
{
  if (qcb->is_perl)
    SvREFCNT_dec((SV*)qcb->callback);
  PE_RING_DETACH(&qcb->ring);
  EvFree(2, qcb);
}

static void pe_map_check(pe_ring *List)
{
  pe_qcallback *qcb = (pe_qcallback*) List->prev->self;
  while (qcb) {
    if (qcb->is_perl) {
      dSP;
      PUSHMARK(SP);
      PUTBACK;
      perl_call_sv((SV*)qcb->callback, G_DISCARD);
    }
    else { /* !is_perl */
      (* (void(*)(void*)) qcb->callback)(qcb->ext_data);
    }
    qcb = (pe_qcallback*) qcb->ring.prev->self;
  }
}