Acme-Parataxis

 view release on metacpan or  search on metacpan

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

                timeout            => $self->{timeout},
                keep_alive         => $self->{keep_alive},
                keep_alive_timeout => $self->{keep_alive_timeout}
            )->connect($scheme, $host, $port, $peer);
        }
        sub request {
            my ($self, $method, $url, $args) = @_;
            my %new_args = %{ $args // {} };
            my $orig_cb = $new_args{data_callback};
            my $content = '';
            $new_args{data_callback} = sub {
                my ($data, $response) = @_;
                if ($orig_cb) { return $orig_cb->($data, $response) }
                $content .= $data;
                return 1;
            };
            my $res = $self->SUPER::request($method, $url, \%new_args);
            $res->{content} = $content unless $orig_cb;
            return $res;
        }
    }
    {
        package My::HTTP::Handle;
        use parent -norequire, 'HTTP::Tiny::Handle';
        use Time::HiRes qw[time];
        sub _do_timeout {
            my ($self, $type, $timeout) = @_;
            $timeout //= $self->{timeout} // 60;
            my $start = time;
            while (1) {
                # Check for readiness NOW (0 timeout)
                return 1 if $self->SUPER::_do_timeout($type, 0);
                # Check for overall timeout
                my $elapsed = time - $start;
                return 0 if $elapsed > $timeout;
                # Suspend fiber and wait for background I/O check
                my $wait = ($timeout - $elapsed) > 0.5 ? 0.5 : ($timeout - $elapsed);
                if ($type eq 'read') {
                    Acme::Parataxis->await_read($self->{fh}, int($wait * 1000));
                } else {
                    Acme::Parataxis->await_write($self->{fh}, int($wait * 1000));
                }
            }
        }
    }

=head1 EXAMPLES

=head2 Cooperative Parallelism

This example demonstrates how to perform multiple HTTP requests concurrently on a single interpretation thread.

    use Acme::Parataxis;
    # ... (See My::HTTP implementation in INTEGRATING SYNCHRONOUS MODULES) ...

    Acme::Parataxis::run(sub {
        my $http = My::HTTP->new(verify_SSL => 0);
        my @urls = qw[http://example.com http://perl.org];

        # Spawn tasks for each URL
        my @futures = map {
            my $url = $_;
            Acme::Parataxis->spawn(sub { $http->get($url)->{status} })
        } @urls;

        # Collect results as they become ready
        say "Status for $urls[$_]: " . $futures[$_]->await( ) for 0..$#urls;
    });

=head2 Symmetric Producer/Consumer

A low-level example of Passing control sideways between fibers.

    my ($p, $c);

    $p = Acme::Parataxis->new(code => sub {
        for my $item (qw[Apple Banana Cherry]) {
            say "Producer: Sending $item";
            $c->transfer($item);
        }
        $c->transfer('DONE');
    });

    $c = Acme::Parataxis->new(code => sub {
        my $item = Acme::Parataxis->yield( ); # Initial wait
        while (1) {
            last if $item eq 'DONE';
            say "Consumer: Eating $item";
            $item = $p->transfer( );
        }
    });

    $c->call( ); # Prime consumer
    $p->call( ); # Start producer

=head1 BEST PRACTICES & GOTCHAS

=over

=item * B<Avoid blocking syscalls:> Never call blocking C<sleep( )> or C<sysread( )> on the main interpretation thread.
Always use the C<await_*> equivalents to offload work to the pool.

=item * B<Thread Safety:> While Perl code remains single-threaded, background tasks run on separate OS threads. Shared
C-level data (if accessed via FFI) must be mutex-protected.

=item * B<Stack Limits:> Each fiber is allocated a 512KB stack by default. This is more than sufficient for most
Perl code and allows for high concurrency with a small memory footprint. Extremely deep recursion or massive regex
backtracking might still hit limits.

=item * B<Efficiency:> The native thread pool is initialized dynamically upon the first asynchronous request. It
starts with a small "seed" pool and grows on demand up to the configured limit. Worker threads use condition
variables to sleep efficiently when idle, ensuring near-zero CPU usage when no background tasks are pending.

=item * B<Reference Cycles:> Be careful when passing fiber objects into their own closures, as this can create
memory leaks.

=back

=head1 GORY TECHNICAL DETAILS

=head2 Architectural Inspiration



( run in 1.068 second using v1.01-cache-2.11-cpan-13bb782fe5a )