{"id":35454477,"url":"https://github.com/vosaka-php/vosaka-foroutines","last_synced_at":"2026-03-14T10:04:27.148Z","repository":{"id":306639919,"uuid":"1026846378","full_name":"vosaka-php/vosaka-foroutines","owner":"vosaka-php","description":"A PHP library for structured asynchronous programming using foroutines (fiber + coroutines)","archived":false,"fork":false,"pushed_at":"2026-03-05T04:03:10.000Z","size":614,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-05T09:06:16.121Z","etag":null,"topics":["coroutine-library","coroutines","fibers","parallel-processing","php-library","php8","yield"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vosaka-php.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-26T18:29:38.000Z","updated_at":"2026-03-05T04:02:32.000Z","dependencies_parsed_at":"2026-03-05T06:09:18.767Z","dependency_job_id":null,"html_url":"https://github.com/vosaka-php/vosaka-foroutines","commit_stats":null,"previous_names":["vosaka-php/vosaka-foroutines"],"tags_count":38,"template":false,"template_full_name":null,"purl":"pkg:github/vosaka-php/vosaka-foroutines","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vosaka-php%2Fvosaka-foroutines","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vosaka-php%2Fvosaka-foroutines/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vosaka-php%2Fvosaka-foroutines/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vosaka-php%2Fvosaka-foroutines/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vosaka-php","download_url":"https://codeload.github.com/vosaka-php/vosaka-foroutines/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vosaka-php%2Fvosaka-foroutines/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30229591,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T19:01:10.287Z","status":"ssl_error","status_checked_at":"2026-03-07T18:59:58.103Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coroutine-library","coroutines","fibers","parallel-processing","php-library","php8","yield"],"created_at":"2026-01-03T05:56:53.860Z","updated_at":"2026-03-14T10:04:27.128Z","avatar_url":"https://github.com/vosaka-php.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VOsaka Foroutines\n\nA PHP library for structured asynchronous programming using foroutines (fiber + coroutines), inspired by Kotlin coroutines.\nThis is project with the contribution of a project from [php-async](https://github.com/terremoth/php-async)\n\n## 📚 Documentation\n\nNew to VOsaka Foroutines? Check out our **[Structured Documentation](./docs/README.md)** (following the Diátaxis framework), which includes:\n- **Tutorials**: Step-by-step learning lessons.\n- **How-to Guides**: Task-oriented recipes for common problems.\n- **Reference**: Detailed technical descriptions of the API.\n- **Explanation**: Conceptual overviews and architectural deep-dives.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                        main() entry point                       │\n├─────────────────────────────────────────────────────────────────┤\n│                                                                 │\n│  ┌──────────────┐   ┌──────────────┐   ┌──────────────────┐    │\n│  │ RunBlocking   │   │   Launch      │   │     Async        │    │\n│  │ (drive loop)  │   │ (fire \u0026 wait) │   │ (await result)   │    │\n│  └──────┬───────┘   └──────┬───────┘   └──────┬───────────┘    │\n│         │                  │                   │                │\n│         ▼                  ▼                   ▼                │\n│  ┌─────────────────────────────────────────────────────────┐    │\n│  │               Cooperative Scheduler Loop                 │    │\n│  │  ┌───────────────┬─────────────────┬────────────────┐   │    │\n│  │  │ AsyncIO       │  WorkerPool     │  Launch Queue  │   │    │\n│  │  │ pollOnce()    │  run()          │  runOnce()     │   │    │\n│  │  │ stream_select │  child procs    │  fiber resume  │   │    │\n│  │  └───────────────┴─────────────────┴────────────────┘   │    │\n│  │                                                         │    │\n│  │  FiberPool: reusable Fiber instances (default: 10)      │    │\n│  │  Idle detection → usleep(500µs) to prevent CPU spin     │    │\n│  └─────────────────────────────────────────────────────────┘    │\n│                                                                 │\n│  ┌──────────────────────────────────────────────────────────┐   │\n│  │                    Dispatchers                            │   │\n│  │  DEFAULT: fibers in current process (+ AsyncIO streams)  │   │\n│  │  IO:      child process (ForkProcess or symfony/process)  │   │\n│  │  MAIN:    EventLoop (deferred scheduling)                 │   │\n│  └──────────────────────────────────────────────────────────┘   │\n│                                                                 │\n│  ┌──────────────────────────────────────────────────────────┐   │\n│  │                    Channel (4 transports)                 │   │\n│  │  IN-PROCESS:  fiber ←→ fiber (in-memory array buffer)    │   │\n│  │  SOCKET POOL: Channel::create() → ChannelBrokerPool      │   │\n│  │  SOCKET IPC:  newSocketInterProcess() → ChannelBroker     │   │\n│  │  FILE IPC:    newInterProcess() → temp file + Mutex       │   │\n│  └──────────────────────────────────────────────────────────┘   │\n│                                                                 │\n│  ┌─────────────┐  ┌─────────────┐  ┌──────────────────────┐    │\n│  │  Flow (cold) │  │ SharedFlow / │  │  WorkerPool          │    │\n│  │  + buffer()  │  │ StateFlow    │  │  (task batching +    │    │\n│  │  operator    │  │ (hot, back-  │  │   dynamic scaling +  │    │\n│  │              │  │  pressure)   │  │   respawn backoff)   │    │\n│  └─────────────┘  └─────────────┘  └──────────────────────┘    │\n│                                                                 │\n│  ┌─────────────┐  ┌─────────────┐  ┌──────────────────────┐    │\n│  │  Mutex       │  │  Select      │  │  Job lifecycle       │    │\n│  │  (multi-proc │  │  (channel    │  │  (cancel, join,      │    │\n│  │   file/sem)  │  │   multiplex) │  │   invokeOnComplete)  │    │\n│  └─────────────┘  └─────────────┘  └──────────────────────┘    │\n│                                                                 │\n│  ┌─────────────┐  ┌──────────────────────────────────────┐     │\n│  │  Actor Model │  │  Supervisor Tree (OTP-style)          │     │\n│  │  (mailbox +  │  │  ONE_FOR_ONE / ONE_FOR_ALL /          │     │\n│  │   message)   │  │  REST_FOR_ONE + restart budget         │     │\n│  └─────────────┘  └──────────────────────────────────────┘     │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Features\n\n**Core** — `RunBlocking`, `Launch`, `Async`, `Async::awaitAll()`, `Delay`, `Repeat`, `WithTimeout`, Job lifecycle\n\n**Dispatchers** — `DEFAULT` (fibers + AsyncIO), `IO` (child process via WorkerPool), `MAIN` (event loop)\n\n**WorkerPool** — Pre-spawned long-lived worker processes with task batching, dynamic pool sizing, and respawn backoff\n\n**FiberPool** — Reusable Fiber instances for scheduler optimization (default: 10, dynamic sizing)\n\n**Channel** — Four transports: in-process, socket pool (default), socket per-channel, file-based\n\n**AsyncIO** — Non-blocking stream I/O via `stream_select()` (TCP, TLS, HTTP, files, DNS)\n\n**Flow** — Cold `Flow`, `SharedFlow`, `StateFlow` with backpressure (`SUSPEND`, `DROP_OLDEST`, `DROP_LATEST`, `ERROR`)\n\n**Actor Model** — Message-passing concurrency with Channel-based mailboxes and `ActorSystem` registry\n\n**Supervisor Tree** — OTP-style supervision with `ONE_FOR_ONE`, `ONE_FOR_ALL`, `REST_FOR_ONE` strategies\n\n**Sync** — `Mutex` (file, semaphore, APCu), `Select` for channel multiplexing\n\n## Rules\n\n\u003cimg src=\"https://github.com/vosaka-php/vosaka-foroutines/blob/main/rules.png\" alt=\"Rules\" width=\"800\"\u003e\n\n## Requirements\n\n- PHP 8.2+\n- ext-shmop, ext-fileinfo, ext-zlib\n\n| Optional Extension | Purpose                                                           |\n| ------------------ | ----------------------------------------------------------------- |\n| ext-pcntl          | Low-overhead IO dispatch via `pcntl_fork()` (~1-5ms vs ~50-200ms) |\n| ext-sysvsem        | Semaphore-based `Mutex`                                           |\n| ext-apcu           | APCu-based `Mutex`                                                |\n\n## Installation\n\n```\ncomposer require venndev/vosaka-fourotines\n```\n\n## Usage\n\nAll entry points must be wrapped in `main()` or use the `#[AsyncMain]` attribute:\n\n```php\nuse function vosaka\\foroutines\\main;\n\nmain(function () {\n    // Your async code here\n});\n```\n\n### RunBlocking + Launch\n\n```php\nuse vosaka\\foroutines\\{RunBlocking, Launch, Delay, Thread};\nuse function vosaka\\foroutines\\main;\n\nmain(function () {\n    RunBlocking::new(function () {\n        Launch::new(function () {\n            Delay::new(1000);\n            var_dump('Task 1 done');\n        });\n\n        Launch::new(function () {\n            Delay::new(500);\n            var_dump('Task 2 done');\n        });\n    });\n});\n```\n\n### Async / Await\n\n```php\nuse vosaka\\foroutines\\{Async, Delay, Dispatchers};\n\n// Create and await a single async task\n$result = Async::new(function () {\n    Delay::new(100);\n    return 42;\n})-\u003eawait();\n\n// Run in a separate worker process (IO dispatcher)\n$io = Async::new(function () {\n    return file_get_contents('data.txt');\n}, Dispatchers::IO)-\u003eawait();\n```\n\n### Async::awaitAll — Concurrent Awaiting\n\n`awaitAll()` drives multiple async tasks forward simultaneously, returning all results in order. This is significantly more efficient than awaiting sequentially.\n\n```php\nuse vosaka\\foroutines\\{Async, Delay};\n\n$asyncA = Async::new(function () {\n    Delay::new(500);\n    return 42;\n});\n\n$asyncB = Async::new(function () {\n    Delay::new(800);\n    return 'hello';\n});\n\n$asyncC = Async::new(function () {\n    Delay::new(300);\n    return 100;\n});\n\n// All three run concurrently — total time ≈ 800ms, not 1600ms\n[$a, $b, $c] = Async::awaitAll($asyncA, $asyncB, $asyncC);\n\n// Also works with spread operator\n$results = Async::awaitAll(...$arrayOfAsyncs);\n```\n\n### WithTimeout\n\n```php\nuse vosaka\\foroutines\\{WithTimeout, WithTimeoutOrNull, Delay};\n\n// Throws RuntimeException if exceeded\n$val = WithTimeout::new(2000, function () {\n    Delay::new(1000);\n    return 'ok';\n});\n\n// Returns null instead of throwing\n$val = WithTimeoutOrNull::new(500, function () {\n    Delay::new(3000);\n    return 'too slow';\n});\n```\n\n### Job Lifecycle\n\n```php\nuse vosaka\\foroutines\\Launch;\n\n$job = Launch::new(function () {\n    Delay::new(5000);\n    return 'done';\n});\n\n$job-\u003einvokeOnCompletion(function ($j) {\n    var_dump('Job finished: ' . $j-\u003egetStatus()-\u003ename);\n});\n\n$job-\u003ecancelAfter(2.0);\n```\n\n### Channel\n\n| Mode                  | Factory                                          | Use Case                           |\n| --------------------- | ------------------------------------------------ | ---------------------------------- |\n| In-process            | `Channel::new(capacity)`                         | Fibers in the same process         |\n| Socket pool (default) | `Channel::create(capacity)`                      | IPC via shared `ChannelBrokerPool` |\n| Socket per-channel    | `Channel::newSocketInterProcess(name, capacity)` | Legacy — 1 process per channel     |\n| File-based            | `Channel::newInterProcess(name, capacity)`       | IPC via temp file + mutex          |\n\n```php\nuse vosaka\\foroutines\\channel\\Channel;\nuse vosaka\\foroutines\\{RunBlocking, Launch, Dispatchers, Thread};\nuse function vosaka\\foroutines\\main;\n\nmain(function () {\n    $ch = Channel::create(5);   // pool-backed IPC channel\n\n    RunBlocking::new(function () use ($ch) {\n        Launch::new(function () use ($ch) {\n            $ch-\u003econnect();     // reconnect in child process\n            $ch-\u003esend('from child 1');\n            $ch-\u003esend('from child 2');\n        }, Dispatchers::IO);\n\n        Launch::new(function () use ($ch) {\n            var_dump($ch-\u003ereceive()); // \"from child 1\"\n            var_dump($ch-\u003ereceive()); // \"from child 2\"\n        });\n\n        $ch-\u003eclose();\n    });\n});\n```\n\n**Non-blocking operations:**\n\n```php\n$ok  = $ch-\u003etrySend(42);     // false if buffer full\n$val = $ch-\u003etryReceive();    // null if buffer empty\n```\n\n**Channels utility class:**\n\n```php\nuse vosaka\\foroutines\\channel\\Channels;\n\n$merged  = Channels::merge($ch1, $ch2, $ch3);\n$doubled = Channels::map($ch, fn($v) =\u003e $v * 2);\n$evens   = Channels::filter($ch, fn($v) =\u003e $v % 2 === 0);\n$first3  = Channels::take($ch, 3);\n$zipped  = Channels::zip($ch1, $ch2);\n$nums    = Channels::range(1, 100);\n$ticks   = Channels::timer(500, maxTicks: 10);\n```\n\n### Select\n\n```php\nuse vosaka\\foroutines\\channel\\Channel;\nuse vosaka\\foroutines\\selects\\Select;\n\n$ch1 = Channel::new(1);\n$ch2 = Channel::new(1);\n$ch1-\u003esend('from ch1');\n\n$result = (new Select())\n    -\u003eonReceive($ch1, fn($v) =\u003e \"Got: $v\")\n    -\u003eonReceive($ch2, fn($v) =\u003e \"Got: $v\")\n    -\u003edefault('nothing ready')\n    -\u003eexecute();\n```\n\n### Flow\n\n```php\nuse vosaka\\foroutines\\flow\\{Flow, SharedFlow, MutableStateFlow, BackpressureStrategy};\n\n// Cold Flow\nFlow::of(1, 2, 3, 4, 5)\n    -\u003efilter(fn($v) =\u003e $v % 2 === 0)\n    -\u003emap(fn($v) =\u003e $v * 10)\n    -\u003ecollect(fn($v) =\u003e var_dump($v)); // 20, 40\n\n// SharedFlow with backpressure\n$flow = SharedFlow::new(\n    replay: 3,\n    extraBufferCapacity: 10,\n    onBufferOverflow: BackpressureStrategy::DROP_OLDEST,\n);\n\n// StateFlow\n$state = MutableStateFlow::new(0);\n$state-\u003ecollect(fn($v) =\u003e var_dump(\"State: $v\"));\n$state-\u003eemit(1);\n\n// Cold Flow with buffer operator\nFlow::fromArray(range(1, 1000))\n    -\u003efilter(fn($v) =\u003e $v % 2 === 0)\n    -\u003ebuffer(capacity: 64, onOverflow: BackpressureStrategy::SUSPEND)\n    -\u003ecollect(fn($v) =\u003e process($v));\n```\n\n### AsyncIO — Non-blocking Stream I/O\n\nAll methods return `Deferred` — a lazy wrapper that executes on `-\u003eawait()`:\n\n```php\nuse vosaka\\foroutines\\AsyncIO;\n\n$body   = AsyncIO::httpGet('https://example.com')-\u003eawait();\n$data   = AsyncIO::fileGetContents('/path/to/file')-\u003eawait();\n$socket = AsyncIO::tcpConnect('example.com', 80)-\u003eawait();\n$ip     = AsyncIO::dnsResolve('example.com')-\u003eawait();\n```\n\n| Method                                  | Returns    | Description                     |\n| --------------------------------------- | ---------- | ------------------------------- |\n| `tcpConnect(host, port)-\u003eawait()`       | `resource` | Non-blocking TCP connection     |\n| `tlsConnect(host, port)-\u003eawait()`       | `resource` | Non-blocking TLS/SSL connection |\n| `streamRead(stream, maxBytes)-\u003eawait()` | `string`   | Read up to N bytes              |\n| `streamReadAll(stream)-\u003eawait()`        | `string`   | Read until EOF                  |\n| `streamWrite(stream, data)-\u003eawait()`    | `int`      | Write data                      |\n| `httpGet(url)-\u003eawait()`                 | `string`   | HTTP GET                        |\n| `httpPost(url, body)-\u003eawait()`          | `string`   | HTTP POST                       |\n| `fileGetContents(path)-\u003eawait()`        | `string`   | Read entire file                |\n| `filePutContents(path, data)-\u003eawait()`  | `int`      | Write file                      |\n| `dnsResolve(hostname)-\u003eawait()`         | `string`   | Resolve hostname to IP          |\n\n### Mutex\n\n```php\nuse vosaka\\foroutines\\sync\\Mutex;\n\nMutex::protect('my-resource', function () {\n    file_put_contents('shared.txt', 'safe write');\n});\n```\n\n### Dispatchers\n\n| Dispatcher | Description                                                            |\n| ---------- | ---------------------------------------------------------------------- |\n| `DEFAULT`  | Runs in the current fiber context (+ AsyncIO for non-blocking streams) |\n| `IO`       | Offloads to a worker process via WorkerPool                            |\n| `MAIN`     | Schedules on the main event loop                                       |\n\n```php\nuse vosaka\\foroutines\\{RunBlocking, Launch, Dispatchers, Thread};\n\nRunBlocking::new(function () {\n    Launch::new(fn() =\u003e heavy_io_work(), Dispatchers::IO);\n});\n```\n\n### Thread::await()\n\nWhile `RunBlocking` automatically drains all pending tasks before returning, `Thread::await()` allows you to manually block and drive the event loop until all work (Launch jobs, WorkerPool tasks, and AsyncIO) is finished.\n\n**When do you need it?**\n- **Inside `RunBlocking`**: If you want to ensure all background tasks (like `Launch` jobs) are completed *before* proceeding to the next line of code *within* the same `RunBlocking` block.\n- **Outside `RunBlocking`**: When you are using `AsyncMain` or `main()` and have scheduled tasks that need to be completed before the script exits, but you aren't using a blocking runner.\n\n```php\nRunBlocking::new(function () {\n    Launch::new(fn() =\u003e print(\"A\"));\n    Thread::await(); // Blocks here until \"A\" is printed\n    print(\"B\");      // Always prints after \"A\"\n});\n```\n\n### WorkerPool\n\nA pool of pre-spawned long-lived child processes. On Linux/macOS uses `pcntl_fork()` + Unix socket pairs; on Windows uses `proc_open()` + TCP loopback sockets.\n\n```php\nuse vosaka\\foroutines\\WorkerPool;\n\nWorkerPool::setPoolSize(8);\n\n$result = WorkerPool::addAsync(function () {\n    return 'processed';\n})-\u003eawait();\n```\n\n#### Task Batching\n\nWhen many small tasks are submitted, IPC round-trip overhead dominates. Task batching groups multiple tasks into a single message sent to each worker, dramatically reducing round-trips.\n\n```\nbatchSize=1 (default):  Parent ──TASK:A──▶ Worker ──RESULT:A──▶ Parent  (1000 round-trips for 1000 tasks)\nbatchSize=5:            Parent ──BATCH:[A,B,C,D,E]──▶ Worker ──BATCH_RESULTS:[A,B,C,D,E]──▶ Parent  (200 round-trips)\n```\n\n```php\nuse vosaka\\foroutines\\WorkerPool;\n\n// Group up to 5 tasks per worker message\nWorkerPool::setBatchSize(5);\n```\n\n| Batch Size  | Behavior                                                |\n| ----------- | ------------------------------------------------------- |\n| 1 (default) | Original single-task protocol — lowest latency per task |\n| 5–10        | Good balance for many small/fast tasks                  |\n| 20–50       | Maximum throughput for trivial tasks                    |\n\nBatching is fully backward compatible — when `batchSize=1`, the pool uses the original `TASK:`/`RESULT:` protocol.\n\n#### Dynamic Pool Sizing\n\nThe pool can automatically scale between a minimum and maximum number of workers based on workload pressure.\n\n```php\nuse vosaka\\foroutines\\WorkerPool;\n\nWorkerPool::setPoolSize(4);    // initial workers at boot\n\nWorkerPool::setDynamicScaling(\n    enabled: true,\n    minPoolSize: 2,            // always keep at least 2 workers alive\n    maxPoolSize: 8,            // never exceed 8 workers\n    idleTimeout: 10.0,         // shut down a worker after 10s idle\n    scaleUpCooldown: 0.5,      // wait 0.5s between scale-ups\n    scaleDownCooldown: 5.0,    // wait 5s between scale-downs\n);\n```\n\n**Scale-up**: When all workers are busy and tasks are queued, a new worker is spawned (up to `maxPoolSize`).\n\n**Scale-down**: When a worker has been idle longer than `idleTimeout` and the pool exceeds `minPoolSize`, it is shut down.\n\n```\nWorkload spike:    2 workers → 4 → 6 → 8 (max)\nWorkload drops:    8 workers → 6 → 4 → 2 (min, after idle timeout)\n```\n\nWhen dynamic scaling is disabled (default), the pool behaves exactly as before — a fixed number of workers.\n\n#### Worker Respawn Backoff\n\nWhen a worker crashes repetitively, respawning uses exponential backoff (100ms → 200ms → … max 30s) to prevent CPU spin. After 10 consecutive failures, the worker slot is removed (circuit-breaker).\n\n```php\n// Customizable\nWorkerPoolState::$maxRespawnAttempts = 10;\nWorkerPoolState::$respawnBaseDelayMs = 100;\n```\n\n### FiberPool\n\nReusable Fiber instances to reduce allocation overhead. Integrated into `Launch`, `Async`, `RunBlocking`.\n\n```php\nuse vosaka\\foroutines\\FiberPool;\n\n// Adjust global pool size\nFiberPool::setDefaultSize(20);\n\n// Direct usage (zero-alloc reuse after first run)\n$pool = new FiberPool(maxSize: 10);\n$result = $pool-\u003erun(fn() =\u003e heavyComputation());\n```\n\n### Actor Model\n\n```php\nuse vosaka\\foroutines\\actor\\{Actor, Message, ActorSystem};\n\nclass GreeterActor extends Actor {\n    protected function receive(Message $msg): void {\n        echo \"Hello, {$msg-\u003epayload}!\\n\";\n    }\n}\n\nmain(function () {\n    RunBlocking::new(function () {\n        $system = ActorSystem::new()\n            -\u003eregister(new GreeterActor('greeter'));\n\n        $system-\u003estartAll();\n        $system-\u003esend('greeter', Message::of('greet', 'World'));\n\n        Delay::new(100);\n        $system-\u003estopAll();\n    });\n});\n```\n\n### Supervisor Tree\n\nOTP-style supervision with automatic restart on child failure.\n\n```php\nuse vosaka\\foroutines\\supervisor\\{Supervisor, RestartStrategy};\n\nmain(function () {\n    RunBlocking::new(function () {\n        Supervisor::new(RestartStrategy::ONE_FOR_ONE)\n            -\u003echild(fn() =\u003e workerA(), 'worker-a')\n            -\u003echild(fn() =\u003e workerB(), 'worker-b', maxRestarts: 5)\n            -\u003estart();\n    });\n});\n```\n\n| Strategy       | Behavior                                     |\n| -------------- | -------------------------------------------- |\n| `ONE_FOR_ONE`  | Restart only the crashed child               |\n| `ONE_FOR_ALL`  | Restart all children                         |\n| `REST_FOR_ONE` | Restart crashed child + all started after it |\n\n### ForkProcess\n\nOn Linux/macOS, `ForkProcess` creates child processes by forking the current process instead of spawning a new interpreter:\n\n| Strategy                    | Overhead  | Closure Serialization      |\n| --------------------------- | --------- | -------------------------- |\n| `ForkProcess` (pcntl_fork)  | ~1-5ms    | Not needed (memory copied) |\n| `Process` (symfony/process) | ~50-200ms | Required                   |\n\nSelection is automatic — `Worker` uses fork when available, falls back to `symfony/process` on Windows.\n\n## Platform Support\n\n| Feature                  | Linux/macOS      | Windows                          |\n| ------------------------ | ---------------- | -------------------------------- |\n| Fibers (core)            | ✅               | ✅                               |\n| FiberPool                | ✅               | ✅                               |\n| AsyncIO (stream_select)  | ✅               | ✅                               |\n| Channel (all transports) | ✅               | ✅                               |\n| Actor Model              | ✅               | ✅                               |\n| Supervisor Tree          | ✅               | ✅                               |\n| WorkerPool (fork mode)   | ✅               | ❌ (uses socket mode)            |\n| WorkerPool (socket mode) | ✅               | ✅                               |\n| ForkProcess (pcntl_fork) | ✅               | ❌ (fallback to symfony/process) |\n| Mutex (file lock)        | ✅               | ✅                               |\n| Mutex (semaphore)        | ✅ (ext-sysvsem) | ❌                               |\n| Mutex (APCu)             | ✅ (ext-apcu)    | ✅ (ext-apcu)                    |\n\n## Comparison with JavaScript Async\n\n| Aspect       | Node.js                          | VOsaka Foroutines                                             |\n| ------------ | -------------------------------- | ------------------------------------------------------------- |\n| Runtime      | libuv event loop (C)             | PHP Fibers + stream_select                                    |\n| I/O model    | Non-blocking by default          | `AsyncIO` for streams; `Dispatchers::IO` for blocking APIs    |\n| Concurrency  | Single-threaded + worker threads | Single process + child processes (fork/spawn)                 |\n| Syntax       | `async/await` (language-level)   | `Async::new()-\u003eawait()` / `Async::awaitAll()` (library-level) |\n| Worker pool  | `worker_threads`                 | `WorkerPool` with task batching + dynamic scaling             |\n| IPC channels | `MessagePort`                    | `Channel::create()` (shared TCP pool)                         |\n| Flow control | Node.js Streams                  | `BackpressureStrategy` (SUSPEND/DROP/ERROR)                   |\n\n## License\n\nGNU Lesser General Public License v2.1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvosaka-php%2Fvosaka-foroutines","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvosaka-php%2Fvosaka-foroutines","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvosaka-php%2Fvosaka-foroutines/lists"}