GraphQL-Houtou
view release on metacpan or search on metacpan
lib/GraphQL/Houtou.xs view on Meta::CPAN
IV entry_index,
SV **resolve_rv_out,
SV **reject_rv_out
)
{
gql_runtime_vm_pending_callback_ctx_t *ctx;
if (gql_runtime_vm_pending_cb_pool_count > 0) {
IV n = --gql_runtime_vm_pending_cb_pool_count;
SV *resolve_cv = gql_runtime_vm_pending_cb_pool_resolve[n];
SV *reject_cv = gql_runtime_vm_pending_cb_pool_reject[n];
gql_runtime_vm_pending_cb_pool_resolve[n] = NULL;
gql_runtime_vm_pending_cb_pool_reject[n] = NULL;
ctx = INT2PTR(
gql_runtime_vm_pending_callback_ctx_t *,
CvXSUBANY((CV *)resolve_cv).any_ptr
);
ctx->state_sv = state_sv ? SvREFCNT_inc_simple_NN(state_sv) : NULL;
ctx->frame = frame;
if (ctx->frame) {
ctx->frame->refcount++;
}
ctx->entry_index = entry_index;
/* The pool's CV refcounts transfer into the returned RVs. */
*resolve_rv_out = newRV_noinc(resolve_cv);
*reject_rv_out = newRV_noinc(reject_cv);
return ctx;
}
{
CV *resolve_cv;
CV *reject_cv;
Newxz(ctx, 1, gql_runtime_vm_pending_callback_ctx_t);
ctx->state_sv = state_sv ? SvREFCNT_inc_simple_NN(state_sv) : NULL;
ctx->frame = frame;
if (ctx->frame) {
ctx->frame->refcount++;
}
ctx->entry_index = entry_index;
ctx->cv_refcnt = 2;
resolve_cv = newXS(NULL, gql_runtime_vm_xs_pending_callback, __FILE__);
CvXSUBANY(resolve_cv).any_ptr = ctx;
gql_runtime_vm_attach_callback_magic_ptr(aTHX_ (SV *)resolve_cv, &gql_runtime_vm_pending_callback_ctx_vtbl, ctx);
reject_cv = newXS(NULL, gql_runtime_vm_xs_pending_reject_callback, __FILE__);
CvXSUBANY(reject_cv).any_ptr = ctx;
gql_runtime_vm_attach_callback_magic_ptr(aTHX_ (SV *)reject_cv, &gql_runtime_vm_pending_callback_ctx_vtbl, ctx);
ctx->resolve_cv = (SV *)resolve_cv;
ctx->reject_cv = (SV *)reject_cv;
*resolve_rv_out = newRV_noinc((SV *)resolve_cv);
*reject_rv_out = newRV_noinc((SV *)reject_cv);
return ctx;
}
}
/* One arm fired: the promise has settled, so the other arm can never
* fire. Drop the ctx's references now (parked callbacks must not keep the
* exec state alive) and park the pair for the next arm to reuse. */
static void
gql_runtime_vm_pending_callback_pair_recycle(
pTHX_
gql_runtime_vm_pending_callback_ctx_t *ctx
)
{
if (!ctx) {
return;
}
if (ctx->state_sv) {
SvREFCNT_dec(ctx->state_sv);
ctx->state_sv = NULL;
}
if (ctx->frame) {
gql_runtime_vm_free_block_frame(aTHX_ ctx->frame);
ctx->frame = NULL;
}
ctx->entry_index = -1;
if (ctx->resolve_cv && ctx->reject_cv && ctx->cv_refcnt == 2
&& gql_runtime_vm_pending_cb_pool_count < GQL_VM_PENDING_CB_POOL_MAX) {
gql_runtime_vm_pending_cb_pool_resolve[gql_runtime_vm_pending_cb_pool_count] =
SvREFCNT_inc_simple_NN(ctx->resolve_cv);
gql_runtime_vm_pending_cb_pool_reject[gql_runtime_vm_pending_cb_pool_count] =
SvREFCNT_inc_simple_NN(ctx->reject_cv);
gql_runtime_vm_pending_cb_pool_count++;
}
}
/* Cancellation counterpart to gql_runtime_vm_pending_callback_pair_recycle:
* an armed entry whose promise/ticket never settled (the request was
* abandoned - on_stall died, or a stall was detected) still needs its
* ctx's state_sv/frame references dropped, exactly like a normal settle
* would, or they leak (a still-pending DataLoader Ticket's own subscriber
* list - populated by gql_runtime_vm_subscribe_dataloader_ticket - holds
* this ctx's resolve/reject CVs alive independent of anything the exec
* state owns, since the ticket can outlive the request via the
* DataLoader's own queue). Unlike recycle, this does NOT pool the CV
* pair: the ticket/promise might still fire it later (a buggy caller
* holding the loader past abandonment), and handing that live CV to an
* unrelated future request would corrupt it. Left disarmed, the CV pair
* is permanently inert instead - gql_runtime_vm_xs_pending_callback and
* its reject counterpart already no-op once ctx->state_sv/ctx->frame are
* NULL - trading the leak for a single bounded, harmless CV+ctx. */
static void
gql_runtime_vm_pending_callback_ctx_disarm(
pTHX_
gql_runtime_vm_pending_callback_ctx_t *ctx
)
{
if (!ctx) {
return;
}
if (ctx->state_sv) {
SvREFCNT_dec(ctx->state_sv);
ctx->state_sv = NULL;
}
if (ctx->frame) {
gql_runtime_vm_free_block_frame(aTHX_ ctx->frame);
ctx->frame = NULL;
}
ctx->entry_index = -1;
}
/* Shared by gql_runtime_vm_xs_list_pending_callback (a settled item that
* arrived as a Promise::XS/Ticket) and
* gql_runtime_vm_async_scheduler_resolve_frame's parent_list_pending
* branch below (a settled item that was a raw linked child frame):
* store the item's final native value and decrement unresolved_count.
* Does not enqueue/drain owner_frame - callers differ on whether that's
* safe/needed right here (a Promise::XS callback fires from outside any
* active drain and must kick one off itself if owner_frame is now ready;
* resolve_frame's branch runs from inside an active drain already and
* only needs to enqueue, mirroring the existing parent_frame branch). */
static void
gql_runtime_vm_list_pending_store_item_value(
pTHX_
gql_runtime_vm_list_pending_t *pending,
IV index,
gql_runtime_vm_native_value_t *item_value
)
{
gql_runtime_vm_native_list_store_at(aTHX_ pending->values_value, index, item_value);
if (pending->unresolved_count > 0) {
pending->unresolved_count--;
}
}
static void
gql_runtime_vm_async_scheduler_resolve_frame(
pTHX_
gql_runtime_vm_exec_state_handle_t *s,
gql_runtime_vm_block_frame_t *frame
)
{
SV *data_sv = NULL;
( run in 2.144 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )