Acme-Parataxis

 view release on metacpan or  search on metacpan

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


Returns the currently configured maximum thread pool size.

=head1 BLOCKING & I/O FUNCTIONS

These functions B<suspend> the current fiber and offload the actual blocking work to the native thread pool.

=head2 C<await_sleep( $ms )>

Suspends the fiber for C<$ms> milliseconds. While the background thread sleeps, other fibers can continue to execute.

=head2 C<await_read( $fh, $timeout = 5000 )>

Suspends the fiber until the filehandle C<$fh> is ready for reading, or the C<$timeout> (in milliseconds) is reached.

    my $status = Acme::Parataxis->await_read($socket);
    if ($status > 0) {
        my $data = <$socket>;
    }

=head2 C<await_write( $fh, $timeout = 5000 )>

Suspends the fiber until the filehandle C<$fh> is ready for writing.

=head2 C<await_core_id( )>

Offloads a request to the thread pool and returns the ID of the CPU core that handled the job.

=head1 MANUAL FIBER MANAGEMENT

Advanced users can manage context switching themselves without using the integrated scheduler.

=head2 C<new( code =E<gt> $sub )>

Instantiates a new fiber. The C<code> argument must be a subroutine reference.

    my $fiber = Acme::Parataxis->new(code => sub {
        my $arg = Acme::Parataxis->yield("Initial data");
        return "Done with $arg";
    });

=head2 C<call( @args )>

Explicitly switches control to the fiber and passes C<@args>. Arguments can be scalars, hash/array references, or
objects. This establishes a parent/child relationship: when the fiber yields or completes, control returns to the
caller.

=head2 C<transfer( @args )>

A "symmetric" switch. Suspends the current context and moves directly to the target fiber. No parent/child relationship
is established. Like C<call>, it supports passing arbitrary Perl data via C<@args>.

=head1 PREEMPTION

=head2 C<maybe_yield( )>

Increments an internal operation counter for the current fiber. If the counter reaches the threshold set by
C<set_preempt_threshold>, the fiber automatically yields.

    while (my $row = $sth->fetch) {
        process($row);
        Acme::Parataxis->maybe_yield( ); # Cooperatively prevent starvation
    }

=head2 C<set_preempt_threshold( $val )>

Sets the number of C<maybe_yield> increments before a forced yield occurs. Default is 0 (preemption disabled).

=head1 Class Methods

=head2 C<tid( )>

Returns the unique OS Thread ID of the main interpreter thread.

=head2 C<current_fid( )>

Returns the unique numeric ID of the currently executing fiber, or -1 if called from the "root" (main) context.

=head2 C<root( )>

Returns a proxy object representing the initial execution context. This is useful for C<transfer( )>ing control back to
the main thread from a symmetric coroutine.

=head1 Acme::Parataxis OBJECT METHODS

=head2 C<fid( )>

Returns the unique numeric ID of the fiber object.

=head2 C<is_done( )>

Returns true if the fiber has finished execution (either by returning or dying). Once a fiber is done, its internal ID
is released and it can no longer be called.

=head1 Acme::Parataxis::Future OBJECT METHODS

=head2 C<await( )>

Suspends the current fiber until the future is ready. Returns the result or B<dies> if the task encountered an error.

=head2 C<is_ready( )>

Returns true if the task associated with the future has completed.

=head2 C<result( )>

Returns the task result immediately. Croaks if the future is not yet ready.


=head1 INTEGRATING SYNCHRONOUS MODULES

To use synchronous modules (like C<HTTP::Tiny>) in a non-blocking way, you can subclass their handle or transport
methods and use a C<while> loop combined with C<yield('WAITING')>. This ensures the fiber yields control until the
underlying I/O is ready.

    # Example: A cooperative HTTP::Tiny subclass
    {
        package My::HTTP;
        use parent 'HTTP::Tiny';
        sub _open_handle {
            my ($self, $request, $scheme, $host, $port, $peer) = @_;

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

            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

The 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

Signals 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 send a signal while a fiber is suspended, it will
generally be processed when the fiber is resumed and hits the 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 <sanko@cpan.org>

=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 1.639 second using v1.01-cache-2.11-cpan-995e09ba956 )