Acme-Parataxis
view release on metacpan or search on metacpan
my ($self, $request, $scheme, $host, $port, $peer) = @_;
return My::HTTP::Handle->new(
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));
}
}
}
}
```
# EXAMPLES
## Cooperative Parallelism
This example demonstrates how to perform multiple HTTP requests concurrently on a single interpretation thread.
```perl
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;
});
```
## Symmetric Producer/Consumer
A low-level example of Passing control sideways between fibers.
```perl
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
```
# BEST PRACTICES & GOTCHAS
- **Avoid blocking syscalls:** Never call blocking `sleep( )` or `sysread( )` on the main interpretation thread.
Always use the `await_*` equivalents to offload work to the pool.
- **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.
- **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.
- **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.
- **Reference Cycles:** Be careful when passing fiber objects into their own closures, as this can create
memory leaks.
# GORY TECHNICAL DETAILS
## Architectural Inspiration
( run in 2.636 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )