{"id":51477178,"url":"https://github.com/lsfratel/nova.c3l","last_synced_at":"2026-07-06T22:01:20.306Z","repository":{"id":367784979,"uuid":"1282168023","full_name":"lsfratel/nova.c3l","owner":"lsfratel","description":"An asynchronous event loop written in C3.","archived":false,"fork":false,"pushed_at":"2026-06-27T16:17:59.000Z","size":274,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T17:22:13.037Z","etag":null,"topics":["c3","event-loop"],"latest_commit_sha":null,"homepage":"","language":"C3","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lsfratel.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":"2026-06-27T12:13:02.000Z","updated_at":"2026-06-27T16:18:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lsfratel/nova.c3l","commit_stats":null,"previous_names":["lsfratel/nova.c3l"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/lsfratel/nova.c3l","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsfratel%2Fnova.c3l","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsfratel%2Fnova.c3l/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsfratel%2Fnova.c3l/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsfratel%2Fnova.c3l/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lsfratel","download_url":"https://codeload.github.com/lsfratel/nova.c3l/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lsfratel%2Fnova.c3l/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35206987,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-06T02:00:07.184Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["c3","event-loop"],"created_at":"2026-07-06T22:01:19.783Z","updated_at":"2026-07-06T22:01:20.286Z","avatar_url":"https://github.com/lsfratel.png","language":"C3","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nova\n\nAn asynchronous event loop written in [C3](https://c3-lang.org).\nProactor-style (submit an operation, get a completion callback),\nzero-allocation on the hot path, with all operation storage owned by the caller.\n\n## Overview\n\n`nova` gives you one event loop that drives timers, sockets, pipes, child\nprocesses, signals, filesystem work and DNS through a single completion model:\n\n- You submit an operation against caller-owned storage.\n- The loop runs it on the platform backend (`epoll` or `io_uring` on Linux,\n  `kqueue` on macOS).\n- When it finishes, your callback runs on the loop's owner thread.\n\nThere is **no per-operation allocation**: every operation embeds an intrusive\nruntime header and is stored wherever you put it. The loop only allocates during\n`init`.\n\n## Feature highlights\n\n- **TCP** — listen / accept / connect / read / write / shutdown, socket options.\n- **UDP** — bind / connect / send / recv, multicast join/leave, source filters.\n- **Pipes** — anonymous pipe pairs, adopt existing descriptors.\n- **Timers** — one-shot and finite-repeat, intrusive unbounded binary heap.\n- **Signals** — async-signal-safe delivery dispatched on the owner thread.\n- **Processes** — spawn with stdio pipes, env/cwd control, kill, reaping.\n- **Filesystem** — open/read/write/stat/…/copyfile/sendfile on a worker pool.\n- **fs-poll** — watch a path for change by stat polling.\n- **fd watch** — readiness watching (readable/writable/hangup/error).\n- **DNS** — forward and reverse resolution on the worker pool.\n- **Work pool** — offload blocking or CPU-heavy work, complete on the loop.\n\n## Buffering layers (optional)\n\nTwo connection helpers sit over `tcp::Stream`, both zero-allocation after init:\n\n- **`nova::buffered` — `StreamConn` (ring buffer).** For byte-stream / line\n  protocols: bytes flow through a fixed ring, framed with `find_byte`, with\n  coalesced vectored writes and read/write watermarks.\n- **`nova::framed` — `FramedConn` (linear buffer + compaction).** For message /\n  frame protocols: contiguous `peek`/`reserve` for in-place zero-copy response\n  building, plus `read_into` to stream a large body straight into caller storage\n  without buffering it.\n\n## Example\n\nA repeating timer (`Duration` is microseconds; `repeat = 2` means 1 initial fire\nplus 2 repeats):\n\n```c3\nimport std::io;\nimport nova @norecurse, nova::timer;\n\nfn void on_tick(Loop* loop, timer::TimerOp* op, timer::TimerResult* result, void* userdata)\n{\n\t(void)loop; (void)op; (void)userdata;\n\tif (result.error.status) return;\n\t(void)io::printfn(\"tick (%d expirations)\", result.expirations);\n}\n\nfn void main()\n{\n\tLoop loop;\n\ttimer::TimerOp op;\n\n\tloop.init()!!;\n\tdefer loop.destroy();\n\n\top.init();\n\tdefer op.destroy();\n\n\ttimer::start(\u0026loop, \u0026op, { .interval = (Duration)100_000, .repeat = 2 }, \u0026on_tick, null)!!;\n\tnova::run(\u0026loop, RunMode.UNTIL_STOPPED)!!;\n}\n```\n\nPrints `tick (1 expirations)` three times, then exits when the loop drains.\n\nSee [`examples/`](examples/) for TCP/UDP echo servers, an HTTP server, a worker\npool, `cat`, a file watcher, process spawning and pipes.\n\n## Design\n\n- **Proactor, not reactor.** You submit a complete operation and receive a\n  completion; you do not get raw readiness events to handle yourself.\n- **Caller-owned storage.** Operation handles (`TimerOp`, `tcp::ReadOp`, …) live\n  wherever you allocate them — stack, struct field, pool. The library never\n  takes ownership and never allocates per operation.\n- **Zero-allocation hot path.** Allocation happens only in `*.init`. Submit,\n  dispatch and callback paths allocate nothing (enforced by tracking-allocator\n  tests).\n- **Single owner thread.** The loop runs on one thread; cross-thread work is\n  posted in and dispatched back on the owner. Calls from another thread are\n  rejected with `nova::NOT_OWNER`.\n- **Contracts.** Caller obligations are `@require`/`@ensure` contracts;\n  recoverable failures are `fault` values via `T?`; release-critical invariants\n  use `always_assert`.\n\n## Install\n\n`nova` is a C3 library bundle (`nova.c3l/`). Put it on a dependency search path\nand depend on the `nova` library:\n\n```json5\n{\n  \"dependency-search-paths\": [ \"lib\" ],\n  \"dependencies\": [ \"nova\" ]\n}\n```\n\n(place `nova.c3l/` under `lib/`), then in your code:\n\n```c3\nimport nova;\nimport nova::timer; // and the submodules you use: net, net::tcp, net::udp, fs, ...\n```\n\nFor a standalone file: `c3c compile-run main.c3 --libdir lib --lib nova`.\n\n## Build \u0026 test\n\nRequires the [`c3c`](https://github.com/c3lang/c3c) compiler.\n\n```sh\nc3c test                          # run the test suite (257 tests)\ncd examples \u0026\u0026 c3c build all --trust=full   # build every example\n```\n\n## Examples\n\n| Target | Shows |\n|--------|-------|\n| `timer` | periodic + one-shot timers |\n| `tcp-echo` | TCP accept → read → echo |\n| `ping-pong` | client request cancelled by a one-shot timer timeout (server sleeps) |\n| `udp-echo` | UDP recv → echo to peer |\n| `buffered-echo` | line echo over the ring `StreamConn` (`nova::buffered`) |\n| `framed-echo` | length-prefixed message echo over `FramedConn` (`nova::framed`), incl. `read_into` for large bodies |\n| `http` / `http-multithread` / `http-signal` | HTTP server variants |\n| `work-pool` | offload compute + cancel a queued job |\n| `fs-cat` | stream a file to stdout |\n| `fs-watch` | detect file change via fs-poll |\n| `watch` | readiness-watch a raw fd (`nova::watch`) with one-shot re-arm |\n| `process` | spawn a child with stdio pipes |\n| `pipe` | anonymous pipe read/write |\n| `dns` | forward/reverse resolution |\n\n## Acknowledgments\n\nDesign, API shape and test coverage were informed by studying three mature\nevent loops: [libuv](https://github.com/libuv/libuv),\n[libxev](https://github.com/mitchellh/libxev) and\n[libevent](https://github.com/libevent/libevent).\n\n## License\n\nSee [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsfratel%2Fnova.c3l","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flsfratel%2Fnova.c3l","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flsfratel%2Fnova.c3l/lists"}