Hypersonic

 view release on metacpan or  search on metacpan

lib/Hypersonic/UA/Async.pm  view on Meta::CPAN

      ->xs_function('xs_async_get_result')
      ->xs_preamble
      ->line('if (items < 1) croak("Usage: get_result($slot)");')
      ->blank
      ->line('int slot = SvIV(ST(0));')
      ->line('if (slot < 0 || slot >= MAX_ASYNC_CONTEXTS || !async_registry[slot].in_use) {')
      ->line('    ST(0) = &PL_sv_undef;')
      ->line('    XSRETURN(1);')
      ->line('}')
      ->blank
      ->line('AsyncContext *ctx = &async_registry[slot];')
      ->blank
      ->if('ctx->state == ASYNC_STATE_ERROR && ctx->error')
        ->comment('Return error as a list (0, error_msg)')
        ->line('ST(0) = sv_2mortal(newSViv(0));')
        ->line('ST(1) = sv_2mortal(newSVpv(ctx->error, 0));')
        ->line('XSRETURN(2);')
      ->elsif('ctx->state == ASYNC_STATE_DONE && ctx->recv_buffer')
        ->comment('Return success as a list (1, body)')
        ->line('ST(0) = sv_2mortal(newSViv(1));')
        ->line('ST(1) = sv_2mortal(newSVpvn(ctx->recv_buffer, ctx->recv_buffer_len));')
        ->line('XSRETURN(2);')
      ->else
        ->comment('Not ready yet')
        ->line('ST(0) = &PL_sv_undef;')
        ->line('XSRETURN(1);')
      ->endif
      ->xs_end
      ->blank;
}

sub gen_xs_tick {
    my ($class, $builder, $opts) = @_;

    my $event_backend = $opts->{event_backend};
    my $backend_name = $opts->{event_backend_name} // 'kqueue';

    $builder->comment("Process pending async events - AUTO-TICKING pure C path ($backend_name)")
      ->comment('Loops until all requests complete OR no progress for 1ms')
      ->xs_function('xs_ua_tick')
      ->xs_preamble
      ->line('int i;')
      ->line('int nev;')
      ->line('I32 j;')
      ->line('if (items < 1) croak("Usage: $ua->tick()");')
      ->blank
      ->line('SV *self_sv = ST(0);')
      ->line('HV *ua_hv = (HV *)SvRV(self_sv);')
      ->blank
      ->comment('Get the _async_pending array')
      ->line('SV **pending_svp = hv_fetch(ua_hv, "_async_pending", 14, 0);')
      ->if('!pending_svp || !SvROK(*pending_svp)')
        ->line('ST(0) = sv_2mortal(newSViv(0));')
        ->line('XSRETURN(1);')
      ->endif
      ->blank
      ->line('AV *pending_av = (AV *)SvRV(*pending_svp);')
      ->blank
      ->comment('Main tick loop - process until no pending or no progress')
      ->line('int total_completed = 0;')
      ->line('int iterations = 0;')
      ->line('int max_iterations = 1000;  /* Safety cap */')
      ->blank
      ->line('tick_loop:')
      ->line('{')
      ->line('    I32 len = av_len(pending_av) + 1;')
      ->line('    if (len == 0 || iterations++ >= max_iterations) {')
      ->line('        ST(0) = sv_2mortal(newSViv(len));')
      ->line('        XSRETURN(1);')
      ->line('    }')
      ->blank
      ->comment('    Collect slots from pending array')
      ->line('    int slots[MAX_ASYNC_CONTEXTS];')
      ->line('    int slot_count = 0;')
      ->blank
      ->line('    for (j = 0; j < len; j++) {')
      ->line('        SV **slot_svp = av_fetch(pending_av, j, 0);')
      ->line('        if (!slot_svp) continue;')
      ->line('        int slot = SvIV(*slot_svp);')
      ->line('        if (slot >= 0 && slot < MAX_ASYNC_CONTEXTS) {')
      ->line('            AsyncContext *ctx = &async_registry[slot];')
      ->line('            if (ctx->in_use && ctx->fd >= 0) {')
      ->line('                slots[slot_count++] = slot;')
      ->line('            }')
      ->line('        }')
      ->line('    }')
      ->blank;
    
    # Create event loop using the backend
    $builder->comment('Create event loop if needed')
      ->line('if (async_ev_fd < 0) {');
    $event_backend->gen_create_loop($builder, 'async_ev_fd');
    $builder->line('}')
      ->blank;
    
    # Register all fds - use backend's native slot-tracking struct.
    # See note in gen_xs_poll_batch above: io_uring overrides this to
    # epoll_event because its slot helpers run on a private epoll fd.
    my $event_struct = $event_backend->slot_event_struct;
    
    $builder->comment('Register all fds with event loop')
      ->line('int change_count = 0;')
      ->blank
      ->line('for (i = 0; i < slot_count; i++) {')
      ->line('    int slot = slots[i];')
      ->line('    AsyncContext *ctx = &async_registry[slot];')
      ->line('    int events = async_poll_one(slot);')
      ->line('    if (events == ASYNC_WAIT_NONE) continue;')
      ->blank
      ->line('    if (events == ASYNC_WAIT_READ) {');
    $event_backend->gen_add_with_slot($builder, 'async_ev_fd', 'ctx->fd', 'slot', 'read');
    $builder->line('    } else {');
    $event_backend->gen_add_with_slot($builder, 'async_ev_fd', 'ctx->fd', 'slot', 'write');
    $builder->line('    }')
      ->line('    change_count++;')
      ->line('}')
      ->blank;
    
    # Wait for events
    $builder->comment('Wait for events (1ms timeout)')
      ->line("struct $event_struct ready_events[MAX_EVENTS];");
    $event_backend->gen_wait_once($builder, 'async_ev_fd', 'ready_events', 'nev', '1');
    
    # Process ready events
    $builder->blank
      ->comment('Process ready events in pure C')
      ->line('for (i = 0; i < nev; i++) {');



( run in 0.784 second using v1.01-cache-2.11-cpan-71847e10f99 )