EV-Future
view release on metacpan or search on metacpan
SvREFCNT_inc(cb);
sv_2mortal(cb);
PUSHMARK(SP);
for (I32 i = 0; i < items; i++) {
XPUSHs(sv_mortalcopy(ST(i)));
}
PUTBACK;
race_cleanup(aTHX_ &ctx);
call_sv(cb, G_DISCARD | G_VOID);
FREETMPS;
LEAVE;
} else {
race_cleanup(aTHX_ &ctx);
}
XSRETURN_EMPTY;
}
static evf_handle *parallel_start(pTHX_ AV *list, SV *worker, SV *final_cb, int unsafe, int want_handle) {
I32 len = av_len(list) + 1;
if (len <= 0) {
if (IS_PVCV(final_cb)) {
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
call_sv(final_cb, G_DISCARD | G_VOID);
FREETMPS;
LEAVE;
}
/* Already over: hand back a dead handle so callers get a uniform
object rather than undef. */
return want_handle ? evf_handle_new(aTHX_ NULL, EVF_KIND_PARALLEL) : NULL;
}
ENTER;
parallel_ctx *ctx;
Newx(ctx, 1, parallel_ctx);
ctx->is_freed_ptr = NULL;
ctx->h = NULL;
ctx->tasks = (AV*)SvREFCNT_inc((SV*)list);
ctx->final_cb = SvREFCNT_inc(final_cb);
ctx->worker = worker ? SvREFCNT_inc(worker) : NULL;
ctx->remaining = len;
ctx->cvs = NULL;
ctx->num_cvs = 0;
ctx->shared_cv = NULL;
ctx->abandoned = 0;
ifp_guard *guard = ifp_guard_push(aTHX_ &ctx->is_freed_ptr, NULL,
NULL, &ctx->abandoned);
int *is_freed = &guard->is_freed;
/* Counted before anything is dispatched: a task that completes the whole
operation synchronously runs cleanup, which drops the context's
reference, and only this one keeps the cell alive until we return it. */
evf_handle *h = NULL;
if (want_handle) {
h = evf_handle_new(aTHX_ NULL, EVF_KIND_PARALLEL);
evf_handle_attach(aTHX_ h, ctx);
ctx->h = h;
guard->pending_h = h;
}
SV *done_rv = NULL;
if (unsafe) {
ctx->shared_cv = newXS(NULL, parallel_task_done, __FILE__);
CvXSUBANY(ctx->shared_cv).any_ptr = ctx;
done_rv = sv_2mortal(newRV_inc((SV*)ctx->shared_cv));
} else {
ctx->num_cvs = len;
Newxz(ctx->cvs, len, CV*);
}
dSP;
I32 i;
/* G_VOID as well as G_DISCARD: the value is discarded either way, but
without the want bit the task's tail expression runs in scalar context,
so a nested primitive there would allocate a handle nobody asked for.
The final_cb calls in this file already pass G_VOID for the same reason. */
U32 flags = G_DISCARD | G_VOID | (unsafe ? 0 : G_EVAL);
const int have_worker = (worker != NULL);
for (i = 0; i < len; i++) {
if (*is_freed) break;
SV **task_ary = (AvREAL(list) && !SvMAGICAL(list)) ? AvARRAY(list) : NULL;
SV **fetch_ptr = (task_ary && i <= AvFILL(list))
? &task_ary[i]
: av_fetch(list, i, 0);
SV *task_sv = fetch_ptr ? *fetch_ptr : NULL;
if (have_worker || IS_PVCV(task_sv)) {
CV *cv = NULL;
if (!unsafe) {
cv = newXS(NULL, parallel_task_done, __FILE__);
CvXSUBANY(cv).any_ptr = ctx;
ctx->cvs[i] = (CV*)SvREFCNT_inc((SV*)cv);
}
ENTER;
SAVETMPS;
if (!unsafe) {
done_rv = sv_2mortal(newRV_noinc((SV*)cv));
}
PUSHMARK(SP);
if (have_worker) {
XPUSHs(task_sv ? sv_2mortal(SvREFCNT_inc(task_sv)) : &PL_sv_undef);
}
XPUSHs(done_rv);
PUTBACK;
call_sv(have_worker ? worker : task_sv, flags);
SPAGAIN;
if (!unsafe) {
( run in 2.317 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )