=pod

=encoding utf8

=head1 NAME

Acme::Parataxis - A terrible idea, honestly...

=head1 SYNOPSIS

The classic way (as I write this, Acme::Parataxis is 5 days old and already has a 'classic' API...)

    use v5.40;
    use Acme::Parataxis;
    $|++;

    Acme::Parataxis::run(
        sub {
            say 'Main task started';

            # Spawn background workers
            my $f1 = Acme::Parataxis->spawn(
                sub {
                    say '  Task 1: Sleeping in a native thread pool...';
                    Acme::Parataxis->await_sleep(1000);
                    say '  Task 1: Ah! What a nice nap...';
                    return 42;
                }
            );
            my $f2 = Acme::Parataxis->spawn(
                sub {
                    say '  Task 2: Performing I/O...';

                    # await_read/write for non-blocking socket handling
                    return 'I/O Done';
                }
            );

            # Block current fiber until results are ready (without blocking the thread)
            say 'Result 1: ' . $f1->await( );
            say 'Result 2: ' . $f2->await( );
        }
    );

Or do things the more modern way:

    use v5.40;
    use Acme::Parataxis qw[:all];
    $|++;

    async {
        say 'Main task started';

        # 'fiber' is a shorter alias for 'spawn'
        my $f1 = fiber {
            say '  Task 1: Sleeping...';
            await_sleep(1000);
            return 42;
        };

        my $f2 = fiber {
            say '  Task 2: Performing I/O...';
            # ...
            return 'I/O Done';
        };

        # 'await' works on fibers and futures
        say 'Result 1: ' . await($f1);
        say 'Result 2: ' . await($f2);
    };

=head1 DESCRIPTION

C<Acme::Parataxis> implements a hybrid concurrency model for Perl, greatly inspired by the concurrency system for the
L<Wren|https://wren.io/concurrency.html> programming language. It combines cooperative multitasking (fibers) with a
preemptive native thread pool.

Fibers are a mechanism for lightweight concurrency. They are similar to threads but are cooperatively scheduled. While
the OS may switch between threads at any time, a fiber only passes control when explicitly told to do so. This makes
concurrency deterministic and easier to reason about. You (probably) don't have to worry about random context switches
clobbering your data.

Fibers are incredibly lightweight. Each one has its own stack and context, but they don't use OS thread resources. You
can easily create thousands of them without stalling your system.

=head2 A Warning

I had this idea while writing cookbook examples for Affix. I wondered if I could implement a hybrid concurrency model
for Perl from within FFI. This is that unpublished article made into a module. It's fragile. It's dangerous. It's my
attempt at combining cooperative multitasking (green threads or fibers or whatever they're called in the latest edit of
Wikipedia) with a preemptive native thread pool. It's Acme::Parataxis.

This module is experimental and resides in the C<Acme::> namespace for a reason. It manually manipulates Perl's
internal stacks and C context. It is very dangerous. It's irresponsible, honestly, that I'm even putting this terrible
idea into the world. Don't use this. Forget you even saw it. Just B<reading> this has probably made your projects more
prone to breaking. Reading the package name out loud might cause brain damage to yourself and those within earshot.

Close the browser and clear your history before this does further harm!

=head1 MODERN API

While the classic object-oriented API is always available, C<Acme::Parataxis> exports a set of functions (via the
C<:all> tag) that provide a more modern, concise way to write concurrent code.

=head2 C<async { ... }>

A convenience wrapper around C<run( )>. It starts the scheduler, executes the provided block as the main fiber, and
automatically calls C<stop( )> when the block completes.

    async {
        say "The scheduler is running!";
    };

=head2 C<fiber { ... }>

An alias for C<spawn( )>. It creates a new fiber and returns a L<Future|/"Acme::Parataxis::Future OBJECT METHODS">.

    my $f = fiber {
        say "Hello from fiber!";
    };

=head2 C<await( $thing )>

A generic await function. It accepts either an C<Acme::Parataxis> fiber object or an C<Acme::Parataxis::Future> and
suspends the current fiber until the target is ready.

    my $result = await($f);

=head2 C<await_sleep( $ms )>

Suspends the current fiber for C<$ms> milliseconds. This is a non-blocking operation that allows other fibers to run
while the current one is paused.

    async {
        say "Taking a nap...";
        await_sleep(1000);
        say "I'm awake!";
    };

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

Suspends the current fiber until the provided filehandle is ready for reading, or the timeout is reached.

    async {
        await_read($socket);
        my $data = <$socket>;
        say "Received: $data";
    };

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

Suspends the current fiber until the provided filehandle is ready for writing, or the timeout is reached.

    async {
        await_write($socket);
        syswrite($socket, $message);
    };

=head2 C<await_core_id( )>

Returns the ID of the CPU core currently executing the background task. This is a non-blocking operation that offloads
the request to the thread pool and suspends the fiber until the result is ready.

    async {
        my $core = await_core_id( );
        say "Background task handled by CPU core: $core";
    };

=head1 CORE CONCEPTS

=head2 Creating Fibers

All Perl code in this system runs within a fiber. When you start your script or call C<Acme::Parataxis::run>, a "main"
fiber is active. You can create new fibers using C<spawn> or by manually instantiating an C<Acme::Parataxis> object:

    my $fiber = Acme::Parataxis->new(code => sub {
        say "I'm in a fiber!";
    });

Creating a fiber does not run it immediately. It simply prepares the context and waits to be invoked.

=head2 Invoking Fibers

To run a fiber, you "call" it. This suspends the current fiber and executes the called one until it finishes or yields.

    $fiber->call( );

When the called fiber finishes, control returns to the fiber that called it. It is an error to call a fiber that is
already done.

=head2 Yielding

Yielding is the "secret sauce" of fibers.

A yielded fiber passes control back to its caller but remembers its exact state including all variables and the current
instruction pointer. The next time it's called, it resumes exactly where it left off.

    Acme::Parataxis->yield( );

=head2 Communication (Passing Values)

Fibers can pass data back and forth through C<call> and C<yield>:

=over

=item * B<Resuming with a value>: Arguments passed to C<$fiber-E<gt>call(@args)> are returned by the C<yield( )> call that
suspended the fiber.

=item * B<Yielding with a value>: Arguments passed to C<Acme::Parataxis-E<gt>yield(@args)> are returned to the caller by
the C<call( )> that resumed the fiber.

=back

=head2 Full Coroutines

Fibers in Parataxis are "full coroutines." This means they can suspend from anywhere in the callstack. You can call
C<yield( )> from deeply nested functions, and the entire fiber stack will be suspended until the fiber is resumed.

=head2 Transferring Control

While C<call( )> and C<yield( )> manage a stack-like chain of execution, C<transfer( )> provides an unstructured way to
switch between fibers. When you transfer to a fiber, the current one is suspended, and the target fiber resumes. Unlike
C<call( )>, transferring does not establish a parent/child relationship. It's more like a C<goto> for execution
contexts.

    $other_fiber->transfer( );

=head2 Fibers vs. Threads

In Parataxis, your B<Perl code> always runs on a single OS thread. However, when you call an C<await_*> function, the
current fiber is suspended, and the actual blocking work is performed on a B<different> OS thread in a native pool.
Once the task completes, your fiber is automatically queued for resumption on the main thread.

=head1 SCHEDULER FUNCTIONS

The following functions are the primary interface for the integrated cooperative scheduler.

=head2 C<run( $code )>

Starts the event loop and executes C<$code> as the initial fiber. The loop continues to run as long as there are active
fibers or pending background tasks.

    Acme::Parataxis::run(sub {
        say "The scheduler is running!";
    });

=head2 C<spawn( $code )>

Creates a new fiber and adds it to the scheduler's queue. Returns a L<Future|/"Acme::Parataxis::Future OBJECT METHODS">
that will eventually contain the fiber's return value.

    my $future = Acme::Parataxis->spawn(sub {
        return "Hello from fiber #" . Acme::Parataxis->current_fid;
    });

=head2 C<yield( @args )>

Pauses the current fiber and returns control to the scheduler. If C<@args> are provided, they are passed to the context
that next resumes this fiber. Arguments can be of any Perl data type.

=head2 C<stop( )>

Tells the scheduler to exit the loop after the current iteration. Note that this does not immediately terminate other
fibers; it simply prevents the scheduler from starting new ones.

=head1 THREAD POOL CONFIGURATION

C<Acme::Parataxis> uses a native thread pool to handle blocking tasks. While it manages itself automatically, you can
tune its behavior using these functions.

=head2 C<set_max_threads( $count )>

Sets the maximum number of worker threads the pool is allowed to spawn. By default, this is set to the number of
logical CPU cores detected on your system (up to a hard limit of 64).

    # Limit the pool to 4 threads
    set_max_threads(4);

=head2 C<max_threads( )>

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) = @_;
            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));
                }
            }
        }
    }

=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

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
