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 1.354 second using v1.01-cache-2.11-cpan-14f38c9f855 )