{"id":13741777,"url":"https://github.com/saltzm/async_io_uring","last_synced_at":"2026-01-17T06:12:41.323Z","repository":{"id":38186081,"uuid":"358425345","full_name":"saltzm/async_io_uring","owner":"saltzm","description":"An event loop in Zig using io_uring and coroutines","archived":false,"fork":false,"pushed_at":"2022-08-12T20:29:08.000Z","size":157,"stargazers_count":111,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-08-03T04:09:01.123Z","etag":null,"topics":["event-loop","io-uring","zig","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","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/saltzm.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}},"created_at":"2021-04-16T00:06:39.000Z","updated_at":"2024-07-16T10:54:22.000Z","dependencies_parsed_at":"2022-07-17T03:49:23.968Z","dependency_job_id":null,"html_url":"https://github.com/saltzm/async_io_uring","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saltzm%2Fasync_io_uring","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saltzm%2Fasync_io_uring/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saltzm%2Fasync_io_uring/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saltzm%2Fasync_io_uring/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saltzm","download_url":"https://codeload.github.com/saltzm/async_io_uring/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224782076,"owners_count":17369080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["event-loop","io-uring","zig","zig-package"],"created_at":"2024-08-03T04:01:02.580Z","updated_at":"2026-01-17T06:12:41.263Z","avatar_url":"https://github.com/saltzm.png","language":"Zig","readme":"\n# Overview\n\n`AsyncIOUring` is an event loop that wraps the `IO_Uring` library with coroutines\nsupport. It supports all `IO_Uring` operations (with the intentional exception\nof `poll_update`\\*). \n\nIn addition, it allows:\n* Adding timeouts to operations\n* Manual cancellation of operations\n* Writing custom operations for advanced use cases\n\nIt is currently functionally complete, though there are a few `TODO`s marked in\nthe source related to polishing the API. It's not used in production anywhere currently.\n\nSee `src/async_io_uring.zig` for full API documentation.\n\nSee the `examples` directory for an echo client and server that use the event loop.\n\n\\* If you need this for some reason, please create an issue.\n\n\u003e :warning: **The `main` branch of `async_io_uring` will follow changes to zig's `master` branch to stay up-to-date with changes to the `IO_Uring` API (among others).** See the tagged releases of `async_io_uring` that are marked to work with specific stable versions of zig. (E.g., release v0.1.0 works with zig 0.9.1)\n\n## Table of contents\n* [Background](#background)\n* [Goals](#goals)\n* [Installation](#installation)\n* [Example usage](#example-usage)\n    * [Echo client](#echo-client)\n    * [Operation timeouts](#operation-cancellation)\n    * [Operation cancellation](#operation-cancellation)\n\n---\n# Background\n\nAs an overview for the unfamiliar, `io_uring` is a new-ish Linux kernel feature \nthat allows users to enqueue requests to perform syscalls into a submission\nqueue (e.g. a request to read from a socket) and then submit the submission\nqueue to the kernel for processing.\n\nWhen requests from the submission queue have been satisfied, the result is\nplaced onto completion queue by the kernel. The user is able to either poll\nthe kernel for completion queue results or block until results are\navailable.\n\nZig's `IO_Uring` library provides a convenient interface to the kernel's\n`io_uring` functionality. The user of `IO_Uring`, however, still has to manually\ndeal with submitting requests to the kernel and retrieving events from the\ncompletion queue, which can be tedious.\n\nThis library wraps the `IO_Uring` library by adding an event loop that handles\nrequest submission and completion, and provides an interface for each syscall\nthat uses zig's `async` functionality to suspend execution of the calling code\nuntil the syscall has been completed. This lets the user write code that looks\nlike blocking code, while still allowing for concurrency even within a single\nthread.\n\n# Goals\n\n* **Minimal**: Wraps the `IO_Uring` library in the most lightweight way\n  possible. This means it still uses the `IO_Uring` data structures in many\n  places, like for completion queue entries. There are no additional internal\n  data structures other than the submission queue and completion queue used by\n  the `IO_Uring` library. This means there's no heap allocation. It also relies\n  entirely on kernel functionality for timeouts and cancellation.\n* **Complete**: You should be able to do anything with this that you could do\n  with `IO_Uring`.\n* **Easy to use**: Because of the use of coroutines, code written with this\n  library looks almost identical to blocking code. In addition, operation\n  timeouts and cancellation support is integrated into the API for all operations.\n* **Performant**: The library does no heap allocation and there's minimal\n  additional logic on top of `suspend`/`resume`. \n\n# Installation \n\nThis library integrates with the [zigmod](https://github.com/nektro/zigmod)\npackage manager. If you've installed `zigmod`, you can add a line like the\nfollowing to your `root_dependencies` in the `zig.mod` file of your project \nand run `zigmod fetch`:\n```yml\nroot_dependencies:\n  - ...\n  - src: git https://github.com/saltzm/async_io_uring.git\n```\n\nYou'll then be able to include `async_io_uring.zig` by doing something like:\n```zig\nconst io = @import(\"async_io_uring\");\n```\n\nThe examples directory is structured roughly as you might structure a project\nthat uses `async_io_uring`, with a working `zig.mod` file and `build.zig` that\ncan serve as examples.\n\nYou'll also need a Linux kernel version that supports all of the `io_uring`\nfeatures you'd like to use. (All testing was done on version 5.13.0.)\n\n# Example usage\n\n## Echo client\n\nJumping right into a realistic example, the following is a snippet of code from\nthe echo client in the `examples` directory:\n\n```zig\nconst io = @import(\"async_io_uring\");\n\npub fn run_client(ring: *AsyncIOUring) !void {\n    // Make a data structure that lets us do async file I/O with the same\n    // syntax as `std.debug.print`.\n    var writer = try AsyncWriter.init(ring, std.io.getStdErr().handle);\n\n    // Address of the echo server.\n    const address = try net.Address.parseIp4(\"127.0.0.1\", 3131);\n\n    // Open a socket for connecting to the server.\n    const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);\n    defer {\n        _ = ring.close(server, null, null) catch {\n            std.os.exit(1);\n        };\n    }\n\n    // Connect to the server.\n    _ = try ring.connect(server, \u0026address.any, address.getOsSockLen(), null, null);\n\n    const stdin_file = std.io.getStdIn();\n    const stdin_fd = stdin_file.handle;\n    var input_buffer: [256]u8 = undefined;\n\n    while (true) {\n        // Prompt the user for input.\n        try writer.print(\"Input: \", .{});\n\n        const read_timeout = os.linux.kernel_timespec{ .tv_sec = 10, .tv_nsec = 0 };\n        // Read a line from stdin with a 10 second timeout.\n        // This is the more verbose API - you can also do `ring.read`.\n        const read_cqe = ring.do(\n            io.Read{ .fd = stdin_fd, .buffer = input_buffer[0..], .offset = input_buffer.len },\n            io.Timeout{ .ts = \u0026read_timeout, .flags = 0 },\n            null,\n        ) catch |err| {\n            if (err == error.Cancelled) {\n                try writer.print(\"\\nTimed out waiting for input, exiting...\\n\", .{});\n                return;\n            } else return err;\n        };\n\n        const num_bytes_read = @intCast(usize, read_cqe.res);\n\n        // Send it to the server.\n        _ = try ring.send(server, input_buffer[0..num_bytes_read], 0, null, null);\n\n        // Receive response.\n        const recv_cqe = try ring.recv(server, input_buffer[0..], 0, null, null);\n\n        const num_bytes_received = @intCast(usize, recv_cqe.res);\n        try writer.print(\"Received: {s}\\n\", .{input_buffer[0..num_bytes_received]});\n    }\n}\n```\n\n## Operation timeouts\n\n`AsyncIOUring` supports adding timeouts to all operations. Adding a timeout to\nan operation causes it to be cancelled after the specified timeout, returning\nan error code `error.Cancelled` if cancellation was successful.\n\nAn example from the unit tests:\n\n```zig\nfn testReadThatTimesOut(ring: *AsyncIOUring) !void {\n    var read_buffer = [_]u8{0} ** 20;\n\n    const ts = os.linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = 10000 };\n    // Try to read from stdin - there won't be any input so this should\n    // reliably time out.\n    const read_cqe = ring.do(\n        Read{ .fd = std.io.getStdIn().handle, .buffer = read_buffer[0..], .offset = 0 },\n        Timeout{ .ts = \u0026ts, .flags = 0 },\n        null,\n    );\n    try std.testing.expectEqual(read_cqe, error.Cancelled);\n}\n```\n\n## Operation cancellation\n\n`AsyncIOUring` supports cancellation for all operations. Each operation is \nidentified by an `id` that is set via a `maybe_id` \"output parameter\" in all\noperation submission functions (e.g. `read`, `send`, etc.). This `id` can then\nbe passed to `AsyncIOUring.cancel` to cancel that operation.\n\nAn example from the unit tests:\n\n```zig\nfn testReadThatIsCancelled(ring: *AsyncIOUring) !void {\n    var read_buffer = [_]u8{0} ** 20;\n\n    var op_id: u64 = undefined;\n\n    // Try to read from stdin - there won't be any input so this operation should\n    // reliably hang until cancellation.\n    var read_frame = async ring.do(\n        Read{ .fd = std.io.getStdIn().handle, .buffer = read_buffer[0..], .offset = 0 },\n        null,\n        \u0026op_id,\n    );\n\n    const cancel_cqe = try ring.cancel(op_id, 0, null, null);\n    // Expect that cancellation succeeded.\n    try std.testing.expectEqual(cancel_cqe.res, 0);\n\n    const read_cqe = await read_frame;\n    try std.testing.expectEqual(read_cqe, error.Cancelled);\n}\n```\n","funding_links":[],"categories":["Libraries"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaltzm%2Fasync_io_uring","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaltzm%2Fasync_io_uring","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaltzm%2Fasync_io_uring/lists"}