Future-AsyncAwait-Frozen

 view release on metacpan or  search on metacpan

lib/Future/AsyncAwait/Frozen.c  view on Meta::CPAN

    f = POPs;
    PUTBACK;
  }

  if(!sv_isobject(f))
    croak("Expected a blessed object reference to await");

  if(future_is_ready(f)) {
    assert(CvDEPTH(curcv) > 0);
    TRACEPRINT("  READY\n");
    if(state)
      state->curcop = NULL;
    /* This might throw */
    future_get_to_stack(f, GIMME_V);
    TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop));
    return PL_op->op_next;
  }

#ifdef DEBUG_SHOW_STACKS
  debug_showstack("Stack before suspend");
#endif

  if(!state) {
    /* Clone the CV and then attach suspendedstate magic to it */
    curcv = cv_dup_for_suspend(curcv);
    state = suspendedstate_new(curcv);

    TRACEPRINT("  SUSPEND cloned CV->%p\n", curcv);
    defer_mortal_curcv = TRUE;
  }
  else {
    TRACEPRINT("  SUSPEND reuse CV\n");
  }

  state->curcop = PL_curcop;

  suspendedstate_suspend(state, origcv);
  {
    SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait::Frozen/suspendhook", FALSE);
    if(hookp && SvOK(*hookp) && SvUV(*hookp)) {
      SuspendHookFunc *hook = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
        if(!state->modhookdata)
          state->modhookdata = newHV();

      (*hook)(aTHX_ FAA_PHASE_POSTSUSPEND, curcv, state->modhookdata);
    }
  }

  CvSTART(curcv) = PL_op; /* resume from here */
  future_on_ready(f, curcv);

  /* If the Future implementation's ->AWAIT_ON_READY failed to capture this CV
   * then we'll segfault later after SvREFCNT_dec() on it. We can at least
   * detect that here
   */
  if(SvREFCNT(curcv) < 2) {
    croak("AWAIT_ON_READY failed to capture the CV");
  }

  state->awaiting_future = newSVsv(f);
  sv_rvweaken(state->awaiting_future);

  if(!state->returning_future)
    state->returning_future = future_new_from_proto(f);

  if(defer_mortal_curcv)
    SvREFCNT_dec((SV *)curcv);

  PUSHMARK(SP);
  mPUSHs(newSVsv(state->returning_future));
  PUTBACK;

  if(!SvWEAKREF(state->returning_future))
    sv_rvweaken(state->returning_future);
  if(!SvROK(state->returning_future))
    panic("ARGH we lost state->returning_future for curcv=%p\n", curcv);

/* For unknown reasons, doing this on perls 5.20 or 5.22 massively breaks
 * everything.
 *   https://rt.cpan.org/Ticket/Display.html?id=129202#txn-1843918
 */
#if HAVE_PERL_VERSION(5, 24, 0)
  future_chain_on_cancel(state->returning_future, state->awaiting_future);
#endif

  if(!SvROK(state->returning_future))
    panic("ARGH we lost state->returning_future for curcv=%p\n", curcv);
  if(!SvROK(state->awaiting_future))
    panic("ARGH we lost state->awaiting_future for curcv=%p\n", curcv);

  TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop));

  return PL_ppaddr[OP_RETURN](aTHX);
}

static OP *newAWAITOP(I32 flags, OP *expr)
{
  OP *op = newUNOP(OP_CUSTOM, flags, expr);
  op->op_ppaddr = &pp_await;

  return op;
}

enum {
  NO_FORBID,
  FORBID_FOREACH_NONLEXICAL,
  FORBID_MAP,
  FORBID_GREP,
};

static void check_optree(pTHX_ OP *op, int forbid, COP **last_cop);
static void check_optree(pTHX_ OP *op, int forbid, COP **last_cop)
{
  OP *op_first;
  OP *kid = NULL;

  if(OP_CLASS(op) == OA_COP)
    *last_cop = (COP *)op;

  switch(op->op_type) {
    case OP_LEAVELOOP:
      if((op_first = cUNOPx(op)->op_first)->op_type != OP_ENTERITER)
        break;

      /* This is a foreach loop of some kind. If it's not using a lexical
       * iterator variable, disallow await inside the body.
       * Check the first child, then apply forbid to the remainder of the body
       */
      check_optree(aTHX_ op_first, forbid, last_cop);
      kid = OpSIBLING(op_first);

      if(!op_first->op_targ)
        forbid = FORBID_FOREACH_NONLEXICAL;
      break;



( run in 0.994 second using v1.01-cache-2.11-cpan-39bf76dae61 )