{"id":13415635,"url":"https://github.com/spacejam/rio","last_synced_at":"2025-05-14T20:07:37.223Z","repository":{"id":37286613,"uuid":"78247590","full_name":"spacejam/rio","owner":"spacejam","description":"pure rust io_uring library, built on libc, thread \u0026 async friendly, misuse resistant","archived":false,"fork":false,"pushed_at":"2022-06-27T12:02:01.000Z","size":177,"stargazers_count":953,"open_issues_count":27,"forks_count":48,"subscribers_count":37,"default_branch":"master","last_synced_at":"2025-05-12T21:32:20.378Z","etag":null,"topics":["io","io-uring","linux","rust","steamy","uring"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spacejam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"spacejam","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2017-01-06T23:52:04.000Z","updated_at":"2025-05-03T00:02:06.000Z","dependencies_parsed_at":"2022-07-14T05:50:31.338Z","dependency_job_id":null,"html_url":"https://github.com/spacejam/rio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spacejam%2Frio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spacejam%2Frio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spacejam%2Frio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spacejam%2Frio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spacejam","download_url":"https://codeload.github.com/spacejam/rio/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254219373,"owners_count":22034397,"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":["io","io-uring","linux","rust","steamy","uring"],"created_at":"2024-07-30T21:00:50.951Z","updated_at":"2025-05-14T20:07:37.138Z","avatar_url":"https://github.com/spacejam.png","language":"Rust","readme":"* [documentation](https://docs.rs/rio)\n* [chat](https://discord.gg/Z6VsXds)\n* [sponsor](https://github.com/sponsors/spacejam)\n\n# rio\n\nbindings for io_uring, the hottest\nthing to happen to linux IO in a long time.\n\n#### Soundness status\n\nrio aims to leverage Rust's compile-time checks to be\nmisuse-resistant compared to io_uring interfaces in\nother languages, but users should beware that\n**use-after-free bugs are still possible without\n`unsafe` when using rio.** `Completion` borrows the\nbuffers involved in a request, and its destructor\nblocks in order to delay the freeing of those buffers\nuntil the corresponding request has completed; but it\nis considered safe in Rust for an object's lifetime\nand borrows to end without its destructor running,\nand this can happen in various ways, including\nthrough `std::mem::forget`. Be careful not to let\ncompletions leak in this way, and if Rust's soundness\nguarantees are important to you, you may want to\navoid this crate.\n\n#### Innovations\n\n* only relies on libc, no need for c/bindgen to\n  complicate things, nobody wants that\n* the completions work great with threads or an\n  async runtime (`Completion` implements Future)\n* uses Rust marker traits to guarantee that a buffer will never\n  be written into unless it is writable memory. (prevents\n  you from trying to write data into static read-only memory)\n* no need to mess with `IoSlice` / `libc::iovec` directly.\n  rio maintains these in the background for you.\n* If left to its own devices, io_uring will allow you to\n  submit more IO operations than would actually fit in\n  the completion queue, allowing completions to be dropped\n  and causing leaks of any userspace thing waiting for\n  the completion. rio exerts backpressure on submitters\n  when the number of in-flight requests reaches this\n  threshold, to guarantee that no completions will\n  be dropped due to completion queue overflow.\n* rio will handle submission queue submissions\n  automatically. If you start waiting for a\n  `Completion`, rio will make sure that we\n  have already submitted at least this request\n  to the kernel. Other io_uring libraries force\n  you to handle this manually, which is another\n  possible source of misuse.\n\nThis is intended to be the core of [sled's](http://sled.rs) writepath.\nIt is built with a specific high-level\napplication in mind: a high performance storage\nengine and replication system.\n\n#### What's io_uring?\n\nio_uring is the biggest thing to happen to the\nlinux kernel in a very long time. It will change\neverything. Anything that uses epoll right now\nwill be rewritten to use io_uring if it wants\nto stay relevant. It started as a way to do real\nasync disk IO without needing to use O_DIRECT, but\nits scope has expanded and it will continue to support\nmore and more kernel functionality over time due to\nits ability to batch large numbers different syscalls.\nIn kernel 5.5 support is added for more networking\noperations like `accept(2)`, `sendmsg(2)`, and `recvmsg(2)`.\nIn 5.6 support is being added for `recv(2)` and `send(2)`.\nio_uring [has been measured to dramatically outperform\nepoll-based networking, with io_uring outperforming\nepoll-based setups more and more under heavier load](https://twitter.com/markpapadakis/status/1216978559601926145).\nI started rio to gain an early deep understanding of this\namazing new interface, so that I could use it ASAP and\nresponsibly with [sled](http://sled.rs).\n\nio_uring unlocks the following kernel features:\n\n* fully-async interface for a growing number of syscalls\n* async disk IO without using O_DIRECT as you have\n  to do with AIO\n* batching hundreds of disk and network IO operations\n  into a single syscall, which is especially wonderful\n  in a post-meltdown/spectre world where our syscalls have\n  [dramatically slowed down](http://www.brendangregg.com/blog/2018-02-09/kpti-kaiser-meltdown-performance.html)\n* 0-syscall IO operation submission, if configured in\n  SQPOLL mode\n* configurable completion polling for trading CPU for\n  low latency\n* Allows expression of sophisticated 0-copy broadcast\n  semantics, similar to splice(2) or sendfile(2) but\n  working with many file-like objects without ever\n  needing to bounce memory and mappings into userspace\n  en-route.\n* Allows IO buffers and file descriptors to be registered\n  for cheap reuse (remapping buffers and file descriptors\n  for use in the kernel has a significant cost).\n\nTo read more about io_uring, check out:\n\n* [Ringing in a new asynchronous I/O API](https://lwn.net/Articles/776703/)\n* [Efficient IO with io_uring](https://kernel.dk/io_uring.pdf)\n* [What’s new with io_uring](https://kernel.dk/io_uring-whatsnew.pdf)\n* Follow [Jens Axboe on Twitter](https://twitter.com/axboe) to follow dev progress\n\nFor some slides with interesting io_uring performance results,\ncheck out slides 43-53 of [this presentation deck by Jens](https://www.slideshare.net/ennael/kernel-recipes-2019-faster-io-through-iouring).\n\n#### why not use those other Rust io_uring libraries?\n\n* they haven't copied `rio`'s features yet, which you pretty much\n  have to use anyway to responsibly use `io_uring` due to the\n  sharp edges of the API. All of the libraries I've seen\n  as of January 13 2020 are totally easy to overflow the\n  completion queue with, as well as easy to express\n  use-after-frees with, don't seem to be async-friendly,\n  etc...\n\n#### examples that will be broken in the next day or two\n\nasync tcp echo server:\n\n```rust\nuse std::{\n    io::self,\n    net::{TcpListener, TcpStream},\n};\n\nasync fn proxy(ring: \u0026rio::Rio, a: \u0026TcpStream, b: \u0026TcpStream) -\u003e io::Result\u003c()\u003e {\n    let buf = vec![0_u8; 512];\n    loop {\n        let read_bytes = ring.read_at(a, \u0026buf, 0).await?;\n        let buf = \u0026buf[..read_bytes];\n        ring.write_at(b, \u0026buf, 0).await?;\n    }\n}\n\nfn main() -\u003e io::Result\u003c()\u003e {\n    let ring = rio::new()?;\n    let acceptor = TcpListener::bind(\"127.0.0.1:6666\")?;\n\n    extreme::run(async {\n        // kernel 5.5 and later support TCP accept\n        loop {\n            let stream = ring.accept(\u0026acceptor).await?;\n            dbg!(proxy(\u0026ring, \u0026stream, \u0026stream).await);\n        }\n    })\n}\n```\n\nfile reading:\n\n```rust\nlet ring = rio::new().expect(\"create uring\");\nlet file = std::fs::open(\"file\").expect(\"openat\");\nlet data: \u0026mut [u8] = \u0026mut [0; 66];\nlet completion = ring.read_at(\u0026file, \u0026mut data, at);\n\n// if using threads\ncompletion.wait()?;\n\n// if using async\ncompletion.await?\n```\n\nfile writing:\n\n```rust\nlet ring = rio::new().expect(\"create uring\");\nlet file = std::fs::create(\"file\").expect(\"openat\");\nlet to_write: \u0026[u8] = \u0026[6; 66];\nlet completion = ring.write_at(\u0026file, \u0026to_write, at);\n\n// if using threads\ncompletion.wait()?;\n\n// if using async\ncompletion.await?\n```\n\nspeedy O_DIRECT shi0t (try this at home / run the o_direct example)\n\n```rust\nuse std::{\n    fs::OpenOptions, io::Result,\n    os::unix::fs::OpenOptionsExt,\n};\n\nconst CHUNK_SIZE: u64 = 4096 * 256;\n\n// `O_DIRECT` requires all reads and writes\n// to be aligned to the block device's block\n// size. 4096 might not be the best, or even\n// a valid one, for yours!\n#[repr(align(4096))]\nstruct Aligned([u8; CHUNK_SIZE as usize]);\n\nfn main() -\u003e Result\u003c()\u003e {\n    // start the ring\n    let ring = rio::new()?;\n\n    // open output file, with `O_DIRECT` set\n    let file = OpenOptions::new()\n        .read(true)\n        .write(true)\n        .create(true)\n        .truncate(true)\n        .custom_flags(libc::O_DIRECT)\n        .open(\"file\")?;\n\n    let out_buf = Aligned([42; CHUNK_SIZE as usize]);\n    let out_slice: \u0026[u8] = \u0026out_buf.0;\n\n    let in_buf = Aligned([42; CHUNK_SIZE as usize]);\n    let in_slice: \u0026[u8] = \u0026in_buf.0;\n\n    let mut completions = vec![];\n\n    for i in 0..(10 * 1024) {\n        let at = i * CHUNK_SIZE;\n\n        // By setting the `Link` order,\n        // we specify that the following\n        // read should happen after this\n        // write.\n        let write = ring.write_at_ordered(\n            \u0026file,\n            \u0026out_slice,\n            at,\n            rio::Ordering::Link,\n        );\n        completions.push(write);\n\n        let read = ring.read_at(\u0026file, \u0026in_slice, at);\n        completions.push(read);\n    }\n\n    for completion in completions.into_iter() {\n        completion.wait()?;\n    }\n\n    Ok(())\n}\n```\n","funding_links":["https://github.com/sponsors/spacejam"],"categories":["Rust","Libraries","rust"],"sub_categories":["Rust"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspacejam%2Frio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspacejam%2Frio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspacejam%2Frio/lists"}