{"id":19152601,"url":"https://github.com/bamless/event_horizon","last_synced_at":"2026-06-07T02:02:39.352Z","repository":{"id":175656617,"uuid":"654280529","full_name":"bamless/event_horizon","owner":"bamless","description":"Event Horizon - Asynchronous I/O for the J* language.","archived":false,"fork":false,"pushed_at":"2026-03-08T02:08:56.000Z","size":216,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-28T00:10:20.809Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","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/bamless.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":"2023-06-15T19:24:02.000Z","updated_at":"2026-03-08T02:09:01.000Z","dependencies_parsed_at":"2025-06-07T11:40:20.679Z","dependency_job_id":"7bcdbb44-8bdc-4817-9e70-f330dbfa8a0c","html_url":"https://github.com/bamless/event_horizon","commit_stats":null,"previous_names":["bamless/event_horizon"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/bamless/event_horizon","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bamless%2Fevent_horizon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bamless%2Fevent_horizon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bamless%2Fevent_horizon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bamless%2Fevent_horizon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bamless","download_url":"https://codeload.github.com/bamless/event_horizon/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bamless%2Fevent_horizon/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","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":[],"created_at":"2024-11-09T08:18:29.207Z","updated_at":"2026-06-07T02:02:39.344Z","avatar_url":"https://github.com/bamless.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Event Horizon: Asynchronous I/O for the [J*](https://github.com/bamless/jstar) language\n\n\nEvent Horizon extends [J*](https://github.com/bamless/jstar) with asynchronous I/O, letting you write concurrent network and I/O code\nusing an `async/await`-like syntax. Under the hood, the library drives a single-threaded\n[libuv](https://libuv.org) event loop that multiplexes I/O readiness events and schedules callbacks\nwhen data is available.\n\n## Features\n\n- **`async/await`**: coroutine-based concurrency via the `@async` decorator and `yield`\n- **Futures and Tasks**: explicit result containers and scheduled coroutines\n- **TCP**: `TCPStream` for clients and servers, with automatic DNS resolution\n- **TLS**: `TLSStream` for encrypted TCP connections (client and server), backed by [mbedTLS](https://github.com/Mbed-TLS/mbedtls)\n- **UDP**: `UDPSocket`, including multicast support\n- **Pipes**: `PipeStream` for cross-platform pipes (Unix domain sockets on Unix, named pipes on Windows)\n- **Timers**: `sleep()` for task-friendly delays\n- **DNS**: asynchronous `getAddrInfo()`\n- **Raw libuv bindings**: `event_horizon.uv` for callback-style code\n\n## How it works\n\nEvent Horizon uses J*'s generator protocol to simulate `async/await`. Marking a function with the\n`@async` decorator causes it to return a lazy coroutine object. Calling the function does not start\nexecution - scheduling it as a `Task` does. A coroutine is scheduled when it is passed to `evh.run()`\nor `loop.runUntilComplete()`, passed to `evh.createTask()` or `loop.createTask()`, yielded (awaited)\nfrom a running task, or passed as an input to combinators such as `all()`, `race()`, and `waitAll()`.\n\nInside an async function, `yield`ing a `Future`, `Task`, or coroutine suspends the current task until\nthe awaitable completes. If it completes with a value, execution resumes with that value; if it\ncompletes with an exception, the exception is re-raised and can be caught with `try/except`.\n\nThe event loop is started explicitly by passing the top-level coroutine, `Future`, or `Task` to\n`evh.run()`, which blocks until that awaitable has completed.\n\nFor code that needs explicit loop control, `getEventLoop()` returns the currently running loop, or the\ndefault loop when no loop is running. `getRunningEventLoop()` returns the loop currently executing\ncallbacks, or `null` outside a running loop. `getDefaultEventLoop()` always returns the process-wide\ndefault loop.\n\nFutures, Tasks, and handles are owned by exactly one event loop. High-level operations reuse the\nloop attached to their handle, while coroutine objects stay loop-neutral until they are scheduled as\nTasks. Awaiting a Future from a different loop raises an error instead of transferring it implicitly.\n\n### Combining awaitables\n\nEvent Horizon exposes a small set of focused combinators for working with compound awaitables:\n\n| Function | Description |\n|---|---|\n| `all(...awaitables)` | Complete with ordered results when every awaitable succeeds. If one raises or is cancelled, cancel unfinished siblings and raise that error. |\n| `race(...awaitables)` | Complete with the first awaitable to finish, whether it returns a value, raises, or is cancelled. Unfinished siblings are cancelled. |\n| `waitAll(...awaitables)` | Wait until every awaitable finishes and return ordered `WaitOutcome` records. Child errors and cancellations are reported as outcomes instead of raising from the returned Future. |\n\n`WaitOutcome` has public `state` and `value` fields. `state` is one of\n`WaitOutcomeState.COMPLETED`, `WaitOutcomeState.FAILED`, or `WaitOutcomeState.CANCELLED`.\n`value` is the returned value for fulfilled outcomes and the exception for rejected or cancelled\noutcomes.\n\nGeneric event loop interface:\n\n| Method | Description |\n|---|---|\n| `runUntilComplete(awaitable)` | Block until a Future, Task, or coroutine completes, then return its result or raise its exception. |\n| `runForever()` | Run callbacks until `stop()` is called or the loop has no active handles left. |\n| `stop()` | Ask the loop to stop on the next suitable turn. |\n| `close()` | Close the loop; raises if there are still pending handles. |\n| `isRunning()` | Return whether this loop is currently running callbacks. |\n| `isClosed()` | Return whether this loop has been closed. |\n| `callSoon(callback)` | Schedule `callback` for the next loop turn and return a cancel function. |\n| `callLater(ms, callback)` | Schedule `callback` after `ms` milliseconds and return a cancel function. |\n| `createFuture()` | Create a pending Future owned by this loop. |\n| `createTask(coro)` | Schedule a coroutine on this loop and return a Task. |\n| `sleep(ms)` | Return a Future that completes after `ms` milliseconds. |\n\n## Module reference\n\n| Module | Exports |\n|---|---|\n| `event_horizon` | `run`, `all`, `race`, `waitAll`, `WaitOutcome`, `WaitOutcomeState`, `sleep`, `createTask`, `ensureFuture`, `getEventLoop`, `getRunningEventLoop`, `getDefaultEventLoop` |\n| `event_horizon.async` | `async`, `Coroutine` |\n| `event_horizon.future` | `Future`, `ensureFuture` |\n| `event_horizon.task` | `Task` |\n| `event_horizon.tcp` | `TCPStream` |\n| `event_horizon.tls` | `TLSStream` |\n| `event_horizon.udp` | `UDPSocket` |\n| `event_horizon.pipe` | `PipeStream` |\n| `event_horizon.dns` | `getAddrInfo` |\n| `event_horizon.uv` | Raw libuv bindings |\n\n## Example\n\nThe following snippet implements a TCP echo server. Error handling is omitted for brevity; see the\n`examples/` folder for complete, production-style examples.\n\n```jstar\nimport event_horizon as evh\nimport event_horizon.async for async\nimport event_horizon.tcp for TCPStream\n\n// The `@async` decorator turns this function into a lazy coroutine.\n// Inside it, `yield` suspends execution until the awaited Future completes.\n@async fun handleClient(client)\n    var data\n    while data = yield client.readLine()\n        yield client.write(data)\n    end\nend\n\n@async fun main()\n    var server = TCPStream()\n    server.bind(\"0.0.0.0\", 8080)\n    // `listen` returns a Future that completes when the server stops accepting connections.\n    yield server.listen(handleClient)\nend\n\n// Start the event loop and run the top-level coroutine to completion.\nevh.run(main())\n```\n\n## Raw libuv bindings\n\nWhile `async/await` is the default and preferred way to write asynchronous code with Event Horizon,\nthe `event_horizon.uv` module exposes thin, callback-based wrappers around libuv for cases where\ndirect control over the event loop is needed.\n\n`event_horizon.uv.loop()` returns the default raw loop object. It implements the same generic loop\ninterface described above, plus internal libuv-oriented helpers used by the raw wrappers and tests:\n`_alive()` reports whether libuv has active handles or requests, and `_walk(callback)` visits active\nhandles with `callback(handle)`. Prefer the generic methods for application code; reach for these\nhelpers only when writing low-level wrapper code or diagnostics.\n\n## Installation\n\n### Prerequisites\n\n- [J*](https://github.com/bamless/jstar) ≥ 1.0\n- [CMake](https://cmake.org) ≥ 3.10\n- [libuv](https://libuv.org) ≥ 1.0 (or use the bundled build described below)\n- [mbedTLS](https://github.com/Mbed-TLS/mbedtls) ≥ 3.0 (or use the bundled build described below)\n\n### Building\n\n```bash\ncmake -B build\ncmake --build build -j\nsudo cmake --install build\n```\n\nThen import the library in your J* code:\n\n```jstar\nimport event_horizon as evh\n```\n\n### Bundling dependencies\n\nBoth libuv and mbedTLS can be downloaded and compiled as part of the build,\nwithout requiring them to be installed on your system:\n\n```bash\ncmake -B build -DEVH_VENDOR_LIBUV=ON -DEVH_VENDOR_MBEDTLS=ON\ncmake --build build -j\nsudo cmake --install build\n```\n\nThe options are independent - you can vendor either library individually if the\nother is already available system-wide.\n\n## Running the tests\n\n```bash\njstar tests/run.jsr\n```\n\n### TLS test certificates\n\nThe TLS tests (`tests/evh/tls.jsr`, `tests/uv/tls.jsr`) require a private key that\nis **not** stored in the repository. Before running the full suite for the first time,\ngenerate a self-signed certificate and key in `tests/certs/`:\n\n```bash\nopenssl req -x509 -newkey rsa:2048 \\\n    -keyout tests/certs/key.pem \\\n    -out    tests/certs/cert.pem \\\n    -days 3650 -nodes \\\n    -subj \"/CN=localhost\" \\\n    -addext \"subjectAltName=DNS:localhost,IP:127.0.0.1\" \\\n    -addext \"basicConstraints=critical,CA:TRUE\" \\\n    -addext \"keyUsage=critical,digitalSignature,keyEncipherment\" \\\n    -addext \"extendedKeyUsage=serverAuth\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbamless%2Fevent_horizon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbamless%2Fevent_horizon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbamless%2Fevent_horizon/lists"}