Acme-Parataxis

 view release on metacpan or  search on metacpan

lib/Acme/Parataxis.pm  view on Meta::CPAN

        affix $l, 'get_job_coro_id',                                       [Int],                          Int;
        affix $l, 'free_job_slot',                                         [Int],                          Void;
        affix $l, 'get_thread_pool_size',                                  [],                             Int;
        affix $l, 'get_max_thread_pool_size',                              [],                             Int;
        affix $l, 'get_fd_setsize',                                        [],                             Int;
        affix $l, 'set_max_threads',                                       [Int],                          Void;
        affix $l, 'get_max_fibers',                                        [],                             Int;
        affix $l, 'set_max_fibers',                                        [Int],                          Void;
        affix $l, [ 'set_preempt_threshold' => '_set_preempt_threshold' ], [LongLong],                     Void;
        affix $l, [ 'maybe_yield' => '_maybe_yield' ],                     [],                             Pointer [SV];
        affix $l, 'get_preempt_count',                                     [],                             LongLong;

        # Capture the main interpreter context
        init_system();
        if ( $^O eq 'MSWin32' ) {
            my $perl_dll = $Config{libperl};
            $perl_dll =~ s/^lib//;
            $perl_dll =~ s/\.a$//;
            $perl_dll .= '.' . $Config{so};
            my $p = Affix::load_library($perl_dll);
            affix $p, 'win32_get_osfhandle', [Int], LongLong;
        }
    }

    BEGIN {
        my $lib_name = ( $^O eq 'MSWin32' ? '' : 'lib' ) . 'parataxis.' . $Config{so};
        my @paths;
        push @paths, File::Spec->catfile( dirname(__FILE__), $lib_name );
        push @paths, File::Spec->catfile( dirname(__FILE__), '..',   'arch', 'auto',      'Acme', 'Parataxis', $lib_name );
        push @paths, File::Spec->catfile( dirname(__FILE__), '..',   '..',   'arch',      'auto', 'Acme', 'Parataxis', $lib_name );
        push @paths, File::Spec->catfile( dirname(__FILE__), 'auto', 'Acme', 'Parataxis', $lib_name );
        for my $inc (@INC) {
            next if ref $inc;
            push @paths, File::Spec->catfile( $inc, 'auto', 'Acme', 'Parataxis', $lib_name );
        }
        for my $path (@paths) {
            if ( -e $path ) {
                $lib = Affix::load_library($path);
                last if $lib;
            }
        }
        die 'Could not find or load ' . $lib_name unless $lib;
        _bind_functions($lib);
    }

    # API aliases and wrappers
    sub fiber : prototype(&) ($code) { spawn( __PACKAGE__, $code ) }
    sub async : prototype(&) ($code) { return run($code) }

    # Index at which user arguments start in @_: 0 when it is a plain call, 1 when $_[0] is a self (package name or
    # blessed object). This mirrors the shift/unshift invocant dance used elsewhere, but reads $_[0] instead of
    # shifting, so calling it never reifies the caller's pad @_. A reified @_ left behind in a parked frame trips
    # pp_entersub's assert(!AvREAL(av))/assert(AvFILLp(av) == -1) when a different fiber later enters the same sub at
    # the same depth.
    sub _arg_offset {
        my $self = $_[0];
        return ( defined $self && ( ( ref $self || $self ) eq __PACKAGE__ || ( builtin::blessed($self) && $self->isa(__PACKAGE__) ) ) ) ? 1 : 0;
    }

    # Aggregate a list of futures into one that resolves when every input has: the result is an arrayref of each
    # input's result, in input order, or the stored error of the first input to fail (reject-fast, Promise.all style).
    # Inputs are not cancelled on a rejection; they are independent and keep running, and their later results are
    # discarded. Zero futures resolve immediately with [] (matching Promise.all([])). An already-resolved input
    # fires its on_ready callback inline, so a wait_all of ready inputs resolves inline (no fiber needed) and any
    # mixed ready/pending input resolves the moment the last straggler lands. Inputs must be Acme::Parataxis::Future
    # objects else croaks. Also callable as Acme::Parataxis->wait_all(...).
    sub wait_all {
        my $o       = _arg_offset( $_[0] );
        my @futures = @_[ $o .. $#_ ];
        @_ = ();
        croak 'wait_all() requires Acme::Parataxis::Future inputs'
            if grep { !( defined $_ && ref $_ && $_->isa('Acme::Parataxis::Future') ) } @futures;
        require Acme::Parataxis::Future;
        my $all = Acme::Parataxis::Future->new;
        if ( @futures == 0 ) {
            $all->set_result( [] );
            return $all;
        }
        my @results;
        my $pending = scalar @futures;
        for my $i ( 0 .. $#futures ) {
            my $idx = $i;
            my $fut = $futures[$i];
            $fut->on_ready(
                sub ($f) {
                    return if $all->is_ready;
                    if ( defined $f->error ) {
                        $all->set_error( $f->error );    # reject-fast: the first failure wins, copied wholesale
                        return;
                    }
                    $results[$idx] = $f->result;
                    $all->set_result( \@results ) if --$pending == 0;
                }
            );
        }
        return $all;
    }

    # Aggregate a list of futures into one that settles with a wholesale copy of the first input to settle - success
    # or failure (Promise.race). The losers are untouched and keep running; their eventual results are dropped by the
    # is_ready guard. An already-ready input settles the aggregate inline. At least one input is required (an empty
    # race would never settle) and each input must be an Acme::Parataxis::Future else croaks. Also callable as
    # Acme::Parataxis->wait_any(...).
    sub wait_any {
        my $o       = _arg_offset( $_[0] );
        my @futures = @_[ $o .. $#_ ];
        @_ = ();
        croak 'wait_any() requires at least one Acme::Parataxis::Future input' unless @futures;
        croak 'wait_any() requires Acme::Parataxis::Future inputs'
            if grep { !( defined $_ && ref $_ && $_->isa('Acme::Parataxis::Future') ) } @futures;
        require Acme::Parataxis::Future;
        my $any = Acme::Parataxis::Future->new;
        for my $f (@futures) {
            $f->on_ready(
                sub ($f) {
                    return if $any->is_ready;
                    if   ( defined $f->error ) { $any->set_error( $f->error ) }     # wholesale copy of the failure
                    else                       { $any->set_result( $f->result ) }
                }
            );
        }



( run in 2.191 seconds using v1.01-cache-2.11-cpan-b554fb43c41 )