Coro

 view release on metacpan or  search on metacpan

Coro/State.xs  view on Meta::CPAN


  --cctx_count;
  coro_destroy (&cctx->cctx);

  coro_stack_free (&cctx->stack);

  Safefree (cctx);
}

/* wether this cctx should be destructed */
#define CCTX_EXPIRED(cctx) ((cctx)->gen != cctx_gen || ((cctx)->flags & CC_NOREUSE))

static coro_cctx *
cctx_get (pTHX)
{
  while (ecb_expect_true (cctx_first))
    {
      coro_cctx *cctx = cctx_first;
      cctx_first = cctx->next;
      --cctx_idle;

      if (ecb_expect_true (!CCTX_EXPIRED (cctx)))
        return cctx;

      cctx_destroy (cctx);
    }

  return cctx_new_run ();
}

static void
cctx_put (coro_cctx *cctx)
{
  assert (("FATAL: cctx_put called on non-initialised cctx in Coro (please report)", cctx->stack.sptr));

  /* free another cctx if overlimit */
  if (ecb_expect_false (cctx_idle >= cctx_max_idle))
    {
      coro_cctx *first = cctx_first;
      cctx_first = first->next;
      --cctx_idle;

      cctx_destroy (first);
    }

  ++cctx_idle;
  cctx->next = cctx_first;
  cctx_first = cctx;
}

/** coroutine switching *****************************************************/

static void
transfer_check (pTHX_ struct coro *prev, struct coro *next)
{
  /* TODO: throwing up here is considered harmful */

  if (ecb_expect_true (prev != next))
    {
      if (ecb_expect_false (!(prev->flags & (CF_RUNNING | CF_NEW))))
        croak ("Coro::State::transfer called with a blocked prev Coro::State, but can only transfer from running or new states,");

      if (ecb_expect_false (next->flags & (CF_RUNNING | CF_ZOMBIE | CF_SUSPENDED)))
        croak ("Coro::State::transfer called with running, destroyed or suspended next Coro::State, but can only transfer to inactive states,");

#if !PERL_VERSION_ATLEAST (5,10,0)
      if (ecb_expect_false (PL_lex_state != LEX_NOTPARSING))
        croak ("Coro::State::transfer called while parsing, but this is not supported in your perl version,");
#endif
    }
}

/* always use the TRANSFER macro */
static void ecb_noinline /* noinline so we have a fixed stackframe */
transfer (pTHX_ struct coro *prev, struct coro *next, int force_cctx)
{
  dSTACKLEVEL;

  /* sometimes transfer is only called to set idle_sp */
  if (ecb_expect_false (!prev))
    {
      cctx_current->idle_sp = STACKLEVEL;
      assert (cctx_current->idle_te = PL_top_env); /* just for the side-effect when asserts are enabled */
    }
  else if (ecb_expect_true (prev != next))
    {
      coro_cctx *cctx_prev;

      if (ecb_expect_false (prev->flags & CF_NEW))
        {
          /* create a new empty/source context */
          prev->flags &= ~CF_NEW;
          prev->flags |=  CF_RUNNING;
        }

      prev->flags &= ~CF_RUNNING;
      next->flags |=  CF_RUNNING;

      /* first get rid of the old state */
      save_perl (aTHX_ prev);

      if (ecb_expect_false (next->flags & CF_NEW))
        {
          /* need to start coroutine */
          next->flags &= ~CF_NEW;
          /* setup coroutine call */
          init_perl (aTHX_ next);
        }
      else
        load_perl (aTHX_ next);

      /* possibly untie and reuse the cctx */
      if (ecb_expect_true (
            cctx_current->idle_sp == STACKLEVEL
            && !(cctx_current->flags & CC_TRACE)
            && !force_cctx
         ))
        {
          /* I assume that stacklevel is a stronger indicator than PL_top_env changes */
          assert (("FATAL: current top_env must equal previous top_env in Coro (please report)", PL_top_env == cctx_current->idle_te));

Coro/State.xs  view on Meta::CPAN

api_is_ready (pTHX_ SV *coro_sv)
{
  return !!(SvSTATE (coro_sv)->flags & CF_READY);
}

/* expects to own a reference to next->hv */
ecb_inline void
prepare_schedule_to (pTHX_ struct coro_transfer_args *ta, struct coro *next)
{
  SV *prev_sv = SvRV (coro_current);

  ta->prev = SvSTATE_hv (prev_sv);
  ta->next = next;

  TRANSFER_CHECK (*ta);

  SvRV_set (coro_current, (SV *)next->hv);

  free_coro_mortal (aTHX);
  coro_mortal = prev_sv;
}

static void
prepare_schedule (pTHX_ struct coro_transfer_args *ta)
{
  for (;;)
    {
      struct coro *next = coro_deq (aTHX);

      if (ecb_expect_true (next))
        {
          /* cannot transfer to destroyed coros, skip and look for next */
          if (ecb_expect_false (next->flags & (CF_ZOMBIE | CF_SUSPENDED)))
            SvREFCNT_dec (next->hv); /* coro_nready has already been taken care of by destroy */
          else
            {
              next->flags &= ~CF_READY;
              --coro_nready;

              prepare_schedule_to (aTHX_ ta, next);
              break;
            }
        }
      else
        {
          /* nothing to schedule: call the idle handler */
          if (SvROK (sv_idle)
              && SvOBJECT (SvRV (sv_idle)))
            {
              if (SvRV (sv_idle) == SvRV (coro_current))
                {
                  require_pv ("Carp");

                  {
                    dSP;

                    ENTER;
                    SAVETMPS;

                    PUSHMARK (SP);
                    XPUSHs (sv_2mortal (newSVpv ("FATAL: $Coro::idle blocked itself - did you try to block inside an event loop callback? Caught", 0)));
                    PUTBACK;
                    call_pv ("Carp::confess", G_VOID | G_DISCARD);

                    FREETMPS;
                    LEAVE;
                  }
                }

              ++coro_nready; /* hack so that api_ready doesn't invoke ready hook */
              api_ready (aTHX_ SvRV (sv_idle));
              --coro_nready;
            }
          else
            {
              /* TODO: deprecated, remove, cannot work reliably *//*D*/
              dSP;

              ENTER;
              SAVETMPS;

              PUSHMARK (SP);
              PUTBACK;
              call_sv (sv_idle, G_VOID | G_DISCARD);

              FREETMPS;
              LEAVE;
            }
        }
    }
}

ecb_inline void
prepare_cede (pTHX_ struct coro_transfer_args *ta)
{
  api_ready (aTHX_ coro_current);
  prepare_schedule (aTHX_ ta);
}

ecb_inline void
prepare_cede_notself (pTHX_ struct coro_transfer_args *ta)
{
  SV *prev = SvRV (coro_current);

  if (coro_nready)
    {
      prepare_schedule (aTHX_ ta);
      api_ready (aTHX_ prev);
    }
  else
    prepare_nop (aTHX_ ta);
}

static void
api_schedule (pTHX)
{
  struct coro_transfer_args ta;

  prepare_schedule (aTHX_ &ta);
  TRANSFER (ta, 1);
}



( run in 1.003 second using v1.01-cache-2.11-cpan-364913b4093 )