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 )