GraphQL-Houtou
view release on metacpan or search on metacpan
lib/GraphQL/Houtou.xs view on Meta::CPAN
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) {
lib/GraphQL/Houtou.xs view on Meta::CPAN
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(
lib/GraphQL/Houtou/Runtime/NativeRuntime.pm view on Meta::CPAN
# to the next stall. No progress while promises remain pending is reported
# as a deadlock.
sub _settle_result {
my ($self, $result, $on_stall) = @_;
return $result
if !GraphQL::Houtou::XS::VM::runtime_value_is_async_xs(
$self->_native_runtime_handle, $result,
);
my ($settled, $value) = (0, undef);
# Keep the derived chain alive until settlement; Future-style adapters may
# otherwise discard it before its callbacks run.
my $chain;
eval {
$chain = GraphQL::Houtou::XS::VM::runtime_then_async_xs(
$self->_native_runtime_handle,
$result,
sub { ($settled, $value) = (1, $_[0]) },
sub { ($settled, $value) = (-1, $_[0]) },
);
1;
t/59_on_stall_native_drive.t view on Meta::CPAN
local $@;
eval { $pending_runtime->execute_document('{ hang }', on_stall => sub { die "on_stall boom\n" }) };
$@;
};
like $err, qr/on_stall boom/, 'the on_stall die propagates';
assert_no_live_frames('after on_stall die (plain Promise::XS pending)');
};
subtest 'on_stall itself dying while a DataLoader Ticket is pending propagates cleanly' => sub {
# Unlike a plain Promise::XS, a DataLoader Ticket that never gets
# dispatched is kept alive independent of the exec state: subscribing to
# it (gql_runtime_vm_subscribe_dataloader_ticket) pushes our resolve/
# reject callback pair onto the Ticket's own subscriber list, which the
# DataLoader's own queue can keep alive well past the abandoned request.
# gql_runtime_vm_cancel_frame_tree disarms that pair's ctx (dropping its
# state_sv/frame refs) so this does not leak the frame tree.
my $loader = GraphQL::Houtou::DataLoader->new(
batch => sub { my ($ids) = @_; return [ map { "v:$_" } @$ids ] },
);
my $err = do {
local $@;
eval {
$runtime->execute_document(
$QUERY,
t/63_async_adapter_promise_es6.t view on Meta::CPAN
'Promise::ES6 pending response is correct';
{
$pending_promise = undef;
$resolve_pending = undef;
my $owner_runtime = $schema->build_native_runtime(async_adapter => $adapter);
my $weak_handle = $owner_runtime->_native_runtime_handle;
weaken($weak_handle);
my $owned_response = $owner_runtime->execute_document('{ pending }');
undef $owner_runtime;
ok defined($weak_handle), 'pending response keeps its native runtime alive';
my $owned_value;
$owned_response->then(sub { $owned_value = $_[0] });
$resolve_pending->('owned');
is_deeply $owned_value, { data => { pending => 'owned' } },
'response settles after its Perl runtime is released';
($owned_response, $pending_promise, $resolve_pending) = (undef, undef, undef);
ok !defined($weak_handle), 'native runtime is released with the response';
}
$then_error = 'adapter then failed';
( run in 3.398 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )