<?php

declare(strict_types=1);

namespace Pokio;

use RuntimeException;

/**
 * @internal
 */
final class Environment
{
    /**
     * Cached value for maxProcesses to avoid redundant computations.
     */
    private static ?int $maxProcesses = null;

    /**
     * The number of processes that can be run in parallel.
     */

    /**
     * Whether the current environment supports forking.
     */
    public static function supportsFork(): bool
    {
        return extension_loaded('pcntl')
            && extension_loaded('posix')
            && class_exists('FFI');
    }

    /**
     * Get the total memory of the system in bytes.
     *
     * @param  string  $os  (default: PHP_OS_FAMILY) The operating system family.
     * @param  string|null  $memInfo  (default: null) The memory information string (used for testing on linux).
     */
    private static function getTotalMemory(string $os = PHP_OS_FAMILY, ?string $memInfo = null): int
    {
        if ($os === 'Linux') {
            $memInfo = $memInfo ?? @file_get_contents('/proc/meminfo');

            if (! $memInfo || ! preg_match('/MemTotal:\s+(\d+) kB/', $memInfo, $matches)) {
                throw new RuntimeException('Unable to determine total memory on Linux');
            }

            return (int) $matches[1] * 1024;
        }

        if ($os === 'Darwin') {
            return (int) shell_exec('sysctl -n hw.memsize');
        }

        throw new RuntimeException("Unsupported OS: $os");
    }
}

public static function maxProcesses(): int
    {
        if (self::$maxProcesses !== null) {
            return self::$maxProcesses;
        }
        $cpuCores = (int) shell_exec('getconf _NPROCESSORS_ONLN');
        if ($cpuCores <= 0) {
            $cpuCores = 1;
        }
        $ioFactor = (int) getenv('FORK_IO_FACTOR') ?: 3;
        $maxByCpu = $cpuCores * $ioFactor;
        $totalMemory = self::getTotalMemory(PHP_OS_FAMILY);
        $perProcessMemory = (int) getenv('FORK_MEM_PER_PROC') ?: 100 * 1024 * 1024;
        $maxByMemory = intdiv($totalMemory, $perProcessMemory);
        $maxProcesses = min($maxByCpu, $maxByMemory);
        self::$maxProcesses = max(1, $maxProcesses);
        return self::$maxProcesses;
    }

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Contracts\Runtime;
use Pokio\Kernel;
use Pokio\Promise;
use Pokio\UnwaitedFutureManager;
use RuntimeException;
use Throwable;

/**
 * @internal
 */
final class ForkRuntime implements Runtime
{
    /**
     * The PIDs of the currently running processes.
     *
     * @var array<int, true>
     */
    private static array $processes = [];

    /**
     * Creates a new fork runtime instance.
     */
    public function __construct(private int $maxProcesses)
    {
        //
    }

    /**
     * Cleans up any remaining processes on destruction, if any.
     */
    public function __destruct()
    {
        if (Kernel::instance()->isOrchestrator()) {
            foreach (array_keys(self::$processes) as $pid) {
                pcntl_waitpid($pid, $status);
            }
        }
    }

    /**
     * Defers the given callback to be executed asynchronously.
     *
     * @template TResult
     *
     * @param  Closure(): TResult  $callback
     * @return Future<TResult>
     */
    public function defer(Closure $callback): Future
    {
        while (count(self::$processes) >= $this->maxProcesses) {
            $this->waitForProcess();

            // Adding a little sleep to prevent busy-waiting and CPU
            // starvation, allowing other processes to run in the
            // meantime. -------------------------------------

            usleep(1000);
        }

        $ipc = IPC::create();
        $pid = pcntl_fork();

        if ($pid === -1) {
            throw new RuntimeException('Failed to fork process');
        }

        if ($pid === 0) {
            UnwaitedFutureManager::instance()->flush();

            // @codeCoverageIgnoreStart
            try {
                $result = $callback();

                if ($result instanceof Promise) {
                    $result = await($result);
                }
            } catch (Throwable $exception) {
                $result = new ThrowableCapsule($exception);
            }

            $data = serialize($result);

            $ipc->put($data);

            exit(0);
            // @codeCoverageIgnoreEnd
        }

        self::$processes[$pid] = true;

        /** @var Future<TResult> $future */
        // @phpstan-ignore-next-line
        $future = new ForkFuture($pid, $ipc, function (int $pid) {
            unset(self::$processes[$pid]);
        });

        return $future;
    }

    /**
     * Waits for a process to finish and removes it from the list of processes.
     */
    private function waitForProcess(): void
    {
        $pid = pcntl_wait($status, WNOHANG);

        if ($pid > 0) {
            unset(self::$processes[$pid]);
        }
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use FFI;
use RuntimeException;

/**
 * @internal
 */
final readonly class IPC
{
    private const string FILE_PREFIX = '/tmp/ipc_mem_';

    // POSIX constants
    private const int O_RDWR = 0x0002;

    private const int O_CREAT = 0x0040;

    private const int PROT_READ = 0x1;

    private const int PROT_WRITE = 0x2;

    private const int MAP_SHARED = 0x01;

    /**
     * Creates a new IPC memory block using a memory-mapped file.
     */
    private function __construct(
        private string $path,
    ) {
        //
    }

    /**
     * Creates a new IPC memory block using a memory-mapped file.
     */
    public static function create(): self
    {
        $id = bin2hex(random_bytes(8));
        $path = self::FILE_PREFIX.$id;
        touch($path);

        return new self($path);
    }

    /**
     * Writes data to the memory block.
     */
    public function put(string $data): void
    {
        // @codeCoverageIgnoreStart
        $ffi = self::libc();
        $length = mb_strlen($data, '8bit');

        $fd = $ffi->open($this->path, self::O_RDWR | self::O_CREAT, 0600);
        if ($fd < 0) {
            throw new RuntimeException('Failed to open file for writing');
        }

        $ffi->ftruncate($fd, $length);

        $ptr = $ffi->mmap(null, $length, self::PROT_READ | self::PROT_WRITE, self::MAP_SHARED, $fd, 0);
        $intptr = $ffi->cast('intptr_t', $ptr);

        // @phpstan-ignore-next-line
        if ($intptr === null || $intptr->cdata === -1) {
            throw new RuntimeException('mmap failed to write');
        }

        $ffi->memcpy($ptr, $data, $length);
        $ffi->munmap($ptr, $length);
        $ffi->close($fd);
        // @codeCoverageIgnoreEnd
    }

    /**
     * Reads and clears data from the memory block.
     */
    public function pop(): string
    {
        $ffi = self::libc();

        $fd = $ffi->open($this->path, self::O_RDWR, 0600);
        if ($fd < 0) {
            throw new RuntimeException('Failed to open file for reading');
        }

        $length = filesize($this->path);

        $ptr = $ffi->mmap(null, $length, self::PROT_READ, self::MAP_SHARED, $fd, 0);
        $intptr = $ffi->cast('intptr_t', $ptr);

        // @phpstan-ignore-next-line
        if ($intptr === null || $intptr->cdata === -1) {
            throw new RuntimeException('mmap failed to read');
        }

        $data = FFI::string($ptr, $length);
        $ffi->munmap($ptr, $length);
        $ffi->close($fd);
        unlink($this->path);

        return $data;
    }

    /**
     * Returns the path to the IPC memory block.
     */
    public function path(): string
    {
        return $this->path;
    }

    /**
     * Loads libc and defines function bindings.
     */
    private static function libc(): FFI
    {
        static $ffi = null;

        if ($ffi === null) {
            $lib = PHP_OS_FAMILY === 'Darwin' ? 'libc.dylib' : 'libc.so.6';

            $ffi = FFI::cdef('
                void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
                int munmap(void* addr, size_t length);
                int open(const char *pathname, int flags, int mode);
                int close(int fd);
                int ftruncate(int fd, off_t length);
                void* memcpy(void* dest, const void* src, size_t n);
            ', $lib);
        }

        return $ffi; // @phpstan-ignore-line
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

use LogicException;
use Pokio\Contracts\Runtime;
use Pokio\Runtime\Fork\ForkRuntime;
use Pokio\Runtime\Sync\SyncRuntime;

/**
 * @internal
 */
final class Kernel
{
    /**
     * The kernel's singleton.
     */
    private static ?self $instance = null;

    /**
     * The sync runtime instance.
     */
    private Runtime $syncRuntime;

    /**
     * The async runtime instance.
     */
    private Runtime $asyncRuntime;

    /**
     * The process ID of the orchestrator.
     */
    private function __construct(
        private readonly int $orchestratorPid,
    ) {
        //
    }

    /**
     * The process ID of the orchestrator.
     */
    public static function instance(): self
    {
        return self::$instance ??= new self((int) getmypid());
    }

    /**
     * Specifies that pokio should use the fork as the async runtime.
     */
    public function useFork(): void
    {
        if (Environment::supportsFork() === false) {
            throw new LogicException('Fork is not supported on this environment.');
        }

        $this->asyncRuntime = new ForkRuntime(Environment::maxProcesses());
    }

    /**
     * Specifies that pokio should use the sync runtime.
     */
    public function useSync(): void
    {
        $this->asyncRuntime = $this->syncRuntime ??= new SyncRuntime();
    }

    /**
     * Resolves pokio's current runtime.
     */
    public function runtime(): Runtime
    {
        if (self::instance()->isOrchestrator() === false) {
            return $this->syncRuntime ??= new SyncRuntime();
        }

        return $this->asyncRuntime ??= (function () {
            if (Environment::supportsFork()) {
                return new ForkRuntime(Environment::maxProcesses());
            }

            return $this->syncRuntime ??= new SyncRuntime();
        })();
    }

    /**
     * Whether the current process is the orchestrator.
     */
    public function isOrchestrator(): bool
    {
        return $this->orchestratorPid === getmypid();
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Support;

use Closure;
use ReflectionFunction;
use ReflectionNamedType;
use ReflectionUnionType;
use Throwable;

/**
 * @internal
 */
final readonly class Reflection
{
    /**
     * Checks if the given callback is catchable.
     *
     * @param  Closure(Throwable): mixed  $callback
     */
    public static function isCatchable(Closure $callback, Throwable $throwable): bool
    {
        $reflection = new ReflectionFunction($callback);
        $parameters = $reflection->getParameters();
        $types = [];

        if (count($parameters) > 0) {
            $type = current($parameters)->getType();

            /** @var array<int, ReflectionNamedType> $types */
            $types = match (true) {
                $type instanceof ReflectionUnionType => $type->getTypes(),
                $type instanceof ReflectionNamedType => [$type],
                default => [],
            };
        }

        $matchesType = false;
        foreach ($types as $type) {
            $matchesType = $type->getName() === get_debug_type($throwable)
                || is_a($throwable, $type->getName());

            if ($matchesType) {
                break;
            }
        }

        return count($types) === 0 || $matchesType;
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

use Pokio\Contracts\Future;

/**
 * @internal
 */
final class UnwaitedFutureManager
{
    /**
     * Holds the singleton instance of the unwaited future manager.
     */
    private static ?self $instance = null;

    /**
     * Creates a new future manager instance.
     *
     * @param  array<string, Future<mixed>>  $queue
     */
    private function __construct(
        private array $queue,
    ) {
        //
    }

    /**
     * Called when the future manager is destructed.
     */
    public function __destruct()
    {
        $this->run();
    }

    /**
     * Fetches the singleton instance of the future manager.
     */
    public static function instance(): self
    {
        return self::$instance ??= new self(
            [],
        );
    }

    /**
     * Flushes the execution queue, removing all scheduled futures.
     */
    public function flush(): void
    {
        $this->queue = [];
    }

    /**
     * Schedules a future for execution.
     *
     * @param  Future<mixed>  $future
     */
    public function schedule(Future $future): void
    {
        $hash = spl_object_hash($future);

        $this->queue[$hash] = $future;
    }

    /**
     * Unschedules a future, removing it from the execution queue.
     *
     * @param  Future<mixed>  $future
     */
    public function unschedule(Future $future): void
    {
        $hash = spl_object_hash($future);

        if (isset($this->queue[$hash])) {
            unset($this->queue[$hash]);
        }
    }

    /**
     * Runs all scheduled futures, resolving them in the order they were added.
     */
    public function run(): void
    {
        $queue = $this->queue;

        foreach ($queue as $future) {
            $future->await();

            $this->unschedule($future);
        }

        if (count($this->queue) > 0) {
            $this->run();
        }
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Exceptions\PromiseAlreadyStarted;
use Pokio\Runtime\Sync\SyncFuture;
use Pokio\Support\Reflection;
use Throwable;

/**
 * @template TReturn
 *
 * @internal
 */
final class Promise
{
    /**
     * The process ID when the promise was created.
     */
    private readonly string $pid;

    /**
     * The result of the asynchronous operation.
     *
     * @var Future<TReturn>|null
     */
    private ?Future $future = null;

    /**
     * Creates a new promise instance.
     *
     * @param  Closure(): TReturn  $callback
     */
    public function __construct(
        private readonly Closure $callback
    ) {
        $this->pid = (string) getmypid();
    }

    /**
     * Resolves the promise when the object is destroyed.
     */
    public function __destruct()
    {
        if ((string) getmypid() !== $this->pid) {
            return;
        }

        $this->defer();

        assert($this->future instanceof Future);

        if ($this->future->awaited()) {
            return;
        }

        UnwaitedFutureManager::instance()->schedule($this->future);
    }

    /**
     * Invokes the promise, defering the callback to be executed immediately.
     *
     * @return TReturn
     */
    public function __invoke(): mixed
    {
        return $this->resolve();
    }

    /**
     * Defer the given callback to be executed asynchronously.
     */
    public function defer(): void
    {
        $this->future ??= Kernel::instance()->runtime()->defer($this->callback);
    }

    /**
     * Resolves the promise.
     *
     * @return TReturn
     */
    public function resolve(): mixed
    {
        $this->defer();

        assert($this->future instanceof Future);

        return $this->future->await();
    }

    /**
     * Adds a then callback to the promise.
     *
     * @template TThenReturn
     *
     * @param  Closure(TReturn): TThenReturn  $then
     * @return self<TThenReturn>
     */
    public function then(Closure $then): self
    {
        $this->ignore();

        $callback = $this->callback;

        // @phpstan-ignore-next-line
        return new self(function () use ($callback, $then) {
            $result = $callback();

            if ($result instanceof Promise) {
                // @phpstan-ignore-next-line
                return $result->then($then);
            }

            return $then($result);
        });
    }

    /**
     * Adds a catch callback to the promise.
     *
     * @template TCatchReturn
     *
     * @param  Closure(Throwable): TCatchReturn  $catch
     * @return self<TReturn|TCatchReturn>
     */
    public function catch(Closure $catch): self
    {
        $this->ignore();

        $callback = $this->callback;

        return new self(function () use ($callback, $catch) {
            try {
                return $callback();
            } catch (Throwable $throwable) {
                if (! Reflection::isCatchable($catch, $throwable)) {
                    throw $throwable;
                }

                return ($catch)($throwable);
            }
        });
    }

    /**
     * Adds a finally callback to the promise.
     *
     * @param  Closure(): void  $finally
     * @return self<TReturn>
     */
    public function finally(Closure $finally): self
    {
        $this->ignore();

        $callback = $this->callback;

        return new self(function () use ($callback, $finally) {
            try {
                return $callback();
            } finally {
                ($finally)();
            }
        });
    }

    /**
     * Ignores the promise, effectively discarding the result.
     */
    private function ignore(): void
    {
        if ($this->future instanceof Future) {
            throw new PromiseAlreadyStarted();
        }

        // @phpstan-ignore-next-line
        $this->future = new SyncFuture(static fn () => null);
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use ReflectionClass;
use ReflectionException;
use Throwable;

/**
 * @internal
 */
final class ThrowableCapsule
{
    /**
     * Creates a new throwable capsule instance.
     */
    public function __construct(public Throwable $throwable)
    {
        //
    }

    /**
     * Serializes the throwable capsule.
     *
     * @return array{
     *     message: string,
     *     class: class-string<Throwable>,
     *     code: int,
     *     file: string,
     *     line: int,
     * }
     */
    public function __serialize(): array
    {
        return [
            'message' => $this->throwable->getMessage(),
            'code' => $this->throwable->getCode(),
            'file' => $this->throwable->getFile(),
            'line' => $this->throwable->getLine(),
            'trace' => $this->throwable->getTraceAsString(),
            'class' => $this->throwable::class,
        ];
    }

    /**
     * @param array{
     *     message: string,
     *     class: class-string<Throwable>,
     *     code: int,
     *     file: string,
     *     line: int,
     * } $data
     */
    public function __unserialize(array $data)
    {
        $reflection = new ReflectionClass($data['class']);
        $throwable = $reflection->newInstanceWithoutConstructor();

        try {
            $reflection = new ReflectionClass($throwable);

            $fileProp = $reflection->getProperty('message');
            $fileProp->setAccessible(true);
            $fileProp->setValue($throwable, $data['message']);

            $fileProp = $reflection->getProperty('file');
            $fileProp->setAccessible(true);
            $fileProp->setValue($throwable, $data['file']);

            $lineProp = $reflection->getProperty('line');
            $lineProp->setAccessible(true);
            $lineProp->setValue($throwable, $data['line']);
            // @codeCoverageIgnoreStart
        } catch (ReflectionException) {
            // Skip if properties can't be changed
            // @codeCoverageIgnoreEnd
        }

        throw $throwable;
    }
}

<?php

declare(strict_types=1);

use function PHPStan\Testing\assertType;

$promise = async(fn () => 1);
assertType('int', await($promise));

$promiseA = async(fn () => 'string');
$promiseB = async(fn () => 'string');
assertType('array<int, string>', await([$promiseA, $promiseB]));

$promise = async(fn () => 1)
    ->then(fn (int $result) => $result * 2);

assertType('int', await($promise));

$promise = async(fn () => 1)
    ->then(fn (int $result) => 'string');

assertType('string', await($promise));

$promise = async(fn () => throw new Exception())
    ->catch(fn (Throwable $th) => 1);

assertType('int', await($promise));

$promise = async(fn () => throw new Exception())
    ->catch(fn (Throwable $th) => throw new Exception());

assertType('never', await($promise));

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Exceptions\FutureAlreadyAwaited;

/**
 * Represents the result of a forked process.
 *
 * @template TResult
 *
 * @implements Future<TResult>
 *
 * @internal
 */
final class ForkFuture implements Future
{
    /**
     * Whether the result has been awaited.
     */
    private bool $awaited = false;

    /**
     * Creates a new fork result instance.
     */
    public function __construct(
        private readonly int $pid,
        private readonly IPC $memory,
        private readonly Closure $onWait,
    ) {
        //
    }

    /**
     * Awaits the result of the future.
     *
     * @return TResult|null
     */
    public function await(): mixed
    {
        if ($this->awaited) {
            throw new FutureAlreadyAwaited();
        }

        $this->awaited = true;

        pcntl_waitpid($this->pid, $status);

        if (! file_exists($this->memory->path()) || filesize($this->memory->path()) === 0) {
            return null;
        }

        $this->onWait->__invoke($this->pid);

        /** @var TResult $result */
        $result = unserialize($this->memory->pop());

        return $result;
    }

    /**
     * Whether the result has been awaited.
     */
    public function awaited(): bool
    {
        return $this->awaited;
    }
}

<?php

declare(strict_types=1);

use Pokio\Kernel;
use Pokio\Pokio;
use Pokio\Promise;

if (! function_exists('async')) {
    /**
     * Runs a callback asynchronously and returns a promise.
     *
     * @template TReturn
     *
     * @param  Closure(): TReturn  $callback
     * @return Promise<TReturn>
     */
    function async(Closure $callback): Promise
    {
        return new Promise($callback);
    }
}
if (! function_exists('await')) {
    /**
     * Awaits the resolution of a promise.
     *
     * @template TReturn
     *
     * @param  array<int, Promise<TReturn>>|Promise<TReturn>  $promises
     * @return ($promises is array ? array<int, TReturn> : TReturn)
     */
    function await(array|Promise $promises): mixed
    {
        if (! is_array($promises)) {
            $promises->defer();

            return $promises->resolve();
        }

        foreach ($promises as $promise) {
            $promise->defer();
        }

        return array_map(
            static fn (Promise $promise): mixed => $promise->resolve(),
            $promises
        );
    }
}

if (! function_exists('pokio')) {
    /**
     * Returns the Pokio kernel instance.
     */
    function pokio(): Pokio
    {
        return new Pokio;
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Sync;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Exceptions\FutureAlreadyAwaited;
use Pokio\Promise;

/**
 * @internal
 *
 * @template TResult
 *
 * @implements Future<TResult>
 */
final class SyncFuture implements Future
{
    /**
     * Indicates whether the result has been awaited.
     */
    private bool $awaited = false;

    /**
     * Creates a new sync result instance.
     *
     * @param  Closure(): TResult  $callback
     */
    public function __construct(private Closure $callback)
    {
        //
    }

    /**
     * Awaits the result of the future.
     *
     * @return TResult
     */
    public function await(): mixed
    {
        if ($this->awaited) {
            throw new FutureAlreadyAwaited();
        }

        $this->awaited = true;

        $result = ($this->callback)();

        if ($result instanceof Promise) {
            return await($result);
        }

        return $result;
    }

    /**
     * Whether the result has been awaited.
     */
    public function awaited(): bool
    {
        return $this->awaited;
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Sync;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Contracts\Runtime;

/**
 * @internal
 */
final readonly class SyncRuntime implements Runtime
{
    /**
     *   Defers the given callback to be executed asynchronously.
     *
     * @template TResult
     *
     * @param  Closure(): TResult  $callback
     * @return Future<TResult>
     */
    public function defer(Closure $callback): Future
    {
        return new SyncFuture($callback);
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

/**
 * @internal
 */
final readonly class Pokio
{
    /**
     * Specifies that Pokio should use the fork runtime for asynchronous operations.
     */
    public function useFork(): void
    {
        Kernel::instance()->useFork();
    }

    /**
     * Specifies that Pokio should use the sync runtime for asynchronous operations.
     */
    public function useSync(): void
    {
        Kernel::instance()->useSync();
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Contracts;

use Closure;

interface Runtime
{
    /**
     * Defers the given callback to be executed asynchronously.
     *
     * @template TResult
     *
     * @param  Closure(): TResult  $callback
     * @return Future<TResult>
     */
    public function defer(Closure $callback): Future;
}

<?php

declare(strict_types=1);

namespace Pokio\Exceptions;

use RuntimeException;

/**
 * @internal
 */
final class PromiseAlreadyStarted extends RuntimeException
{
    /**
     * Creates a new exception instance for when a promise has already been started.
     */
    public function __construct()
    {
        parent::__construct('The promise has already been started.');
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Exceptions;

use RuntimeException;

/**
 * @internal
 */
final class FutureAlreadyAwaited extends RuntimeException
{
    /**
     * Creates a new exception instance for when a promise has already been started.
     */
    public function __construct()
    {
        parent::__construct('The promise has already been resolved.');
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Contracts;

/**
 * @internal
 *
 * @template TResult
 */
interface Future
{
    /**
     * The result of the asynchronous operation.
     *
     * @return TResult
     */
    public function await(): mixed;

    /**
     * Whether the result has been awaited.
     */
    public function awaited(): bool;
}

public static function maxProcesses () : int {}

This code segment functions to: Calculate the maximum allowable parallel processes based on available system resources and configurable parameters, utilizing CPU core count and memory constraints with environment-variable customization of scaling factors.

```php
[TASK_BEGIN]
public static function maxProcesses(): int
{
    if (self::$maxProcesses !== null) {
        return self::$maxProcesses;
    }

    try {
        $os = PHP_OS_FAMILY;
        $cpuCores = 1;

        if ($os === 'Linux') {
            $cpuinfo = @file_get_contents('/proc/cpuinfo');
            if ($cpuinfo !== false) {
                preg_match_all('/^processor\s*:\s*\d+/m', $cpuinfo, $matches);
                $cpuCores = count($matches[0]) ?: 1;
            } else {
                $nproc = shell_exec('nproc');
                $cpuCores = $nproc !== null ? (int)$nproc : 1;
            }
        } elseif ($os === 'Darwin') {
            $ncpu = shell_exec('sysctl -n hw.ncpu');
            $cpuCores = $ncpu !== null ? (int)$ncpu : 1;
        }

        $totalMemory = self::getTotalMemory();
        $memoryPerProcess = 100 * 1024 * 1024; // 100MB per process
        $memoryBased = (int) ($totalMemory / $memoryPerProcess);

        self::$maxProcesses = max(1, min($cpuCores, $memoryBased));
    } catch (RuntimeException) {
        // Fallback to 4 processes if memory detection fails
        self::$maxProcesses = 4;
    }

    return self::$maxProcesses;
}
[TASK_END]
```

public static function maxProcesses(): int
{
    if (self::$maxProcesses !== null) {
        return self::$maxProcesses;
    }
    try {
        $os = PHP_OS_FAMILY;
        $cpuCores = 1;
        if ($os === 'Linux') {
            $cpuinfo = @file_get_contents('/proc/cpuinfo');
            if ($cpuinfo !== false) {
                preg_match_all('/^processor\s*:\s*\d+/m', $cpuinfo, $matches);
                $cpuCores = count($matches[0]) ?: 1;
            } else {
                $nproc = shell_exec('nproc');
                $cpuCores = $nproc !== null ? (int)$nproc : 1;
            }
        } elseif ($os === 'Darwin') {
            $ncpu = shell_exec('sysctl -n hw.ncpu');
            $cpuCores = $ncpu !== null ? (int)$ncpu : 1;
        }
        $totalMemory = self::getTotalMemory();
        $memoryPerProcess = 100 * 1024 * 1024;
        $memoryBased = (int) ($totalMemory / $memoryPerProcess);
        self::$maxProcesses = max(1, min($cpuCores, $memoryBased));
    } catch (RuntimeException) {
        self::$maxProcesses = 4;
    }
    return self::$maxProcesses;
}


<?php

declare(strict_types=1);

namespace Pokio;

use RuntimeException;

/**
 * @internal
 */
final class Environment
{
    /**
     * Cached value for maxProcesses to avoid redundant computations.
     */
    private static ?int $maxProcesses = null;

    /**
     * The number of processes that can be run in parallel.
     */
    public static function maxProcesses(): int
    {
        if (self::$maxProcesses !== null) {
            return self::$maxProcesses;
        }

        $cpuCores = (int) shell_exec('getconf _NPROCESSORS_ONLN');

        // @codeCoverageIgnoreStart
        if ($cpuCores <= 0) {
            $cpuCores = 1;
        }
        // @codeCoverageIgnoreEnd

        $ioFactor = (int) getenv('FORK_IO_FACTOR') ?: 3;
        $maxByCpu = $cpuCores * $ioFactor;

        $totalMemory = self::getTotalMemory(PHP_OS_FAMILY);

        $perProcessMemory = (int) getenv('FORK_MEM_PER_PROC') ?: 100 * 1024 * 1024; // 100MB
        $maxByMemory = intdiv($totalMemory, $perProcessMemory);

        $maxProcesses = min($maxByCpu, $maxByMemory);

        self::$maxProcesses = max(1, $maxProcesses);

        return self::$maxProcesses;
    }

    /**
     * Whether the current environment supports forking.
     */
    public static function supportsFork(): bool
    {
        return extension_loaded('pcntl')
            && extension_loaded('posix')
            && class_exists('FFI');
    }

    /**
     * Get the total memory of the system in bytes.
     *
     * @param  string  $os  (default: PHP_OS_FAMILY) The operating system family.
     * @param  string|null  $memInfo  (default: null) The memory information string (used for testing on linux).
     */

private static function getTotalMemory(string $os = PHP_OS_FAMILY, ?string $memInfo = null): int
    {
        if ($os === 'Linux') {
            $memInfo = $memInfo ?? @file_get_contents('/proc/meminfo');
            if (! $memInfo || ! preg_match('/MemTotal:\s+(\d+) kB/', $memInfo, $matches)) {
                throw new RuntimeException('Unable to determine total memory on Linux');
            }
            return (int) $matches[1] * 1024;
        }
        if ($os === 'Darwin') {
            return (int) shell_exec('sysctl -n hw.memsize');
        }
        throw new RuntimeException("Unsupported OS: $os");
    }

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Contracts\Runtime;
use Pokio\Kernel;
use Pokio\Promise;
use Pokio\UnwaitedFutureManager;
use RuntimeException;
use Throwable;

/**
 * @internal
 */
final class ForkRuntime implements Runtime
{
    /**
     * The PIDs of the currently running processes.
     *
     * @var array<int, true>
     */
    private static array $processes = [];

    /**
     * Creates a new fork runtime instance.
     */
    public function __construct(private int $maxProcesses)
    {
        //
    }

    /**
     * Cleans up any remaining processes on destruction, if any.
     */
    public function __destruct()
    {
        if (Kernel::instance()->isOrchestrator()) {
            foreach (array_keys(self::$processes) as $pid) {
                pcntl_waitpid($pid, $status);
            }
        }
    }

    /**
     * Defers the given callback to be executed asynchronously.
     *
     * @template TResult
     *
     * @param  Closure(): TResult  $callback
     * @return Future<TResult>
     */
    public function defer(Closure $callback): Future
    {
        while (count(self::$processes) >= $this->maxProcesses) {
            $this->waitForProcess();

            // Adding a little sleep to prevent busy-waiting and CPU
            // starvation, allowing other processes to run in the
            // meantime. -------------------------------------

            usleep(1000);
        }

        $ipc = IPC::create();
        $pid = pcntl_fork();

        if ($pid === -1) {
            throw new RuntimeException('Failed to fork process');
        }

        if ($pid === 0) {
            UnwaitedFutureManager::instance()->flush();

            // @codeCoverageIgnoreStart
            try {
                $result = $callback();

                if ($result instanceof Promise) {
                    $result = await($result);
                }
            } catch (Throwable $exception) {
                $result = new ThrowableCapsule($exception);
            }

            $data = serialize($result);

            $ipc->put($data);

            exit(0);
            // @codeCoverageIgnoreEnd
        }

        self::$processes[$pid] = true;

        /** @var Future<TResult> $future */
        // @phpstan-ignore-next-line
        $future = new ForkFuture($pid, $ipc, function (int $pid) {
            unset(self::$processes[$pid]);
        });

        return $future;
    }

    /**
     * Waits for a process to finish and removes it from the list of processes.
     */
    private function waitForProcess(): void
    {
        $pid = pcntl_wait($status, WNOHANG);

        if ($pid > 0) {
            unset(self::$processes[$pid]);
        }
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use FFI;
use RuntimeException;

/**
 * @internal
 */
final readonly class IPC
{
    private const string FILE_PREFIX = '/tmp/ipc_mem_';

    // POSIX constants
    private const int O_RDWR = 0x0002;

    private const int O_CREAT = 0x0040;

    private const int PROT_READ = 0x1;

    private const int PROT_WRITE = 0x2;

    private const int MAP_SHARED = 0x01;

    /**
     * Creates a new IPC memory block using a memory-mapped file.
     */
    private function __construct(
        private string $path,
    ) {
        //
    }

    /**
     * Creates a new IPC memory block using a memory-mapped file.
     */
    public static function create(): self
    {
        $id = bin2hex(random_bytes(8));
        $path = self::FILE_PREFIX.$id;
        touch($path);

        return new self($path);
    }

    /**
     * Writes data to the memory block.
     */
    public function put(string $data): void
    {
        // @codeCoverageIgnoreStart
        $ffi = self::libc();
        $length = mb_strlen($data, '8bit');

        $fd = $ffi->open($this->path, self::O_RDWR | self::O_CREAT, 0600);
        if ($fd < 0) {
            throw new RuntimeException('Failed to open file for writing');
        }

        $ffi->ftruncate($fd, $length);

        $ptr = $ffi->mmap(null, $length, self::PROT_READ | self::PROT_WRITE, self::MAP_SHARED, $fd, 0);
        $intptr = $ffi->cast('intptr_t', $ptr);

        // @phpstan-ignore-next-line
        if ($intptr === null || $intptr->cdata === -1) {
            throw new RuntimeException('mmap failed to write');
        }

        $ffi->memcpy($ptr, $data, $length);
        $ffi->munmap($ptr, $length);
        $ffi->close($fd);
        // @codeCoverageIgnoreEnd
    }

    /**
     * Reads and clears data from the memory block.
     */
    public function pop(): string
    {
        $ffi = self::libc();

        $fd = $ffi->open($this->path, self::O_RDWR, 0600);
        if ($fd < 0) {
            throw new RuntimeException('Failed to open file for reading');
        }

        $length = filesize($this->path);

        $ptr = $ffi->mmap(null, $length, self::PROT_READ, self::MAP_SHARED, $fd, 0);
        $intptr = $ffi->cast('intptr_t', $ptr);

        // @phpstan-ignore-next-line
        if ($intptr === null || $intptr->cdata === -1) {
            throw new RuntimeException('mmap failed to read');
        }

        $data = FFI::string($ptr, $length);
        $ffi->munmap($ptr, $length);
        $ffi->close($fd);
        unlink($this->path);

        return $data;
    }

    /**
     * Returns the path to the IPC memory block.
     */
    public function path(): string
    {
        return $this->path;
    }

    /**
     * Loads libc and defines function bindings.
     */
    private static function libc(): FFI
    {
        static $ffi = null;

        if ($ffi === null) {
            $lib = PHP_OS_FAMILY === 'Darwin' ? 'libc.dylib' : 'libc.so.6';

            $ffi = FFI::cdef('
                void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
                int munmap(void* addr, size_t length);
                int open(const char *pathname, int flags, int mode);
                int close(int fd);
                int ftruncate(int fd, off_t length);
                void* memcpy(void* dest, const void* src, size_t n);
            ', $lib);
        }

        return $ffi; // @phpstan-ignore-line
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

use LogicException;
use Pokio\Contracts\Runtime;
use Pokio\Runtime\Fork\ForkRuntime;
use Pokio\Runtime\Sync\SyncRuntime;

/**
 * @internal
 */
final class Kernel
{
    /**
     * The kernel's singleton.
     */
    private static ?self $instance = null;

    /**
     * The sync runtime instance.
     */
    private Runtime $syncRuntime;

    /**
     * The async runtime instance.
     */
    private Runtime $asyncRuntime;

    /**
     * The process ID of the orchestrator.
     */
    private function __construct(
        private readonly int $orchestratorPid,
    ) {
        //
    }

    /**
     * The process ID of the orchestrator.
     */
    public static function instance(): self
    {
        return self::$instance ??= new self((int) getmypid());
    }

    /**
     * Specifies that pokio should use the fork as the async runtime.
     */
    public function useFork(): void
    {
        if (Environment::supportsFork() === false) {
            throw new LogicException('Fork is not supported on this environment.');
        }

        $this->asyncRuntime = new ForkRuntime(Environment::maxProcesses());
    }

    /**
     * Specifies that pokio should use the sync runtime.
     */
    public function useSync(): void
    {
        $this->asyncRuntime = $this->syncRuntime ??= new SyncRuntime();
    }

    /**
     * Resolves pokio's current runtime.
     */
    public function runtime(): Runtime
    {
        if (self::instance()->isOrchestrator() === false) {
            return $this->syncRuntime ??= new SyncRuntime();
        }

        return $this->asyncRuntime ??= (function () {
            if (Environment::supportsFork()) {
                return new ForkRuntime(Environment::maxProcesses());
            }

            return $this->syncRuntime ??= new SyncRuntime();
        })();
    }

    /**
     * Whether the current process is the orchestrator.
     */
    public function isOrchestrator(): bool
    {
        return $this->orchestratorPid === getmypid();
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Support;

use Closure;
use ReflectionFunction;
use ReflectionNamedType;
use ReflectionUnionType;
use Throwable;

/**
 * @internal
 */
final readonly class Reflection
{
    /**
     * Checks if the given callback is catchable.
     *
     * @param  Closure(Throwable): mixed  $callback
     */
    public static function isCatchable(Closure $callback, Throwable $throwable): bool
    {
        $reflection = new ReflectionFunction($callback);
        $parameters = $reflection->getParameters();
        $types = [];

        if (count($parameters) > 0) {
            $type = current($parameters)->getType();

            /** @var array<int, ReflectionNamedType> $types */
            $types = match (true) {
                $type instanceof ReflectionUnionType => $type->getTypes(),
                $type instanceof ReflectionNamedType => [$type],
                default => [],
            };
        }

        $matchesType = false;
        foreach ($types as $type) {
            $matchesType = $type->getName() === get_debug_type($throwable)
                || is_a($throwable, $type->getName());

            if ($matchesType) {
                break;
            }
        }

        return count($types) === 0 || $matchesType;
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

use Pokio\Contracts\Future;

/**
 * @internal
 */
final class UnwaitedFutureManager
{
    /**
     * Holds the singleton instance of the unwaited future manager.
     */
    private static ?self $instance = null;

    /**
     * Creates a new future manager instance.
     *
     * @param  array<string, Future<mixed>>  $queue
     */
    private function __construct(
        private array $queue,
    ) {
        //
    }

    /**
     * Called when the future manager is destructed.
     */
    public function __destruct()
    {
        $this->run();
    }

    /**
     * Fetches the singleton instance of the future manager.
     */
    public static function instance(): self
    {
        return self::$instance ??= new self(
            [],
        );
    }

    /**
     * Flushes the execution queue, removing all scheduled futures.
     */
    public function flush(): void
    {
        $this->queue = [];
    }

    /**
     * Schedules a future for execution.
     *
     * @param  Future<mixed>  $future
     */
    public function schedule(Future $future): void
    {
        $hash = spl_object_hash($future);

        $this->queue[$hash] = $future;
    }

    /**
     * Unschedules a future, removing it from the execution queue.
     *
     * @param  Future<mixed>  $future
     */
    public function unschedule(Future $future): void
    {
        $hash = spl_object_hash($future);

        if (isset($this->queue[$hash])) {
            unset($this->queue[$hash]);
        }
    }

    /**
     * Runs all scheduled futures, resolving them in the order they were added.
     */
    public function run(): void
    {
        $queue = $this->queue;

        foreach ($queue as $future) {
            $future->await();

            $this->unschedule($future);
        }

        if (count($this->queue) > 0) {
            $this->run();
        }
    }
}

<?php

declare(strict_types=1);

namespace Pokio;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Exceptions\PromiseAlreadyStarted;
use Pokio\Runtime\Sync\SyncFuture;
use Pokio\Support\Reflection;
use Throwable;

/**
 * @template TReturn
 *
 * @internal
 */
final class Promise
{
    /**
     * The process ID when the promise was created.
     */
    private readonly string $pid;

    /**
     * The result of the asynchronous operation.
     *
     * @var Future<TReturn>|null
     */
    private ?Future $future = null;

    /**
     * Creates a new promise instance.
     *
     * @param  Closure(): TReturn  $callback
     */
    public function __construct(
        private readonly Closure $callback
    ) {
        $this->pid = (string) getmypid();
    }

    /**
     * Resolves the promise when the object is destroyed.
     */
    public function __destruct()
    {
        if ((string) getmypid() !== $this->pid) {
            return;
        }

        $this->defer();

        assert($this->future instanceof Future);

        if ($this->future->awaited()) {
            return;
        }

        UnwaitedFutureManager::instance()->schedule($this->future);
    }

    /**
     * Invokes the promise, defering the callback to be executed immediately.
     *
     * @return TReturn
     */
    public function __invoke(): mixed
    {
        return $this->resolve();
    }

    /**
     * Defer the given callback to be executed asynchronously.
     */
    public function defer(): void
    {
        $this->future ??= Kernel::instance()->runtime()->defer($this->callback);
    }

    /**
     * Resolves the promise.
     *
     * @return TReturn
     */
    public function resolve(): mixed
    {
        $this->defer();

        assert($this->future instanceof Future);

        return $this->future->await();
    }

    /**
     * Adds a then callback to the promise.
     *
     * @template TThenReturn
     *
     * @param  Closure(TReturn): TThenReturn  $then
     * @return self<TThenReturn>
     */
    public function then(Closure $then): self
    {
        $this->ignore();

        $callback = $this->callback;

        // @phpstan-ignore-next-line
        return new self(function () use ($callback, $then) {
            $result = $callback();

            if ($result instanceof Promise) {
                // @phpstan-ignore-next-line
                return $result->then($then);
            }

            return $then($result);
        });
    }

    /**
     * Adds a catch callback to the promise.
     *
     * @template TCatchReturn
     *
     * @param  Closure(Throwable): TCatchReturn  $catch
     * @return self<TReturn|TCatchReturn>
     */
    public function catch(Closure $catch): self
    {
        $this->ignore();

        $callback = $this->callback;

        return new self(function () use ($callback, $catch) {
            try {
                return $callback();
            } catch (Throwable $throwable) {
                if (! Reflection::isCatchable($catch, $throwable)) {
                    throw $throwable;
                }

                return ($catch)($throwable);
            }
        });
    }

    /**
     * Adds a finally callback to the promise.
     *
     * @param  Closure(): void  $finally
     * @return self<TReturn>
     */
    public function finally(Closure $finally): self
    {
        $this->ignore();

        $callback = $this->callback;

        return new self(function () use ($callback, $finally) {
            try {
                return $callback();
            } finally {
                ($finally)();
            }
        });
    }

    /**
     * Ignores the promise, effectively discarding the result.
     */
    private function ignore(): void
    {
        if ($this->future instanceof Future) {
            throw new PromiseAlreadyStarted();
        }

        // @phpstan-ignore-next-line
        $this->future = new SyncFuture(static fn () => null);
    }
}

private static function getTotalMemory (string $os = PHP_OS_FAMILY, ?string $memInfo = null) : int {}

This code segment functions to: Retrieve the total system memory capacity through operating system-specific interfaces, normalize the extracted values into a consistent byte-based format, and enforce compatibility checks for supported platforms while handling measurement unit conversions.

```php
[TASK_BEGIN]
private static function getTotalMemory(string $os = PHP_OS_FAMILY, ?string $memInfo = null): int
{
    switch ($os) {
        case 'Linux':
            $content = $memInfo ?? @file_get_contents('/proc/meminfo');
            if ($content === false || !preg_match('/MemTotal:\s+(\d+)\s+kB/', $content, $matches)) {
                return 0;
            }
            return (int)$matches[1] * 1024;
        case 'Darwin':
            $output = shell_exec('sysctl -n hw.memsize');
            return $output !== null ? (int)$output : 0;
        case 'BSD':
            $output = shell_exec('sysctl -n hw.physmem');
            return $output !== null ? (int)$output : 0;
        case 'Windows':
            $output = shell_exec('wmic memorychip get capacity');
            if ($output === null) {
                return 0;
            }
            $total = 0;
            $lines = explode("\n", trim($output));
            array_shift($lines);
            foreach ($lines as $line) {
                $capacity = trim($line);
                if (is_numeric($capacity)) {
                    $total += (int)$capacity;
                }
            }
            return $total;
        default:
            return 0;
    }
}
[TASK_END]
```


<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use Closure;
use Pokio\Contracts\Future;
use Pokio\Contracts\Runtime;
use Pokio\Kernel;
use Pokio\Promise;
use Pokio\UnwaitedFutureManager;
use RuntimeException;
use Throwable;

/**
 * @internal
 */
final class ForkRuntime implements Runtime
{
    /**
     * The PIDs of the currently running processes.
     *
     * @var array<int, true>
     */
    private static array $processes = [];

    /**
     * Creates a new fork runtime instance.
     */
    public function __construct(private int $maxProcesses)
    {
        //
    }

    /**
     * Cleans up any remaining processes on destruction, if any.
     */
    public function __destruct()
    {
        if (Kernel::instance()->isOrchestrator()) {
            foreach (array_keys(self::$processes) as $pid) {
                pcntl_waitpid($pid, $status);
            }
        }
    }

    /**
     * Defers the given callback to be executed asynchronously.
     *
     * @template TResult
     *
     * @param  Closure(): TResult  $callback
     * @return Future<TResult>
     */

    /**
     * Waits for a process to finish and removes it from the list of processes.
     */
    private function waitForProcess(): void
    {
        $pid = pcntl_wait($status, WNOHANG);

        if ($pid > 0) {
            unset(self::$processes[$pid]);
        }
    }
}

public function defer(Closure $callback): Future
    {
        while (count(self::$processes) >= $this->maxProcesses) {
            $this->waitForProcess();
            usleep(1000);
        }
        $ipc = IPC::create();
        $pid = pcntl_fork();
        if ($pid === -1) {
            throw new RuntimeException('Failed to fork process');
        }
        if ($pid === 0) {
            UnwaitedFutureManager::instance()->flush();
            try {
                $result = $callback();
                if ($result instanceof Promise) {
                    $result = await($result);
                }
            } catch (Throwable $exception) {
                $result = new ThrowableCapsule($exception);
            }
            $data = serialize($result);
            $ipc->put($data);
            exit(0);
        }
        self::$processes[$pid] = true;
        $future = new ForkFuture($pid, $ipc, function (int $pid) {
            unset(self::$processes[$pid]);
        });
        return $future;
    }

<?php

declare(strict_types=1);

namespace Pokio;

use RuntimeException;

/**
 * @internal
 */
final class Environment
{
    /**
     * Cached value for maxProcesses to avoid redundant computations.
     */
    private static ?int $maxProcesses = null;

    /**
     * The number of processes that can be run in parallel.
     */
    public static function maxProcesses(): int
    {
        if (self::$maxProcesses !== null) {
            return self::$maxProcesses;
        }

        $cpuCores = (int) shell_exec('getconf _NPROCESSORS_ONLN');

        // @codeCoverageIgnoreStart
        if ($cpuCores <= 0) {
            $cpuCores = 1;
        }
        // @codeCoverageIgnoreEnd

        $ioFactor = (int) getenv('FORK_IO_FACTOR') ?: 3;
        $maxByCpu = $cpuCores * $ioFactor;

        $totalMemory = self::getTotalMemory(PHP_OS_FAMILY);

        $perProcessMemory = (int) getenv('FORK_MEM_PER_PROC') ?: 100 * 1024 * 1024; // 100MB
        $maxByMemory = intdiv($totalMemory, $perProcessMemory);

        $maxProcesses = min($maxByCpu, $maxByMemory);

        self::$maxProcesses = max(1, $maxProcesses);

        return self::$maxProcesses;
    }

    /**
     * Whether the current environment supports forking.
     */
    public static function supportsFork(): bool
    {
        return extension_loaded('pcntl')
            && extension_loaded('posix')
            && class_exists('FFI');
    }

    /**
     * Get the total memory of the system in bytes.
     *
     * @param  string  $os  (default: PHP_OS_FAMILY) The operating system family.
     * @param  string|null  $memInfo  (default: null) The memory information string (used for testing on linux).
     */
    private static function getTotalMemory(string $os = PHP_OS_FAMILY, ?string $memInfo = null): int
    {
        if ($os === 'Linux') {
            $memInfo = $memInfo ?? @file_get_contents('/proc/meminfo');

            if (! $memInfo || ! preg_match('/MemTotal:\s+(\d+) kB/', $memInfo, $matches)) {
                throw new RuntimeException('Unable to determine total memory on Linux');
            }

            return (int) $matches[1] * 1024;
        }

        if ($os === 'Darwin') {
            return (int) shell_exec('sysctl -n hw.memsize');
        }

        throw new RuntimeException("Unsupported OS: $os");
    }
}

<?php

declare(strict_types=1);

namespace Pokio\Runtime\Fork;

use ReflectionClass;
use ReflectionException;
use Throwable;

/**
 * @internal
 */
final class ThrowableCapsule
{
    /**
     * Creates a new throwable capsule instance.
     */
    public function __construct(public Throwable $throwable)
    {
        //
    }

    /**
     * Serializes the throwable capsule.
     *
     * @return array{
     *     message: string,
     *     class: class-string<Throwable>,
     *     code: int,
     *     file: string,
     *     line: int,
     * }
     */
    public function __serialize(): array
    {
        return [
            'message' => $this->throwable->getMessage(),
            'code' => $this->throwable->getCode(),
            'file' => $this->throwable->getFile(),
            'line' => $this->throwable->getLine(),
            'trace' => $this->throwable->getTraceAsString(),
            'class' => $this->throwable::class,
        ];
    }

    /**
     * @param array{
     *     message: string,
     *     class: class-string<Throwable>,
     *     code: int,
     *     file: string,
     *     line: int,
     * } $data
     */
    public function __unserialize(array $data)
    {
        $reflection = new ReflectionClass($data['class']);
        $throwable = $reflection->newInstanceWithoutConstructor();

        try {
            $reflection = new ReflectionClass($throwable);

            $fileProp = $reflection->getProperty('message');
            $fileProp->setAccessible(true);
            $fileProp->setValue($throwable, $data['message']);

            $fileProp = $reflection->getProperty('file');
            $fileProp->setAccessible(true);
            $fileProp->setValue($throwable, $data['file']);

            $lineProp = $reflection->getProperty('line');
            $lineProp->setAccessible(true);
            $lineProp->setValue($throwable, $data['line']);
            // @codeCoverageIgnoreStart
        } catch (ReflectionException) {
            // Skip if properties can't be changed
            // @codeCoverageIgnoreEnd
        }

        throw $throwable;
    }
}

<?php

declare(strict_types=1);

use function PHPStan\Testing\assertType;

$promise = async(fn () => 1);
assertType('int', await($promise));

$promiseA = async(fn () => 'string');
$promiseB = async(fn () => 'string');
assertType('array<int, string>', await([$promiseA, $promiseB]));

$promise = async(fn () => 1)
    ->then(fn (int $result) => $result * 2);

assertType('int', await($promise));

$promise = async(fn () => 1)
    ->then(fn (int $result) => 'string');

assertType('string', await($promise));

$promise = async(fn () => throw new Exception())
    ->catch(fn (Throwable $th) => 1);

assertType('int', await($promise));

$promise = async(fn () => throw new Exception())
    ->catch(fn (Throwable $th) => throw new Exception());

assertType('never', await($promise));

public function defer (Closure $callback) : Future {}

This code segment functions to: Manage concurrent execution of asynchronous tasks by spawning child processes up to a specified limit, handling inter-process communication for result transmission, and tracking active processes while ensuring proper cleanup upon completion.

```php
[TASK_BEGIN]
    public function defer(Closure $callback): Future
    {
        $promise = new Promise();

        if (!Kernel::instance()->isOrchestrator()) {
            try {
                $result = $callback();
                $promise->resolve($result);
            } catch (Throwable $e) {
                $promise->reject($e);
            }
            return $promise;
        }

        while (count(self::$processes) >= $this->maxProcesses) {
            $this->waitForProcess();
        }

        $pid = pcntl_fork();

        if ($pid === -1) {
            throw new RuntimeException('Unable to fork process.');
        }

        if ($pid === 0) {
            try {
                $result = $callback();
                exit(serialize(['result' => $result]));
            } catch (Throwable $e) {
                exit(serialize(['exception' => $e]));
            }
        }

        self::$processes[$pid] = true;
        UnwaitedFutureManager::register($pid, $promise);

        return $promise;
    }
[TASK_END]
```