Acme-Parataxis
view release on metacpan or search on metacpan
lib/Acme/Parataxis.pod view on Meta::CPAN
A channel of size C<1> is a rendezvous point (no buffering: C<put> waits for a matching C<get>); to buffer one element
use size C<2>, and so on.
use Acme::Parataxis;
use Acme::Parataxis::Channel;
my $q = Acme::Parataxis::Channel->new( 4 );
async {
fiber { $q->put( $_ ) for 1 .. 8 }; # producers
say $q->get for 1 .. 8; # consumer
};
=head2 Signals
An object with a two-state flag and a FIFO queue of waiters. A fiber parked in C<wait> does not busy-wait; it is
resumed by the scheduler when the signal fires.
use Acme::Parataxis;
use Acme::Parataxis::Signal;
my $sig = Acme::Parataxis::Signal->new;
async {
fiber { $sig->wait; say 'I rise!' };
$sig->send;
};
=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 virtual stack backed by mmap with a guard page. Physical memory is only consumed for pages the fiber actually touches, so this is cheap even for thousands of fibers. On Linux and FreeBSD the reservat...
=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 efficient...
=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
The core concurrency model in Parataxis is heavily inspired by the B<Wren> programming language, specifically its
treatment of fibers as the primary unit of execution and its deterministic cooperative scheduling.
=head2 Stack Virtualization
On Unix-like systems, we use C<ucontext.h> to manage stack and register state. On Windows, we leverage the native
C<Fiber API>. In both cases, we perform heart surgery on the Perl interpreter by manually teleporting its internal
global pointers (the C<PL_*> variables) between contexts.
=head2 Shared CVs and Pad Virtualization
A significant challenge in Perl green threads is the shared nature of PadLists and the global C<CvDEPTH> counter. In
debug builds of Perl, calling a shared subroutine from multiple fibers can trigger internal assertions (like
C<AvFILLp(av) == -1>). Parataxis includes a specialized workaround that surgically cleans the next landing pad before
every context switch to satisfy these assertions without clobbering active lexical state.
=head2 C<eval> vs. C<try/catch>
While C<feature 'try'> is available in modern Perl, manually teleporting interpreter state can occasionally confuse the
compiler's expectations for stack unwinding. Standard C<eval { ... }> remains the most predictable way to handle
exceptions within fibers.
=head2 Signal Handling
Not to be confused with C<Acme::Parataxis::Signal>, true OS-level signals (like C<SIGINT>) are delivered to the main
process thread. Perl handles these at 'safe points,' which in this module typically occur during a context switch
(yield, transfer, or call). If you receive an OS signal while a fiber is suspended, it will generally be processed when
the fiber is resumed and hits its next internal Perl opcode.
=head2 The 'Final Transfer' Requirement
In a symmetric coroutine model (using C<transfer()>), fibers don't have a natural 'parent' to return to. I've added
fallback logic to return to the C<last_sender> or the main thread on exit, but it's good practice to explicitly
C<transfer()> back to a partner fiber or the C<root()> context to ensure your application logic remains predictable.
Leaving a fiber to just 'fall off the end' is like walking out of a room without closing the door; eventually, the
draft will bother someone.
=head2 C<is_done()> vs. Destruction
A fiber being C<is_done()> simply means its Perl code has finished executing. The underlying C-level memory (stacks,
context, etc.) is not immediately freed until the C<Acme::Parataxis> object is destroyed or the runtime performs its
final C<cleanup()>. This is why you might see memory usage stay flat even after a fiber finishes, until the garbage
collector finally catches up with the object.
=head1 AUTHOR
Sanko Robinson L<https://github.com/sanko>
=head1 LICENSE
Copyright (C) Sanko Robinson.
This library is free software; you can redistribute it and/or modify it under the terms found in the Artistic License
2.
=cut
( run in 0.960 second using v1.01-cache-2.11-cpan-062aa07a564 )