Repository: withoutboats/ringbahn
Branch: master
Commit: 08e6464e64d8
Files: 55
Total size: 99.4 KB
Directory structure:
gitextract_7bu16q06/
├── .gitignore
├── Cargo.toml
├── README.md
├── examples/
│ ├── copy-file.rs
│ ├── echo-client.rs
│ ├── echo-server.rs
│ └── read-event.rs
├── props.txt
├── src/
│ ├── buf.rs
│ ├── drive/
│ │ ├── demo.rs
│ │ └── mod.rs
│ ├── event/
│ │ ├── accept.rs
│ │ ├── close.rs
│ │ ├── connect.rs
│ │ ├── epoll_ctl.rs
│ │ ├── fadvise.rs
│ │ ├── fallocate.rs
│ │ ├── files_update.rs
│ │ ├── fsync.rs
│ │ ├── mod.rs
│ │ ├── openat.rs
│ │ ├── provide_buffers.rs
│ │ ├── read.rs
│ │ ├── readv.rs
│ │ ├── recv.rs
│ │ ├── send.rs
│ │ ├── splice.rs
│ │ ├── statx.rs
│ │ ├── timeout.rs
│ │ ├── write.rs
│ │ └── writev.rs
│ ├── fs.rs
│ ├── io.rs
│ ├── lib.rs
│ ├── net/
│ │ ├── listener.rs
│ │ ├── mod.rs
│ │ └── stream.rs
│ ├── ring/
│ │ ├── cancellation.rs
│ │ ├── completion.rs
│ │ └── mod.rs
│ ├── submission.rs
│ └── unix/
│ ├── listener.rs
│ ├── mod.rs
│ └── stream.rs
└── tests/
├── basic-read.rs
├── basic-readv.rs
├── basic-write.rs
├── basic-writev.rs
├── file-close.rs
├── file-read.rs
├── file-seek.rs
├── file-write.rs
├── println.rs
├── registered-fd.rs
└── stdio.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/target
Cargo.lock
================================================
FILE: Cargo.toml
================================================
[package]
name = "ringbahn"
version = "0.0.0-experimental.3"
authors = ["Without Boats <woboats@gmail.com>"]
description = "an experimental safe API for io-uring"
repository = "https://github.com/withoutboats/ringbahn"
license = "MIT OR Apache-2.0"
edition = "2018"
[dependencies]
futures-io = "0.3.5"
futures-core = "0.3.5"
parking_lot = "0.10.2"
once_cell = "1.3.1"
libc = "0.2.71"
uring-sys = "0.7.4"
nix = "0.18.0"
iou = "0.3.3"
either = "1.6.1"
event-listener = "2.5.1"
[dev-dependencies]
tempfile = "3.1.0"
futures = { version = "0.3.5", features = ["thread-pool"] }
================================================
FILE: README.md
================================================
# ringbahn - a safe interface to io-uring
The Berlin Ringbahn is a double-tracked commuter rail line which forms a complete ring around the
center of the city of Berlin. Similarly, io-uring is a new interface for asynchronous IO with the
Linux kernel built on a double ring buffer data structure.
ringbahn is an attempt to define a good interface to perform IO on io-uring with these properties:
- 100% memory safe
- Completely non-blocking
- Ergonomic with async/await syntax
- A zero-cost abstraction with minimal overhead
- Abstracted over different patterns for driving the io-uring instance
- Misuse-resistant with a well implemented driver
**The current version of ringbahn is highly experimental and insufficiently hardened for production
use.** You are strongly recommended not to deploy the code under the current version. But tests, bug
reports, user feedback, and other experiments are all welcome at this stage.
Though ringbahn is a prototype, it demonstrates that a safe, ergonomic, efficient, and flexible
interface to io-uring is possible in Rust. It should be a goal for the Rust community not only to
have an adequate interface for io-uring, but to have the *best* interface for io-uring.
## License
ringbahn is licensed under your choice of MIT or Apache-2.0.
================================================
FILE: examples/copy-file.rs
================================================
use futures::io::AsyncReadExt;
use futures::io::AsyncWriteExt;
use ringbahn::fs::File;
fn main() {
futures::executor::block_on(async move {
let mut input: File = File::open("props.txt").await.unwrap();
let mut output: File = File::create("test.txt").await.unwrap();
let mut buf = vec![0; 1024];
let len = input.read(&mut buf).await.unwrap();
output.write(&mut buf[0..len]).await.unwrap();
output.flush().await.unwrap();
});
}
================================================
FILE: examples/echo-client.rs
================================================
use ringbahn::net::TcpStream;
use std::io::{self, BufRead, Write};
use futures::io::{AsyncBufReadExt, AsyncWriteExt};
use futures::executor::block_on;
fn main() {
block_on(async move {
let mut stream = TcpStream::connect(("127.0.0.1", 7878)).await.unwrap();
let stdin = io::stdin();
let stdout = io::stdout();
let mut stdout = stdout;
let mut buf = String::new();
for line in stdin.lock().lines() {
let line = line.unwrap();
stream.write_all(line.as_bytes()).await.unwrap();
stream.read_line(&mut buf).await.unwrap();
stdout.write_all(buf.as_bytes()).unwrap();
buf.clear();
}
})
}
================================================
FILE: examples/echo-server.rs
================================================
use ringbahn::net::TcpListener;
use futures::StreamExt;
use futures::io::{AsyncReadExt, AsyncWriteExt};
use futures::executor::{ThreadPool, block_on};
fn main() {
let mut listener = TcpListener::bind(("127.0.0.1", 7878)).unwrap();
println!("listening on port 7878");
let mut incoming = listener.incoming();
let pool = ThreadPool::new().unwrap();
block_on(async move {
while let Some(stream) = incoming.next().await {
println!("recieved connection");
let (mut stream, _) = stream.unwrap();
pool.spawn_ok(async move {
loop {
let mut buf = [0; 8096];
let n = stream.read(&mut buf[..]).await.unwrap();
println!("read {} bytes", n);
buf[n] = b'\n';
stream.write_all(&buf[0..n + 1]).await.unwrap();
println!("write {} bytes", n + 1);
}
});
}
});
}
================================================
FILE: examples/read-event.rs
================================================
use ringbahn::*;
use std::fs::{metadata, File};
use std::io;
use std::os::unix::io::AsRawFd;
fn main() -> io::Result<()> {
let driver = drive::demo::driver();
let meta = metadata("props.txt")?;
let file = File::open("props.txt")?;
let event = event::Read {
fd: file.as_raw_fd(),
buf: vec![0; meta.len() as usize].into(),
offset: 0
};
let submission = Submission::new(event, driver.clone());
futures::executor::block_on(async move {
let (event, result) = submission.await;
let bytes_read = result? as usize;
let content = String::from_utf8_lossy(&event.buf[0..bytes_read]).to_string();
ringbahn::print!(driver, "{}", content).await;
Ok(())
})
}
================================================
FILE: props.txt
================================================
But this formidable power of death - and this is perhaps what accounts for part of its force and
the cynicism with which it has so greatly expanded its limits - now presents itself as the
counterpart of a power that exerts a positive influence on life, that endeavors to administer,
optimize, and multiply it, subjecting it to precise controls and comprehensive regulations. Wars
are no longer waged in the name of a sovereign who must be defended; they are waged on behalf of
the existence of everyone; entire populations are mobilized for the purpose of wholesale slaughter
in the name of life necessity: massacres have become vital. It is as managers of life and survival,
of bodies and the race, that so many regimes have been able to wage so many wars, causing so many
men to be killed.
================================================
FILE: src/buf.rs
================================================
use std::cmp;
use std::io;
use std::task::Poll;
use futures_core::ready;
use crate::ring::Cancellation;
#[derive(Default, Debug)]
pub struct Buffer {
data: Option<Box<[u8]>>,
pos: u32,
cap: u32,
}
impl Buffer {
pub fn buffered_from_read(&self) -> &[u8] {
self.data.as_deref().map_or(&[], |data| &data[self.pos as usize..self.cap as usize])
}
pub fn fill_buf(&mut self, fill: impl FnOnce(&mut [u8]) -> Poll<io::Result<u32>>)
-> Poll<io::Result<&[u8]>>
{
const CAPACITY: usize = 4096 * 2;
if self.pos >= self.cap {
if self.data.is_none() {
self.data = Some(vec![0; CAPACITY].into_boxed_slice());
}
self.cap = ready!(fill(self.data.as_deref_mut().unwrap()))?;
self.pos = 0;
}
Poll::Ready(Ok(self.buffered_from_read()))
}
pub fn consume(&mut self, amt: usize) {
self.pos = cmp::min(self.pos + amt as u32, self.cap);
}
pub fn clear(&mut self) {
self.pos = 0;
self.cap = 0;
}
pub fn into_boxed_slice(self) -> Option<Box<[u8]>> {
self.data
}
pub fn cancellation(&mut self) -> Cancellation {
Cancellation::from(self.data.take())
}
}
================================================
FILE: src/drive/demo.rs
================================================
//! A demo driver for experimentation purposes
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::sync::Once;
use std::task::{Poll, Context};
use std::thread;
use event_listener::*;
use futures_core::ready;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
const ENTRIES: u32 = 32;
use super::{Drive, Completion};
use iou::*;
type Queues = (
Mutex<SubmissionQueue<'static>>,
Mutex<CompletionQueue<'static>>,
Registrar<'static>,
Event,
);
static QUEUES: Lazy<Queues> = Lazy::new(init);
/// The driver handle
pub struct DemoDriver {
listener: Option<EventListener>,
}
impl DemoDriver {
fn poll_submit_inner(&mut self, ctx: &mut Context<'_>, sq: &mut SubmissionQueue<'_>)
-> Poll<io::Result<u32>>
{
start_completion_thread();
if let Some(listener) = &mut self.listener {
ready!(Pin::new(listener).poll(ctx));
}
match sq.submit() {
Ok(n) => Poll::Ready(Ok(n)),
Err(err) => {
if err.raw_os_error().map_or(false, |code| code == libc::EBUSY) {
self.listener = Some(QUEUES.3.listen());
Poll::Pending
} else {
Poll::Ready(Err(err))
}
}
}
}
}
impl Default for DemoDriver {
fn default() -> Self {
driver()
}
}
impl Clone for DemoDriver {
fn clone(&self) -> DemoDriver {
driver()
}
}
impl Drive for DemoDriver {
fn poll_prepare<'cx>(
mut self: Pin<&mut Self>,
ctx: &mut Context<'cx>,
count: u32,
prepare: impl FnOnce(SQEs<'_>, &mut Context<'cx>) -> Completion<'cx>,
) -> Poll<Completion<'cx>> {
let mut sq = QUEUES.0.lock();
loop {
match sq.prepare_sqes(count) {
Some(sqs) => return Poll::Ready(prepare(sqs, ctx)),
None => {
let _ = ready!(self.poll_submit_inner(ctx, &mut *sq));
}
}
}
}
fn poll_submit(
mut self: Pin<&mut Self>,
ctx: &mut Context<'_>,
) -> Poll<io::Result<u32>> {
self.poll_submit_inner(ctx, &mut *QUEUES.0.lock())
}
}
/// Construct a demo driver handle
pub fn driver() -> DemoDriver {
DemoDriver {
listener: None,
}
}
/// Access the registrar
///
/// This will return `None` if events have already been submitted to the driver. The Demo Driver
/// currently only allows registering IO objects prior to submitting IO.
pub fn registrar() -> Option<&'static Registrar<'static>> {
if !STARTED_COMPLETION_THREAD.is_completed() {
Some(&QUEUES.2)
} else {
None
}
}
fn init() -> Queues {
let flags = SetupFlags::empty();
let features = SetupFeatures::NODROP;
let ring = Box::new(IoUring::new_with_flags(ENTRIES, flags, features).unwrap());
let ring = Box::leak(ring);
let (sq, cq, reg) = ring.queues();
(Mutex::new(sq), Mutex::new(cq), reg, Event::new())
}
static STARTED_COMPLETION_THREAD: Once = Once::new();
fn start_completion_thread() {
STARTED_COMPLETION_THREAD.call_once(|| { thread::spawn(move || {
let mut cq = QUEUES.1.lock();
while let Ok(cqe) = cq.wait_for_cqe() {
let mut ready = cq.ready() as usize + 1;
QUEUES.3.notify_additional(ready);
super::complete(cqe);
ready -= 1;
while let Some(cqe) = cq.peek_for_cqe() {
if ready == 0 {
ready = cq.ready() as usize + 1;
QUEUES.3.notify_additional(ready);
}
super::complete(cqe);
ready -= 1;
}
debug_assert!(ready == 0);
}
}); });
}
================================================
FILE: src/drive/mod.rs
================================================
//! Drive IO on io-uring
pub mod demo;
use std::io;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use crate::ring;
use crate::{Submission, Event};
use iou::{SQE, SQEs};
pub use crate::ring::completion::complete;
/// A completion which will be used to wake the task waiting on this event.
///
/// This type is opaque to users of ringbahn. It is constructed by the callback passed to
/// [Drive::poll_prepare].
pub struct Completion<'cx> {
pub(crate) real: ring::Completion,
marker: PhantomData<fn(&'cx ()) -> &'cx ()>,
}
impl<'cx> Completion<'cx> {
pub(crate) fn new(mut sqe: SQE<'_>, _sqes: SQEs<'_>, cx: &mut Context<'cx>) -> Completion<'cx> {
let real = ring::Completion::new(cx.waker().clone());
unsafe {
sqe.set_user_data(real.addr());
}
Completion { real, marker: PhantomData }
}
}
/// Implemented by drivers for io-uring.
///
/// The type that implements `Drive` is used to prepare and submit IO events to an io-uring
/// instance. Paired with a piece of code which processes completions, it can run IO on top of
/// io-uring.
pub trait Drive {
/// Prepare an event on the submission queue.
///
/// The implementer is responsible for provisioning an [`iou::SQE`] from the
/// submission queue. Once an SQE is available, the implementer should pass it to the
/// `prepare` callback, which constructs a [`Completion`], and return that `Completion` to the
/// caller.
///
/// If the driver is not ready to receive more events, it can return `Poll::Pending`. If it
/// does, it must register a waker to wake the task when more events can be prepared, otherwise
/// this method will not be called again. This allows the driver to implement backpressure.
///
/// Drivers which call `prepare` but do not return the completion it gives are incorrectly
/// implemented. This will lead ringbahn to panic.
fn poll_prepare<'cx>(
self: Pin<&mut Self>,
ctx: &mut Context<'cx>,
count: u32,
prepare: impl FnOnce(SQEs<'_>, &mut Context<'cx>) -> Completion<'cx>,
) -> Poll<Completion<'cx>>;
/// Suggest to submit all of the events on the submission queue.
///
/// The implementer is responsible for determining how and when events are submitted to the
/// kernel to complete. It is valid for this function to do nothing at all; this function just
/// informs the driver that the user program is waiting for its prepared events to be
/// submitted and completed.
///
/// If the implementation is not ready to submit, but wants to be called again to try later, it
/// can return `Poll::Pending`. If it does, it must register a waker to wake the task when it
/// would be appropriate to try submitting again.
///
/// It is also valid not to submit an event but not to register a waker to try again, in which
/// case the appropriate response would be to return `Ok(0)`. This indicates to the caller that
/// the submission step is complete, whether or not actual IO was performed by the driver.
fn poll_submit(
self: Pin<&mut Self>,
ctx: &mut Context<'_>,
) -> Poll<io::Result<u32>>;
fn submit<E: Event>(self, event: E) -> Submission<E, Self> where Self: Sized {
Submission::new(event, self)
}
}
================================================
FILE: src/event/accept.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::sqe::{SockFlag, SockAddrStorage};
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
pub struct Accept<FD = RawFd> {
pub addr: Option<Box<SockAddrStorage>>,
pub fd: FD,
pub flags: SockFlag,
}
impl<FD: UringFd + Copy> Event for Accept<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_accept(self.fd, self.addr.as_deref_mut(), self.flags);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).addr)
}
}
================================================
FILE: src/event/close.rs
================================================
use std::os::unix::io::RawFd;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs};
pub struct Close<FD = RawFd> {
pub fd: FD,
}
impl<FD: UringFd + Copy> Event for Close<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_close(self.fd);
sqe
}
}
================================================
FILE: src/event/connect.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::sqe::SockAddr;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
pub struct Connect<FD = RawFd> {
pub fd: FD,
pub addr: Box<SockAddr>,
}
impl<FD: UringFd + Copy> Event for Connect<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_connect(self.fd, &mut *self.addr);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).addr)
}
}
================================================
FILE: src/event/epoll_ctl.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::sqe::{EpollOp, EpollEvent};
use super::{Event, SQE, SQEs, Cancellation};
pub struct EpollCtl {
pub epoll_fd: RawFd,
pub op: EpollOp,
pub fd: RawFd,
pub event: Option<Box<EpollEvent>>,
}
impl Event for EpollCtl {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_epoll_ctl(self.epoll_fd, self.op, self.fd, self.event.as_deref_mut());
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).event)
}
}
================================================
FILE: src/event/fadvise.rs
================================================
use std::os::unix::io::RawFd;
use iou::sqe::PosixFadviseAdvice;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs};
pub struct Fadvise<FD = RawFd> {
pub fd: FD,
pub offset: u64,
pub size: u64,
pub flags: PosixFadviseAdvice,
}
impl<FD: UringFd + Copy> Event for Fadvise<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_fadvise(self.fd, self.offset, self.size, self.flags);
sqe
}
}
================================================
FILE: src/event/fallocate.rs
================================================
use std::os::unix::io::RawFd;
use iou::registrar::UringFd;
use iou::sqe::FallocateFlags;
use super::{Event, SQE, SQEs};
pub struct Fallocate<FD = RawFd> {
pub fd: FD,
pub offset: u64,
pub size: u64,
pub flags: FallocateFlags,
}
impl<FD: UringFd + Copy> Event for Fallocate<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_fallocate(self.fd, self.offset, self.size, self.flags);
sqe
}
}
================================================
FILE: src/event/files_update.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use super::{Event, SQE, SQEs, Cancellation};
pub struct FilesUpdate {
pub files: Box<[RawFd]>,
pub offset: u32,
}
impl Event for FilesUpdate {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_files_update(&self.files[..], self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).files)
}
}
================================================
FILE: src/event/fsync.rs
================================================
use std::os::unix::io::RawFd;
use iou::registrar::UringFd;
use iou::sqe::FsyncFlags;
use super::{Event, SQE, SQEs};
pub struct Fsync<FD = RawFd> {
pub fd: FD,
pub flags: FsyncFlags,
}
impl<FD: UringFd + Copy> Event for Fsync<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_fsync(self.fd, self.flags);
sqe
}
}
================================================
FILE: src/event/mod.rs
================================================
//! Events that can be scheduled on io-uring with a [`Submission`](crate::Submission)
mod accept;
mod close;
mod connect;
mod epoll_ctl;
mod fadvise;
mod fallocate;
mod files_update;
mod fsync;
mod openat;
mod provide_buffers;
mod read;
mod readv;
mod recv;
mod send;
mod splice;
mod statx;
mod timeout;
mod write;
mod writev;
use std::mem::ManuallyDrop;
use iou::{SQE, SQEs};
use crate::ring::Cancellation;
pub use accept::Accept;
pub use close::Close;
pub use connect::Connect;
pub use epoll_ctl::EpollCtl;
pub use fadvise::Fadvise;
pub use fallocate::Fallocate;
pub use files_update::FilesUpdate;
pub use fsync::Fsync;
pub use openat::OpenAt;
pub use provide_buffers::{ProvideBuffers, RemoveBuffers};
pub use read::{Read, ReadFixed};
pub use readv::ReadVectored;
pub use recv::Recv;
pub use send::Send;
pub use splice::Splice;
pub use statx::Statx;
pub use timeout::{Timeout, StaticTimeout};
pub use write::{Write, WriteFixed};
pub use writev::WriteVectored;
/// An IO event that can be scheduled on an io-uring driver.
///
/// ## Safety
///
/// Event is a safe trait with two unsafe methods. It's important to understand that when
/// implementing an unsafe method, the code author implementing that method is allowed to assume
/// certain additional invariants will be upheld by all callers. It is the caller's responsibility
/// to ensure those invariants are upheld, not the implementer. However, any unsafe operations
/// performed inside of the method must be safe under those invariants and any other invariants the
/// implementer has upheld. The implementer is not allowed to add any additional invariants that
/// the caller must uphold that are not required by the trait.
pub trait Event {
fn sqes_needed(&self) -> u32;
/// Prepare an event to be submitted using the SQE argument.
///
/// ## Safety
///
/// When this method is called, these guarantees will be maintained by the caller:
///
/// The data contained by this event will not be accessed again by this program until one of
/// two things happen:
/// - The event being prepared has been completed by the kernel, in which case ownership of
/// this event will be passed back to users of this library.
/// - Interest in the event is cancelled, in which case `Event::cancel` will be called and the
/// event's destructor will not run.
///
/// In essence implementing prepare, users can write code ass if any heap addresses passed to
/// the kernel have passed ownership of that data to the kernel for the time that the event is
/// completed.
unsafe fn prepare<'a>(&mut self, sqs: &mut SQEs<'a>) -> SQE<'a>;
/// Return the cancellation callback for this event.
///
/// If this event is cancelled, this callback will be stored with the completion to be dropped
/// when the IO event completes. This way, any managed resources passed to the kernel (like
/// buffers) can be cleaned up once the kernel no longer needs them.
fn cancel(_: ManuallyDrop<Self>) -> Cancellation where Self: Sized {
Cancellation::from(())
}
}
================================================
FILE: src/event/openat.rs
================================================
use std::ffi::CString;
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use iou::sqe::{Mode, OFlag};
use super::{Event, SQE, SQEs, Cancellation};
pub struct OpenAt {
pub path: CString,
pub dir_fd: RawFd,
pub flags: OFlag,
pub mode: Mode,
}
impl OpenAt {
pub fn without_dir(path: impl AsRef<Path>, flags: OFlag, mode: Mode) -> OpenAt {
let path = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
OpenAt { path, dir_fd: libc::AT_FDCWD, flags, mode }
}
}
impl Event for OpenAt {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_openat(self.dir_fd, &*self.path, self.flags, self.mode);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).path)
}
}
================================================
FILE: src/event/provide_buffers.rs
================================================
use std::mem::ManuallyDrop;
use iou::sqe::BufferGroupId;
use super::{Event, SQE, SQEs, Cancellation};
pub struct ProvideBuffers {
pub bufs: Box<[u8]>,
pub count: u32,
pub group: BufferGroupId,
pub index: u32,
}
impl Event for ProvideBuffers {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_provide_buffers(&mut self.bufs[..], self.count, self.group, self.index);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).bufs)
}
}
pub struct RemoveBuffers {
pub count: u32,
pub group: BufferGroupId,
}
impl Event for RemoveBuffers {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_remove_buffers(self.count, self.group);
sqe
}
}
================================================
FILE: src/event/read.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::registrar::{UringFd, RegisteredBuf};
use super::{Event, SQE, SQEs, Cancellation};
/// A basic read event.
pub struct Read<FD = RawFd> {
pub fd: FD,
pub buf: Box<[u8]>,
pub offset: u64,
}
impl<FD: UringFd + Copy> Event for Read<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_read(self.fd, &mut self.buf[..], self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).buf)
}
}
pub struct ReadFixed<FD = RawFd> {
pub fd: FD,
pub buf: RegisteredBuf,
pub offset: u64,
}
impl<FD: UringFd + Copy> Event for ReadFixed<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_read(self.fd, self.buf.as_mut(), self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).buf)
}
}
================================================
FILE: src/event/readv.rs
================================================
use std::io::IoSliceMut;
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
/// A `readv` event.
pub struct ReadVectored<FD = RawFd> {
pub fd: FD,
pub bufs: Box<[Box<[u8]>]>,
pub offset: u64,
}
impl<FD> ReadVectored<FD> {
fn as_iovecs(buffers: &mut [Box<[u8]>]) -> &mut [IoSliceMut] {
// Unsafe contract:
// This pointer cast is defined behaviour because Box<[u8]> (wide pointer)
// is currently ABI compatible with libc::iovec.
//
// Then, libc::iovec is guaranteed ABI compatible with IoSliceMut on Unix:
// https://doc.rust-lang.org/beta/std/io/struct.IoSliceMut.html
//
// We are relying on the internals of Box<[u8]>, but this is such a
// foundational part of Rust it's unlikely the data layout would change
// without warning.
//
// Pointer cast expression adapted from the "Turning a &mut T into an &mut U"
// example of: https://doc.rust-lang.org/std/mem/fn.transmute.html#alternatives
unsafe { &mut *(buffers as *mut [Box<[u8]>] as *mut [IoSliceMut]) }
}
}
impl<FD: UringFd + Copy> Event for ReadVectored<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_read_vectored(self.fd, Self::as_iovecs(&mut self.bufs[..]), self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).bufs)
}
}
================================================
FILE: src/event/recv.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::sqe::MsgFlags;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
pub struct Recv<FD = RawFd> {
pub fd: FD,
pub buf: Box<[u8]>,
pub flags: MsgFlags,
}
impl<FD: UringFd + Copy> Event for Recv<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_recv(self.fd, &mut self.buf[..], self.flags);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).buf)
}
}
================================================
FILE: src/event/send.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::sqe::MsgFlags;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
pub struct Send<FD = RawFd> {
pub fd: FD,
pub buf: Box<[u8]>,
pub flags: MsgFlags,
}
impl<FD: UringFd + Copy> Event for Send<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_send(self.fd, &self.buf[..], self.flags);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).buf)
}
}
================================================
FILE: src/event/splice.rs
================================================
use std::os::unix::io::RawFd;
use iou::sqe::SpliceFlags;
use super::{Event, SQE, SQEs};
pub struct Splice {
pub fd_in: RawFd,
pub off_in: i64,
pub fd_out: RawFd,
pub off_out: i64,
pub bytes: u32,
pub flags: SpliceFlags,
}
impl Event for Splice {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_splice(self.fd_in, self.off_in, self.fd_out, self.off_out, self.bytes, self.flags);
sqe
}
}
================================================
FILE: src/event/statx.rs
================================================
use std::ffi::CString;
use std::mem::{self, ManuallyDrop};
use std::os::unix::io::RawFd;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use iou::sqe::{StatxFlags, StatxMode};
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
pub struct Statx<FD = RawFd> {
pub dir_fd: FD,
pub path: CString,
pub flags: StatxFlags,
pub mask: StatxMode,
pub statx: Box<libc::statx>,
}
impl Statx {
pub fn without_dir(path: impl AsRef<Path>, flags: StatxFlags, mask: StatxMode) -> Statx {
let path = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let statx = unsafe { Box::new(mem::zeroed()) };
Statx { path, dir_fd: libc::AT_FDCWD, flags, mask, statx }
}
}
impl<FD: UringFd> Statx<FD> {
pub fn without_path(fd: FD, mut flags: StatxFlags, mask: StatxMode) -> Statx<FD> {
unsafe {
// TODO don't allocate? Use Cow? Use NULL?
let path = CString::new("").unwrap();
let statx = Box::new(mem::zeroed());
flags.insert(StatxFlags::AT_EMPTY_PATH);
Statx { dir_fd: fd, path, flags, mask, statx }
}
}
}
impl<FD: UringFd + Copy> Event for Statx<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_statx(self.dir_fd, self.path.as_c_str(), self.flags, self.mask, &mut *self.statx);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
let this = ManuallyDrop::into_inner(this);
Cancellation::from((this.statx, this.path))
}
}
================================================
FILE: src/event/timeout.rs
================================================
use std::mem::ManuallyDrop;
use std::time::Duration;
use super::{Event, SQE, SQEs, Cancellation};
use iou::sqe::TimeoutFlags;
pub struct StaticTimeout {
ts: uring_sys::__kernel_timespec,
events: u32,
flags: TimeoutFlags,
}
impl StaticTimeout {
pub const fn new(duration: Duration, events: u32, flags: TimeoutFlags) -> StaticTimeout {
StaticTimeout {
ts: timespec(duration),
events, flags,
}
}
}
impl Event for &'static StaticTimeout {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_timeout(&self.ts, self.events, self.flags);
sqe
}
}
pub struct Timeout {
ts: Box<uring_sys::__kernel_timespec>,
events: u32,
flags: TimeoutFlags,
}
impl Timeout {
pub fn new(duration: Duration, events: u32, flags: TimeoutFlags) -> Timeout {
Timeout {
ts: Box::new(timespec(duration)),
events, flags,
}
}
}
impl Event for Timeout {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_timeout(&*self.ts, self.events, self.flags);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).ts)
}
}
const fn timespec(duration: Duration) -> uring_sys::__kernel_timespec {
uring_sys::__kernel_timespec {
tv_sec: duration.as_secs() as i64,
tv_nsec: duration.subsec_nanos() as _,
}
}
================================================
FILE: src/event/write.rs
================================================
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::registrar::{UringFd, RegisteredBuf};
use super::{Event, SQE, SQEs, Cancellation};
/// A basic write event.
pub struct Write<FD = RawFd> {
pub fd: FD,
pub buf: Box<[u8]>,
pub offset: u64,
}
impl<FD: UringFd + Copy> Event for Write<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_write(self.fd, &self.buf[..], self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).buf)
}
}
pub struct WriteFixed<FD = RawFd> {
pub fd: FD,
pub buf: RegisteredBuf,
pub offset: u64,
}
impl<FD: UringFd + Copy> Event for WriteFixed<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_write(self.fd, self.buf.as_ref(), self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).buf)
}
}
================================================
FILE: src/event/writev.rs
================================================
use std::io::IoSlice;
use std::mem::ManuallyDrop;
use std::os::unix::io::RawFd;
use iou::registrar::UringFd;
use super::{Event, SQE, SQEs, Cancellation};
/// A `writev` event.
pub struct WriteVectored<FD = RawFd> {
pub fd: FD,
pub bufs: Box<[Box<[u8]>]>,
pub offset: u64,
}
impl<FD> WriteVectored<FD> {
fn iovecs(&self) -> &[IoSlice] {
unsafe { & *(&self.bufs[..] as *const [Box<[u8]>] as *const [IoSlice]) }
}
}
impl<FD: UringFd + Copy> Event for WriteVectored<FD> {
fn sqes_needed(&self) -> u32 { 1 }
unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
let mut sqe = sqs.single().unwrap();
sqe.prep_write_vectored(self.fd, self.iovecs(), self.offset);
sqe
}
fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
Cancellation::from(ManuallyDrop::into_inner(this).bufs)
}
}
================================================
FILE: src/fs.rs
================================================
//! Interact with the file system using io-uring
use std::fs;
use std::future::Future;
use std::io;
use std::mem::{self, ManuallyDrop};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::path::Path;
use std::pin::Pin;
use std::task::{Context, Poll};
use either::Either;
use futures_core::ready;
use futures_io::{AsyncRead, AsyncBufRead, AsyncWrite, AsyncSeek};
use iou::sqe::{OFlag, Mode};
use crate::buf::Buffer;
use crate::drive::Drive;
use crate::drive::demo::DemoDriver;
use crate::ring::{Ring, Cancellation};
use crate::event::OpenAt;
use crate::Submission;
type FileBuf = Either<Buffer, Box<libc::statx>>;
/// A file handle that runs on io-uring
pub struct File<D: Drive = DemoDriver> {
ring: Ring<D>,
fd: RawFd,
active: Op,
buf: FileBuf,
pos: u64,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Op {
Read,
Write,
Close,
Nothing,
Statx,
Closed,
}
impl File {
/// Open a file using the default driver
pub fn open(path: impl AsRef<Path>) -> Open {
File::open_on_driver(path, DemoDriver::default())
}
/// Create a new file using the default driver
pub fn create(path: impl AsRef<Path>) -> Create {
File::create_on_driver(path, DemoDriver::default())
}
}
impl<D: Drive + Clone> File<D> {
/// Open a file
pub fn open_on_driver(path: impl AsRef<Path>, driver: D) -> Open<D> {
let flags = OFlag::O_CLOEXEC | OFlag::O_RDONLY;
Open(driver.submit(OpenAt::without_dir(path, flags, Mode::from_bits(0o666).unwrap())))
}
/// Create a file
pub fn create_on_driver(path: impl AsRef<Path>, driver: D) -> Create<D> {
let flags = OFlag::O_CLOEXEC | OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC;
Create(driver.submit(OpenAt::without_dir(path, flags, Mode::from_bits(0o666).unwrap())))
}
}
impl<D: Drive> File<D> {
/// Take an existing file and run its IO on an io-uring driver
pub fn run_on_driver(file: fs::File, driver: D) -> File<D> {
let file = ManuallyDrop::new(file);
File::from_fd(file.as_raw_fd(), driver)
}
fn from_fd(fd: RawFd, driver: D) -> File<D> {
File {
ring: Ring::new(driver),
active: Op::Nothing,
buf: Either::Left(Buffer::default()),
pos: 0,
fd,
}
}
/// Access any data that has been read into the buffer, but not consumed
///
/// This is similar to the fill_buf method from AsyncBufRead, but instead of performing IO if
/// the buffer is empty, it will just return an empty slice. This method can be used to copy
/// out any left over buffered data before closing or performing a write.
pub fn read_buffered(&self) -> &[u8] {
if self.active == Op::Read {
self.buf.as_ref().unwrap_left().buffered_from_read()
} else { &[] }
}
fn guard_op(self: Pin<&mut Self>, op: Op) {
let (ring, buf, .., active) = self.split();
if *active == Op::Closed {
panic!("Attempted to perform IO on a closed File");
} else if *active != Op::Nothing && *active != op {
let new_buf = Either::Left(Buffer::default());
ring.cancel_pinned(Cancellation::from(mem::replace(buf, new_buf)));
}
*active = op;
}
fn cancel(&mut self) {
self.active = Op::Nothing;
let new_buf = Either::Left(Buffer::default());
self.ring.cancel(Cancellation::from(mem::replace(&mut self.buf, new_buf)));
}
fn poll_file_size(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<u64>> {
static EMPTY: libc::c_char = 0;
use std::ffi::CStr;
self.as_mut().guard_op(Op::Statx);
let fd = self.fd;
let (ring, statx, ..) = self.split_with_statx();
let flags = iou::sqe::StatxFlags::AT_EMPTY_PATH;
let mask = iou::sqe::StatxMode::STATX_SIZE;
ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_statx(fd, CStr::from_ptr(&EMPTY), flags, mask, statx);
}
sqe
}))?;
Poll::Ready(Ok((*statx).stx_size))
}
#[inline(always)]
fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut FileBuf, &mut u64, &mut Op) {
unsafe {
let this = Pin::get_unchecked_mut(self);
(Pin::new_unchecked(&mut this.ring), &mut this.buf, &mut this.pos, &mut this.active)
}
}
#[inline(always)]
fn split_with_buf(self: Pin<&mut Self>)
-> (Pin<&mut Ring<D>>, &mut Buffer, &mut u64, &mut Op)
{
let (ring, buf, pos, active) = self.split();
let buf = buf.as_mut().unwrap_left();
(ring, buf, pos, active)
}
#[inline(always)]
fn split_with_statx(self: Pin<&mut Self>)
-> (Pin<&mut Ring<D>>, &mut libc::statx, &mut u64, &mut Op)
{
let (ring, buf, pos, active) = self.split();
if buf.is_left() {
*buf = Either::Right(Box::new(unsafe { mem::zeroed() }));
}
let statx = buf.as_mut().unwrap_right();
(ring, statx, pos, active)
}
#[inline(always)]
fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
self.split().0
}
#[inline(always)]
fn buf(self: Pin<&mut Self>) -> &mut Buffer {
self.split_with_buf().1
}
#[inline(always)]
fn pos(self: Pin<&mut Self>) -> &mut u64 {
self.split().2
}
fn confirm_close(self: Pin<&mut Self>) {
*self.split().3 = Op::Closed;
}
}
impl<D: Drive> AsyncRead for File<D> {
fn poll_read(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8])
-> Poll<io::Result<usize>>
{
let mut inner = ready!(self.as_mut().poll_fill_buf(ctx))?;
let len = io::Read::read(&mut inner, buf)?;
self.consume(len);
Poll::Ready(Ok(len))
}
}
impl<D: Drive> AsyncBufRead for File<D> {
fn poll_fill_buf(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.as_mut().guard_op(Op::Read);
let fd = self.fd;
let (ring, buf, pos, ..) = self.split_with_buf();
buf.fill_buf(|buf| {
let n = ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_read(fd, buf, *pos);
}
sqe
}))?;
*pos += n as u64;
Poll::Ready(Ok(n as u32))
})
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.buf().consume(amt);
}
}
impl<D: Drive> AsyncWrite for File<D> {
fn poll_write(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[u8]) -> Poll<io::Result<usize>> {
self.as_mut().guard_op(Op::Write);
let fd = self.fd;
let (ring, buf, pos, ..) = self.split_with_buf();
let data = ready!(buf.fill_buf(|mut buf| {
Poll::Ready(Ok(io::Write::write(&mut buf, slice)? as u32))
}))?;
let n = ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_write(fd, data, *pos);
}
sqe
}))?;
*pos += n as u64;
buf.clear();
Poll::Ready(Ok(n as usize))
}
fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.poll_write(ctx, &[]))?;
Poll::Ready(Ok(()))
}
fn poll_close(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.as_mut().guard_op(Op::Close);
let fd = self.fd;
ready!(self.as_mut().ring().poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_close(fd);
}
sqe
}))?;
self.confirm_close();
Poll::Ready(Ok(()))
}
}
impl<D: Drive> AsyncSeek for File<D> {
fn poll_seek(mut self: Pin<&mut Self>, ctx: &mut Context, pos: io::SeekFrom)
-> Poll<io::Result<u64>>
{
let (whence, offset) = match pos {
io::SeekFrom::Start(n) => {
*self.as_mut().pos() = n;
return Poll::Ready(Ok(self.pos));
}
io::SeekFrom::Current(n) => (self.pos, n),
io::SeekFrom::End(n) => {
(ready!(self.as_mut().poll_file_size(ctx))?, n)
}
};
let valid_seek = if offset.is_negative() {
match whence.checked_sub(offset.abs() as u64) {
Some(valid_seek) => valid_seek,
None => {
let invalid = io::Error::from(io::ErrorKind::InvalidInput);
return Poll::Ready(Err(invalid));
}
}
} else {
match whence.checked_add(offset as u64) {
Some(valid_seek) => valid_seek,
None => {
let overflow = io::Error::from_raw_os_error(libc::EOVERFLOW);
return Poll::Ready(Err(overflow));
}
}
};
*self.as_mut().pos() = valid_seek;
Poll::Ready(Ok(self.pos))
}
}
impl From<fs::File> for File {
fn from(file: fs::File) -> File {
File::run_on_driver(file, DemoDriver::default())
}
}
impl<D: Drive> From<File<D>> for fs::File {
fn from(mut file: File<D>) -> fs::File {
file.cancel();
let file = ManuallyDrop::new(file);
unsafe {
fs::File::from_raw_fd(file.fd)
}
}
}
impl<D: Drive> Drop for File<D> {
fn drop(&mut self) {
match self.active {
Op::Closed => { }
Op::Nothing => unsafe { libc::close(self.fd); },
_ => self.cancel(),
}
}
}
/// A future representing an opening file.
pub struct Open<D: Drive = DemoDriver>(Submission<OpenAt, D>);
impl<D: Drive> Open<D> {
fn inner(self: Pin<&mut Self>) -> Pin<&mut Submission<OpenAt, D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.0) }
}
}
impl<D: Drive + Clone> Future for Open<D> {
type Output = io::Result<File<D>>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<File<D>>> {
let mut inner = self.inner();
let (_, result) = ready!(inner.as_mut().poll(ctx));
let fd = result? as i32;
let driver = inner.driver().clone();
Poll::Ready(Ok(File::from_fd(fd, driver)))
}
}
/// A future representing a file being created.
pub struct Create<D: Drive = DemoDriver>(Submission<OpenAt, D>);
impl<D: Drive> Create<D> {
fn inner(self: Pin<&mut Self>) -> Pin<&mut Submission<OpenAt, D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.0) }
}
}
impl<D: Drive + Clone> Future for Create<D> {
type Output = io::Result<File<D>>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<File<D>>> {
let mut inner = self.inner();
let (_, result) = ready!(inner.as_mut().poll(ctx));
let fd = result? as i32;
let driver = inner.driver().clone();
Poll::Ready(Ok(File::from_fd(fd, driver)))
}
}
================================================
FILE: src/io.rs
================================================
use std::borrow::Cow;
use std::future::Future;
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::pin::Pin;
use std::task::{Poll, Context};
use futures_core::ready;
use futures_io::AsyncWrite;
use crate::buf::Buffer;
use crate::{Drive, ring::Ring};
use crate::drive::demo::DemoDriver;
#[macro_export]
macro_rules! print {
($driver:expr, $($arg:tt)*) => {{
let mut s = format!($($arg)*);
$crate::io::__print($driver, s.into_bytes())
}};
}
#[macro_export]
macro_rules! println {
($driver:expr) => {$crate::io::__print($driver, b"\n")};
($driver:expr, $($arg:tt)*) => {{
let mut s = format!($($arg)*);
s.push('\n');
$crate::io::__print($driver, s.into_bytes())
}};
}
#[macro_export]
macro_rules! eprint {
($driver:expr, $($arg:tt)*) => {{
let mut s = format!($($arg)*);
$crate::io::__eprint($driver, s.into_bytes())
}};
}
#[macro_export]
macro_rules! eprintln {
($driver:expr) => {$crate::io::__eprint($driver, b"\n")};
($driver:expr, $($arg:tt)*) => {{
let mut s = format!($($arg)*);
s.push('\n');
$crate::io::__eprint($driver, s.into_bytes())
}};
}
#[doc(hidden)]
pub async fn __print<D: Drive>(driver: D, bytes: impl Into<Cow<'static, [u8]>>) {
Print {
ring: Ring::new(driver),
fd: 1,
bytes: bytes.into(),
idx: 0,
}.await.expect("printing to stdout failed")
}
#[doc(hidden)]
pub async fn __eprint<D: Drive>(driver: D, bytes: impl Into<Cow<'static, [u8]>>) {
Print {
ring: Ring::new(driver),
fd: 2,
bytes: bytes.into(),
idx: 0,
}.await.expect("printing to stderr failed")
}
struct Print<D: Drive> {
ring: Ring<D>,
fd: RawFd,
bytes: Cow<'static, [u8]>,
idx: usize,
}
impl<D: Drive> Print<D> {
fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, RawFd, &[u8], &mut usize) {
unsafe {
let this = Pin::get_unchecked_mut(self);
let bytes = &this.bytes.as_ref()[this.idx..];
(Pin::new_unchecked(&mut this.ring), this.fd, bytes, &mut this.idx)
}
}
}
impl<D: Drive> Future for Print<D> {
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let (mut ring, fd, mut bytes, idx) = self.split();
if !bytes.is_empty() {
loop {
let written = ready!(ring.as_mut().poll(ctx, 1, |sqs| unsafe {
let mut sqe = sqs.single().unwrap();
sqe.prep_write(fd, bytes, 0);
sqe
}))? as usize;
*idx += written;
if written == bytes.len() {
return Poll::Ready(Ok(()));
} else {
bytes = &bytes[written..];
}
}
} else {
return Poll::Ready(Ok(()));
}
}
}
/// A handle to the standard output of the current process.
pub struct Stdout<D: Drive> {
ring: Ring<D>,
buf: Buffer,
}
/// Constructs a new `stdout` handle run on the demo driver.
/// ```no_run
/// use ringbahn::io;
///
/// # use futures::AsyncWriteExt;
/// # fn main() -> std::io::Result<()> { futures::executor::block_on(async {
/// io::stdout().write(b"hello, world").await?;
/// # Ok(())
/// # })
/// # }
/// ```
// TODO synchronization note?
pub fn stdout() -> Stdout<DemoDriver> {
stdout_on_driver(DemoDriver::default())
}
/// Constructs a new `stdout` handle run on the provided driver.
pub fn stdout_on_driver<D: Drive>(driver: D) -> Stdout<D> {
Stdout {
ring: Ring::new(driver),
buf: Buffer::default(),
}
}
impl<D: Drive> Stdout<D> {
#[inline(always)]
fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut Buffer) {
unsafe {
let this = Pin::get_unchecked_mut(self);
(Pin::new_unchecked(&mut this.ring), &mut this.buf)
}
}
}
impl<D: Drive> AsyncWrite for Stdout<D> {
fn poll_write(self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[u8])
-> Poll<io::Result<usize>>
{
let fd = self.as_raw_fd();
let (ring, buf, ..) = self.split();
let data = ready!(buf.fill_buf(|mut buf| {
Poll::Ready(Ok(io::Write::write(&mut buf, slice)? as u32))
}))?;
let n = ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_write(fd, data, 0);
}
sqe
}))?;
buf.clear();
Poll::Ready(Ok(n as usize))
}
fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.poll_write(ctx, &[]))?;
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(ctx)
}
}
impl<D: Drive> AsRawFd for Stdout<D> {
fn as_raw_fd(&self) -> RawFd {
libc::STDOUT_FILENO
}
}
================================================
FILE: src/lib.rs
================================================
pub mod fs;
pub mod net;
pub mod unix;
pub mod drive;
pub mod event;
pub mod ring;
pub mod io;
mod buf;
mod submission;
pub use submission::Submission;
#[doc(inline)]
pub use drive::Drive;
#[doc(inline)]
pub use event::Event;
================================================
FILE: src/net/listener.rs
================================================
use std::io;
use std::future::Future;
use std::net::{ToSocketAddrs, SocketAddr};
use std::os::unix::io::{RawFd};
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::{ready, Stream};
use iou::sqe::SockAddrStorage;
use nix::sys::socket::{self as nix_socket, SockProtocol, SockFlag};
use crate::drive::{Drive, demo::DemoDriver};
use crate::ring::{Cancellation, Ring};
use super::TcpStream;
pub struct TcpListener<D: Drive = DemoDriver> {
ring: Ring<D>,
fd: RawFd,
active: Op,
addr: Option<Box<iou::sqe::SockAddrStorage>>,
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
enum Op {
Nothing = 0,
Accept,
Close,
Closed,
}
impl TcpListener {
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
TcpListener::bind_on_driver(addr, DemoDriver::default())
}
}
impl<D: Drive> TcpListener<D> {
pub fn bind_on_driver<A: ToSocketAddrs>(addr: A, driver: D) -> io::Result<TcpListener<D>> {
let (fd, addr) = super::socket(addr, SockProtocol::Tcp)?;
let addr = iou::sqe::SockAddr::Inet(nix_socket::InetAddr::from_std(&addr));
nix_socket::setsockopt(fd, nix_socket::sockopt::ReuseAddr, &true)
.map_err(|e| e.as_errno().unwrap_or(nix::errno::Errno::EIO))?;
nix_socket::bind(fd, &addr).map_err(|e| e.as_errno().unwrap_or(nix::errno::Errno::EIO))?;
nix_socket::listen(fd, 128).map_err(|e| e.as_errno().unwrap_or(nix::errno::Errno::EIO))?;
let ring = Ring::new(driver);
Ok(TcpListener {
active: Op::Nothing,
addr: None,
fd, ring,
})
}
pub fn close(&mut self) -> Close<D> where D: Unpin {
Pin::new(self).close_pinned()
}
pub fn close_pinned(self: Pin<&mut Self>) -> Close<D> {
Close { socket: self }
}
fn guard_op(self: Pin<&mut Self>, op: Op) {
let (ring, addr, active) = self.split();
if *active == Op::Closed {
panic!("Attempted to perform IO on a closed TcpListener");
} else if *active != Op::Nothing && *active != op {
ring.cancel_pinned(Cancellation::from(addr.take()));
}
*active = op;
}
fn cancel(&mut self) {
let cancellation = match self.active {
Op::Accept => Cancellation::from(self.addr.take()),
Op::Close => Cancellation::from(()),
Op::Closed => return,
Op::Nothing => return,
};
self.active = Op::Nothing;
self.ring.cancel(cancellation);
}
fn drop_addr(self: Pin<&mut Self>) {
self.split().1.take();
}
fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
self.split().0
}
fn split(self: Pin<&mut Self>)
-> (Pin<&mut Ring<D>>, &mut Option<Box<SockAddrStorage>>, &mut Op)
{
unsafe {
let this = Pin::get_unchecked_mut(self);
(Pin::new_unchecked(&mut this.ring), &mut this.addr, &mut this.active)
}
}
fn split_with_addr(self: Pin<&mut Self>)
-> (Pin<&mut Ring<D>>, &mut SockAddrStorage, &mut Op)
{
let (ring, addr, active) = self.split();
if addr.is_none() {
*addr = Some(Box::new(SockAddrStorage::uninit()));
}
(ring, &mut **addr.as_mut().unwrap(), active)
}
fn confirm_close(self: Pin<&mut Self>) {
*self.split().2 = Op::Closed;
}
}
impl<D: Drive + Clone> TcpListener<D> {
pub fn accept(&mut self) -> Accept<'_, D> where D: Unpin {
Pin::new(self).accept_pinned()
}
pub fn accept_pinned(self: Pin<&mut Self>) -> Accept<'_, D> {
Accept { socket: self }
}
pub fn incoming(&mut self) -> Incoming<'_, D> where D: Unpin {
Pin::new(self).incoming_pinned()
}
pub fn incoming_pinned(self: Pin<&mut Self>) -> Incoming<'_, D> {
Incoming { accept: self.accept_pinned() }
}
pub fn accept_no_addr(&mut self) -> AcceptNoAddr<'_, D> where D: Unpin {
Pin::new(self).accept_no_addr_pinned()
}
pub fn accept_no_addr_pinned(self: Pin<&mut Self>) -> AcceptNoAddr<'_, D> {
AcceptNoAddr { socket: self }
}
pub fn incoming_no_addr(&mut self) -> IncomingNoAddr<'_, D> where D: Unpin {
Pin::new(self).incoming_no_addr_pinned()
}
pub fn incoming_no_addr_pinned(self: Pin<&mut Self>) -> IncomingNoAddr<'_, D> {
IncomingNoAddr { accept: self.accept_no_addr_pinned() }
}
pub fn poll_accept(mut self: Pin<&mut Self>, ctx: &mut Context<'_>)
-> Poll<io::Result<(TcpStream<D>, SocketAddr)>>
{
self.as_mut().guard_op(Op::Accept);
let fd = self.fd;
let (ring, addr, ..) = self.as_mut().split_with_addr();
let fd = ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_accept(fd, Some(addr), SockFlag::empty());
}
sqe
}))? as RawFd;
let addr = {
let result = unsafe { addr.as_socket_addr() };
self.as_mut().drop_addr();
match result? {
iou::sqe::SockAddr::Inet(addr) => addr.to_std(),
addr => panic!("TcpListener addr cannot be {:?}", addr.family()),
}
};
Poll::Ready(Ok((TcpStream::from_fd(fd, self.ring().clone()), addr)))
}
pub fn poll_accept_no_addr(mut self: Pin<&mut Self>, ctx: &mut Context<'_>)
-> Poll<io::Result<TcpStream<D>>>
{
self.as_mut().guard_op(Op::Accept);
let fd = self.fd;
let fd = ready!(self.as_mut().ring().poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_accept(fd, None, SockFlag::empty());
}
sqe
}))? as RawFd;
Poll::Ready(Ok(TcpStream::from_fd(fd, self.ring().clone())))
}
}
impl<D: Drive> Drop for TcpListener<D> {
fn drop(&mut self) {
match self.active {
Op::Closed => { }
Op::Nothing => unsafe { libc::close(self.fd); }
_ => self.cancel(),
}
}
}
pub struct Accept<'a, D: Drive> {
socket: Pin<&'a mut TcpListener<D>>,
}
impl<'a, D: Drive + Clone> Future for Accept<'a, D> {
type Output = io::Result<(TcpStream<D>, SocketAddr)>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
self.socket.as_mut().poll_accept(ctx)
}
}
pub struct AcceptNoAddr<'a, D: Drive> {
socket: Pin<&'a mut TcpListener<D>>,
}
impl<'a, D: Drive + Clone> Future for AcceptNoAddr<'a, D> {
type Output = io::Result<TcpStream<D>>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
self.socket.as_mut().poll_accept_no_addr(ctx)
}
}
pub struct Incoming<'a, D: Drive> {
accept: Accept<'a, D>,
}
impl<'a, D: Drive> Incoming<'a, D> {
fn inner(self: Pin<&mut Self>) -> Pin<&mut Accept<'a, D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.accept) }
}
}
impl<'a, D: Drive + Clone> Stream for Incoming<'a, D> {
type Item = io::Result<(TcpStream<D>, SocketAddr)>;
fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = ready!(self.inner().poll(ctx));
Poll::Ready(Some(next))
}
}
pub struct IncomingNoAddr<'a, D: Drive> {
accept: AcceptNoAddr<'a, D>,
}
impl<'a, D: Drive> IncomingNoAddr<'a, D> {
fn inner(self: Pin<&mut Self>) -> Pin<&mut AcceptNoAddr<'a, D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.accept) }
}
}
impl<'a, D: Drive + Clone> Stream for IncomingNoAddr<'a, D> {
type Item = io::Result<TcpStream<D>>;
fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = ready!(self.inner().poll(ctx));
Poll::Ready(Some(next))
}
}
pub struct Close<'a, D: Drive> {
socket: Pin<&'a mut TcpListener<D>>,
}
impl<'a, D: Drive> Future for Close<'a, D> {
type Output = io::Result<()>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.socket.as_mut().guard_op(Op::Close);
let fd = self.socket.fd;
ready!(self.socket.as_mut().ring().poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_close(fd);
}
sqe
}))?;
self.socket.as_mut().confirm_close();
Poll::Ready(Ok(()))
}
}
================================================
FILE: src/net/mod.rs
================================================
mod listener;
mod stream;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::os::unix::io::RawFd;
pub use listener::{TcpListener, Accept, AcceptNoAddr, Close, Incoming, IncomingNoAddr};
pub use stream::{TcpStream, Connect};
use nix::sys::socket as nix;
fn socket<A: ToSocketAddrs>(addr: A, protocol: nix::SockProtocol) -> io::Result<(RawFd, SocketAddr)> {
use io::{Error, ErrorKind};
let mut error = Error::new(ErrorKind::InvalidInput, "could not resolve to any addresses");
for addr in addr.to_socket_addrs()? {
let domain = match addr.is_ipv6() {
true => nix::AddressFamily::Inet6,
false => nix::AddressFamily::Inet,
};
let flags = nix::SockFlag::SOCK_CLOEXEC;
match nix::socket(domain, nix::SockType::Stream, flags, Some(protocol)) {
Ok(fd) => return Ok((fd, addr)),
_ => error = io::Error::last_os_error(),
}
}
Err(error)
}
================================================
FILE: src/net/stream.rs
================================================
use std::io;
use std::future::Future;
use std::net::ToSocketAddrs;
use std::os::unix::io::RawFd;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::ready;
use futures_io::{AsyncRead, AsyncBufRead, AsyncWrite};
use iou::sqe::SockAddr;
use nix::sys::socket::SockProtocol;
use crate::buf::Buffer;
use crate::drive::{Drive, demo::DemoDriver};
use crate::ring::Ring;
use crate::event;
use crate::Submission;
use super::socket;
pub struct TcpStream<D: Drive = DemoDriver> {
ring: Ring<D>,
buf: Buffer,
active: Op,
fd: RawFd,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Op {
Read,
Write,
Close,
Nothing,
Closed,
}
impl TcpStream {
pub fn connect<A: ToSocketAddrs>(addr: A) -> Connect {
TcpStream::connect_on_driver(addr, DemoDriver::default())
}
}
impl<D: Drive + Clone> TcpStream<D> {
pub fn connect_on_driver<A: ToSocketAddrs>(addr: A, driver: D) -> Connect<D> {
let (fd, addr) = match socket(addr, SockProtocol::Tcp) {
Ok(fd) => fd,
Err(e) => return Connect(Err(Some(e))),
};
let addr = Box::new(SockAddr::Inet(nix::sys::socket::InetAddr::from_std(&addr)));
Connect(Ok(driver.submit(event::Connect { fd, addr })))
}
}
impl<D: Drive> TcpStream<D> {
pub(crate) fn from_fd(fd: RawFd, ring: Ring<D>) -> TcpStream<D> {
TcpStream {
buf: Buffer::default(),
active: Op::Nothing,
fd, ring,
}
}
fn guard_op(self: Pin<&mut Self>, op: Op) {
let (ring, buf, active) = self.split();
if *active == Op::Closed {
panic!("Attempted to perform IO on a closed stream");
} else if *active != Op::Nothing && *active != op {
ring.cancel_pinned(buf.cancellation());
}
*active = op;
}
fn cancel(&mut self) {
self.active = Op::Nothing;
self.ring.cancel(self.buf.cancellation());
}
#[inline(always)]
fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
self.split().0
}
#[inline(always)]
fn buf(self: Pin<&mut Self>) -> &mut Buffer {
self.split().1
}
#[inline(always)]
fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut Buffer, &mut Op) {
unsafe {
let this = Pin::get_unchecked_mut(self);
(Pin::new_unchecked(&mut this.ring), &mut this.buf, &mut this.active)
}
}
fn confirm_close(self: Pin<&mut Self>) {
*self.split().2 = Op::Closed;
}
}
pub struct Connect<D: Drive = DemoDriver>(
Result<Submission<event::Connect, D>, Option<io::Error>>
);
impl<D: Drive + Clone> Future for Connect<D> {
type Output = io::Result<TcpStream<D>>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
match self.project() {
Ok(mut submission) => {
let (connect, result) = ready!(submission.as_mut().poll(ctx));
result?;
let driver = submission.driver().clone();
Poll::Ready(Ok(TcpStream::from_fd(connect.fd, Ring::new(driver))))
}
Err(err) => {
let err = err.take().expect("polled Connect future after completion");
Poll::Ready(Err(err))
}
}
}
}
impl<D: Drive> Connect<D> {
fn project(self: Pin<&mut Self>)
-> Result<Pin<&mut Submission<event::Connect, D>>, &mut Option<io::Error>>
{
unsafe {
match &mut Pin::get_unchecked_mut(self).0 {
Ok(submission) => Ok(Pin::new_unchecked(submission)),
Err(err) => Err(err)
}
}
}
}
impl<D: Drive> AsyncRead for TcpStream<D> {
fn poll_read(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8])
-> Poll<io::Result<usize>>
{
let mut inner = ready!(self.as_mut().poll_fill_buf(ctx))?;
let len = io::Read::read(&mut inner, buf)?;
self.consume(len);
Poll::Ready(Ok(len))
}
}
impl<D: Drive> AsyncBufRead for TcpStream<D> {
fn poll_fill_buf(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.as_mut().guard_op(Op::Read);
let fd = self.fd;
let (ring, buf, ..) = self.split();
buf.fill_buf(|buf| {
let n = ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_read(fd, buf, 0);
}
sqe
}))?;
Poll::Ready(Ok(n as u32))
})
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.buf().consume(amt);
}
}
impl<D: Drive> AsyncWrite for TcpStream<D> {
fn poll_write(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[u8]) -> Poll<io::Result<usize>> {
self.as_mut().guard_op(Op::Write);
let fd = self.fd;
let (ring, buf, ..) = self.split();
let data = ready!(buf.fill_buf(|mut buf| {
Poll::Ready(Ok(io::Write::write(&mut buf, slice)? as u32))
}))?;
let n = ready!(ring.poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_write(fd, data, 0);
}
sqe
}))?;
buf.clear();
Poll::Ready(Ok(n as usize))
}
fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.poll_write(ctx, &[]))?;
Poll::Ready(Ok(()))
}
fn poll_close(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.as_mut().guard_op(Op::Close);
let fd = self.fd;
ready!(self.as_mut().ring().poll(ctx, 1, |sqs| {
let mut sqe = sqs.single().unwrap();
unsafe {
sqe.prep_close(fd);
}
sqe
}))?;
self.confirm_close();
Poll::Ready(Ok(()))
}
}
impl<D: Drive> Drop for TcpStream<D> {
fn drop(&mut self) {
match self.active {
Op::Closed => { }
Op::Nothing => unsafe { libc::close(self.fd); },
_ => self.cancel(),
}
}
}
================================================
FILE: src/ring/cancellation.rs
================================================
use std::any::Any;
use std::ffi::CString;
use std::mem;
use std::ptr;
use either::Either;
use crate::buf::Buffer;
/// A cancellation callback to clean up resources when IO gets cancelled.
///
/// When a user cancels interest in an event, the future representing that event needs to be
/// dropped. However, that future may share ownership of some data structures (like buffers)
/// with the kernel, which is completing the event. The cancellation callback will take
/// ownership of those resources, and clean them up when it is dropped.
pub struct Cancellation {
data: *mut (),
metadata: usize,
drop: unsafe fn(*mut (), usize),
}
pub unsafe trait Cancel {
fn into_raw(self) -> (*mut (), usize);
unsafe fn drop_raw(data: *mut (), metadata: usize);
}
unsafe impl<T> Cancel for Box<T> {
fn into_raw(self) -> (*mut (), usize) {
(Box::into_raw(self) as *mut (), 0)
}
unsafe fn drop_raw(data: *mut (), _: usize) {
drop(Box::from_raw(data as *mut T));
}
}
unsafe impl<T> Cancel for Box<[T]> {
fn into_raw(self) -> (*mut (), usize) {
let len = self.len();
(Box::into_raw(self) as *mut (), len)
}
unsafe fn drop_raw(data: *mut (), len: usize) {
drop(Vec::from_raw_parts(data as *mut T, len, len).into_boxed_slice());
}
}
#[repr(C)]
struct TraitObject {
data: *mut (),
vtable: *mut (),
}
unsafe impl Cancel for Box<dyn Any + Send + Sync> {
fn into_raw(self) -> (*mut (), usize) {
let obj = unsafe { mem::transmute::<Self, TraitObject>(self) };
(obj.data, obj.vtable as usize)
}
unsafe fn drop_raw(data: *mut (), metadata: usize) {
let obj = TraitObject { data, vtable: metadata as *mut () };
drop(mem::transmute::<TraitObject, Self>(obj));
}
}
unsafe impl Cancel for iou::registrar::RegisteredBuf {
fn into_raw(self) -> (*mut (), usize) {
self.into_inner().into_raw()
}
unsafe fn drop_raw(data: *mut (), metadata: usize) {
Box::<[u8]>::drop_raw(data, metadata)
}
}
unsafe impl Cancel for CString {
fn into_raw(self) -> (*mut (), usize) {
(self.into_raw() as *mut (), 0)
}
unsafe fn drop_raw(data: *mut (), _: usize) {
drop(CString::from_raw(data as *mut libc::c_char));
}
}
unsafe impl Cancel for () {
fn into_raw(self) -> (*mut (), usize) {
(ptr::null_mut(), 0)
}
unsafe fn drop_raw(_: *mut (), _: usize) { }
}
pub unsafe trait CancelNarrow: Cancel { }
unsafe impl<T> CancelNarrow for Box<T> { }
unsafe impl CancelNarrow for CString { }
unsafe impl<T: CancelNarrow, U: CancelNarrow> Cancel for (T, U) {
fn into_raw(self) -> (*mut (), usize) {
let left = self.0.into_raw().0;
let right = self.1.into_raw().0;
(left, right as usize)
}
unsafe fn drop_raw(data: *mut (), metadata: usize) {
T::drop_raw(data, 0);
U::drop_raw(metadata as *mut (), 0);
}
}
impl Cancellation {
fn new<T: Cancel>(object: T) -> Cancellation {
let (data, metadata) = object.into_raw();
Cancellation { data, metadata, drop: T::drop_raw }
}
}
impl<T: Cancel> From<T> for Cancellation {
fn from(object: T) -> Cancellation {
Cancellation::new(object)
}
}
impl<T> From<Option<T>> for Cancellation where Cancellation: From<T> {
fn from(object: Option<T>) -> Cancellation {
object.map_or(Cancellation::new(()), Cancellation::from)
}
}
impl From<Buffer> for Cancellation {
fn from(buffer: Buffer) -> Cancellation {
Cancellation::from(buffer.into_boxed_slice())
}
}
impl<T, U> From<Either<T, U>> for Cancellation where Cancellation: From<T> + From<U> {
fn from(object: Either<T, U>) -> Cancellation {
object.either(Cancellation::from, Cancellation::from)
}
}
unsafe impl Send for Cancellation { }
unsafe impl Sync for Cancellation { }
impl Drop for Cancellation {
fn drop(&mut self) {
unsafe {
(self.drop)(self.data, self.metadata)
}
}
}
================================================
FILE: src/ring/completion.rs
================================================
use std::io;
use std::mem::{self, ManuallyDrop};
use std::task::Waker;
use parking_lot::Mutex;
use crate::ring::Cancellation;
use iou::CQE;
use State::*;
/// A completion tracks an event that has been submitted to io-uring. It is a pointer to a heap
/// allocated object which represents the state of the event's completion. Ownership of this object
/// is shared between the Completion type and the io-uring instance (the address of the object is
/// passed as a user_data field with the event's SQE).
///
/// Therefore, it requires a fair amout of unsafe code and synchronization to properly manage the
/// lifecycle of this object. That code is encapsulated here inside a safe API for the rest of
/// ringbahn to use.
///
/// This API is not publicly visible outside of this crate. (The Completion type in the public API
/// is an opaque wrapper aroud this type). End users do not need to understand the completion API.
pub struct Completion {
state: ManuallyDrop<Box<Mutex<State>>>,
}
enum State {
Submitted(Waker),
Completed(io::Result<u32>),
Cancelled(Cancellation),
Empty,
}
impl Completion {
/// Create a new completion for an event being prepared. When the event is completed by
/// io-uring, the waker this completion holds will be awoken.
pub fn new(waker: Waker) -> Completion {
Completion {
state: ManuallyDrop::new(Box::new(Mutex::new(Submitted(waker)))),
}
}
/// Get the address of this completion, so that it can set as the user_data field of the SQE
/// being prepared.
pub fn addr(&self) -> u64 {
&**self.state as *const Mutex<State> as usize as u64
}
/// Check if the completion has completed. If it has, the result of the completion will be
/// returned and the completion will be deallocated. If it has not been completed, the waker
/// field will be updated to the new waker if the old waker would not wake the same task.
pub fn check(self, waker: &Waker) -> Result<io::Result<u32>, Completion> {
let mut state = self.state.lock();
match mem::replace(&mut *state, State::Empty) {
Submitted(old_waker) => {
let waker = if old_waker.will_wake(waker) { old_waker } else { waker.clone() };
*state = Submitted(waker);
drop(state);
Err(self)
}
Completed(result) => {
drop(state);
drop(ManuallyDrop::into_inner(self.state));
Ok(result)
}
_ => unreachable!()
}
}
/// Cancel interest in this completion. The Cancellation callback will be stored to clean up
/// resources shared with the kernel when the event completes.
pub fn cancel(self, callback: Cancellation) {
let mut state = self.state.lock();
match &*state {
Submitted(_) => {
*state = Cancelled(callback);
drop(state);
}
Completed(_) => {
drop(callback);
drop(state);
drop(ManuallyDrop::into_inner(self.state));
}
_ => unreachable!()
}
}
fn complete(self, result: io::Result<u32>) {
let mut state = self.state.lock();
match mem::replace(&mut *state, State::Empty) {
Submitted(waker) => {
*state = Completed(result);
waker.wake();
}
Cancelled(callback) => {
drop(callback);
drop(state);
drop(ManuallyDrop::into_inner(self.state));
}
_ => unreachable!()
}
}
}
pub fn complete(cqe: CQE) {
unsafe {
let result = cqe.result();
let user_data = cqe.user_data();
// iou should never raise LIBURING_UDATA_TIMEOUTs, this is just to catch bugs in iou
debug_assert!(user_data != uring_sys::LIBURING_UDATA_TIMEOUT);
let state = user_data as *mut Mutex<State>;
if !state.is_null() {
let completion = Completion {
state: ManuallyDrop::new(Box::from_raw(state))
};
completion.complete(result);
}
};
}
================================================
FILE: src/ring/mod.rs
================================================
mod cancellation;
pub(crate) mod completion;
use std::io;
use std::mem;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::ready;
use iou::{SQE, SQEs};
use crate::drive::{self, Drive};
pub use cancellation::{Cancellation, Cancel, CancelNarrow};
pub(crate) use completion::Completion;
use State::*;
/// A low-level primitive for building an IO object on io-uring
///
/// Ring is a state machine similar to `Submission`, but it is designed to cycle through multiple
/// IO events submitted to io-uring, rather than representing a single submission. Because of this,
/// it is more low level, but it is suitable for building an IO object like a `File` on top of
/// io-uring.
///
/// Users writing code on top of `Ring` are responsible for making sure that it is correct. For
/// example, when calling `poll`, users must ensure that they are in the proper state to submit
/// whatever type of IO they would be attempting to submit. Additionally, users should note that
/// `Ring` does not implement `Drop`. In order to cancel any ongoing IO, users are responsible for
/// implementing drop to call cancel properly.
pub struct Ring<D: Drive> {
state: State,
driver: D,
}
enum State {
Inert,
Prepared(Completion),
Submitted(Completion),
Cancelled(u64),
Lost,
}
impl<D: Default + Drive> Default for Ring<D> {
fn default() -> Ring<D> {
Ring::new(D::default())
}
}
impl<D: Drive + Clone> Clone for Ring<D> {
fn clone(&self) -> Ring<D> {
Ring::new(self.driver.clone())
}
}
impl<D: Drive> Ring<D> {
/// Construct a new Ring on top of a driver.
#[inline(always)]
pub fn new(driver: D) -> Ring<D> {
Ring {
state: Inert,
driver
}
}
pub fn driver(&self) -> &D {
&self.driver
}
/// Poll the ring state machine.
///
/// This accepts a callback, `prepare`, which prepares an event to be submitted to io-uring.
/// This callback will only be called once during an iteration of ring's state machine: once an
/// event has been prepared, until it is completed or cancelled, a single ring instance will
/// not prepare any additional events.
#[inline]
pub fn poll(
mut self: Pin<&mut Self>,
ctx: &mut Context<'_>,
count: u32,
prepare: impl for<'sq> FnOnce(&mut SQEs<'sq>) -> SQE<'sq>,
) -> Poll<io::Result<u32>> {
match self.state {
Inert | Cancelled(_) => {
ready!(self.as_mut().poll_prepare(ctx, count, prepare));
ready!(self.as_mut().poll_submit(ctx));
Poll::Pending
}
Prepared(_) => {
match self.as_mut().poll_complete(ctx) {
ready @ Poll::Ready(..) => ready,
Poll::Pending => {
ready!(self.poll_submit(ctx));
Poll::Pending
}
}
}
Submitted(_) => self.poll_complete(ctx),
Lost => panic!("Ring in a bad state; driver is faulty"),
}
}
#[inline(always)]
fn poll_prepare(
self: Pin<&mut Self>,
ctx: &mut Context<'_>,
count: u32,
prepare: impl for<'sq> FnOnce(&mut SQEs<'sq>) -> SQE<'sq>,
) -> Poll<()> {
let (driver, state) = self.split();
let completion = match *state {
Cancelled(prev) => {
ready!(driver.poll_prepare(ctx, count + 1, |mut sqs, ctx| {
*state = Lost;
unsafe { sqs.hard_linked().next().unwrap().prep_cancel(prev, 0); }
let sqe = prepare(&mut sqs);
drive::Completion::new(sqe, sqs, ctx)
}))
}
Inert => {
ready!(driver.poll_prepare(ctx, count, |mut sqs, ctx| {
*state = Lost;
let sqe = prepare(&mut sqs);
drive::Completion::new(sqe, sqs, ctx)
}))
}
_ => unreachable!(),
};
*state = Prepared(completion.real);
Poll::Ready(())
}
#[inline(always)]
fn poll_submit(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<()> {
let (driver, state) = self.split();
// TODO figure out how to handle this result
let _ = ready!(driver.poll_submit(ctx));
if let Prepared(completion) | Submitted(completion) = mem::replace(state, Lost) {
*state = Submitted(completion);
Poll::Ready(())
} else {
unreachable!()
}
}
#[inline(always)]
fn poll_complete(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<u32>> {
let (_, state) = self.split();
match mem::replace(state, Lost) {
Prepared(completion) => {
match completion.check(ctx.waker()) {
Ok(result) => {
*state = Inert;
Poll::Ready(result)
}
Err(completion) => {
*state = Prepared(completion);
Poll::Pending
}
}
}
Submitted(completion) => {
match completion.check(ctx.waker()) {
Ok(result) => {
*state = Inert;
Poll::Ready(result)
}
Err(completion) => {
*state = Submitted(completion);
Poll::Pending
}
}
}
_ => unreachable!(),
}
}
/// Cancel any ongoing IO with this cancellation.
///
/// Users are responsible for ensuring that the cancellation passed would be appropriate to
/// clean up the resources of the running event.
#[inline]
pub fn cancel(&mut self, cancellation: Cancellation) {
self.state.cancel(cancellation);
}
/// Cancel any ongoing IO, but from a pinned reference.
///
/// This has the same behavior of as Ring::cancel.
pub fn cancel_pinned(self: Pin<&mut Self>, cancellation: Cancellation) {
self.split().1.cancel(cancellation);
}
fn split(self: Pin<&mut Self>) -> (Pin<&mut D>, &mut State) {
unsafe {
let this = Pin::get_unchecked_mut(self);
(Pin::new_unchecked(&mut this.driver), &mut this.state)
}
}
}
impl State {
fn cancel(&mut self, cancellation: Cancellation) {
match mem::replace(self, Lost) {
Prepared(completion) | Submitted(completion) => {
*self = Cancelled(completion.addr());
completion.cancel(cancellation);
}
state => {
*self = state;
}
}
}
}
================================================
FILE: src/submission.rs
================================================
use std::future::Future;
use std::io;
use std::mem::ManuallyDrop;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::ready;
use crate::{Event, Drive, ring::Ring};
/// A [`Future`] representing an event submitted to io-uring
pub struct Submission<E: Event, D: Drive> {
ring: Ring<D>,
event: Option<E>,
}
impl<E: Event, D: Drive> Submission<E, D> {
/// Construct a new submission from an event and a driver.
pub fn new(event: E, driver: D) -> Submission<E, D> {
Submission {
ring: Ring::new(driver),
event: Some(event),
}
}
/// Access the driver this submission is using
pub fn driver(&self) -> &D {
self.ring.driver()
}
pub fn replace_event(self: Pin<&mut Self>, event: E) {
let (ring, event_slot) = self.split();
if let Some(event) = event_slot.take() {
ring.cancel_pinned(E::cancel(ManuallyDrop::new(event)))
}
*event_slot = Some(event);
}
fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut Option<E>) {
unsafe {
let this = Pin::get_unchecked_mut(self);
(Pin::new_unchecked(&mut this.ring), &mut this.event)
}
}
}
impl<E, D> Future for Submission<E, D> where
E: Event,
D: Drive,
{
type Output = (E, io::Result<u32>);
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let (ring, event) = self.split();
let result = if let Some(event) = event {
let count = event.sqes_needed();
ready!(ring.poll(ctx, count, |sqs| unsafe { event.prepare(sqs) }))
} else {
panic!("polled Submission after completion")
};
Poll::Ready((event.take().unwrap(), result))
}
}
impl<E: Event, D: Drive> Drop for Submission<E, D> {
fn drop(&mut self) {
if let Some(event) = self.event.take() {
self.ring.cancel(E::cancel(ManuallyDrop::new(event)))
}
}
}
================================================
FILE: src/unix/listener.rs
================================================
use std::io;
use std::future::Future;
use std::os::unix::io::{RawFd};
use std::path::Path;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::{ready, Stream};
use nix::sys::socket::{self as nix_socket, SockFlag};
use crate::drive::{Drive, demo::DemoDriver};
use crate::ring::{Ring, Cancellation};
use super::UnixStream;
pub struct UnixListener<D: Drive = DemoDriver> {
ring: Ring<D>,
fd: RawFd,
active: Op,
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
enum Op {
Nothing = 0,
Accept,
Close,
Closed,
}
impl UnixListener {
pub fn bind(path: impl AsRef<Path>) -> io::Result<UnixListener> {
UnixListener::bind_on_driver(path, DemoDriver::default())
}
}
impl<D: Drive> UnixListener<D> {
pub fn bind_on_driver(path: impl AsRef<Path>, driver: D) -> io::Result<UnixListener<D>> {
let fd = super::socket()?;
let addr = iou::sqe::SockAddr::Unix(nix_socket::UnixAddr::new(path.as_ref()).unwrap());
nix_socket::bind(fd, &addr).map_err(|e| e.as_errno().unwrap_or(nix::errno::Errno::EIO))?;
nix_socket::listen(fd, 128).map_err(|e| e.as_errno().unwrap_or(nix::errno::Errno::EIO))?;
let ring = Ring::new(driver);
Ok(UnixListener {
active: Op::Nothing,
fd, ring,
})
}
pub fn close(&mut self) -> Close<D> where D: Unpin {
Pin::new(self).close_pinned()
}
pub fn close_pinned(self: Pin<&mut Self>) -> Close<D> {
Close { socket: self }
}
fn guard_op(self: Pin<&mut Self>, op: Op) {
let this = unsafe { Pin::get_unchecked_mut(self) };
if this.active == Op::Closed {
panic!("Attempted to perform IO on a closed UnixListener");
}
if this.active != Op::Nothing && this.active != op {
this.cancel();
}
this.active = op;
}
fn cancel(&mut self) {
if !matches!(self.active, Op::Nothing | Op::Closed) {
self.active = Op::Nothing;
self.ring.cancel(Cancellation::from(()));
}
}
fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.ring) }
}
fn confirm_close(self: Pin<&mut Self>) {
unsafe { Pin::get_unchecked_mut(self).active = Op::Closed; }
}
}
impl<D: Drive + Clone> UnixListener<D> {
pub fn accept(&mut self) -> Accept<'_, D> where D: Unpin {
Pin::new(self).accept_pinned()
}
pub fn accept_pinned(self: Pin<&mut Self>) -> Accept<'_, D> {
Accept { socket: self }
}
pub fn incoming(&mut self) -> Incoming<'_, D> where D: Unpin {
Pin::new(self).incoming_pinned()
}
pub fn incoming_pinned(self: Pin<&mut Self>) -> Incoming<'_, D> {
Incoming { accept: self.accept_pinned() }
}
pub fn poll_accept(mut self: Pin<&mut Self>, ctx: &mut Context<'_>)
-> Poll<io::Result<UnixStream<D>>>
{
self.as_mut().guard_op(Op::Accept);
let fd = self.fd;
let fd = ready!(self.as_mut().ring().poll(ctx, 1, |sqs| unsafe {
let mut sqe = sqs.single().unwrap();
sqe.prep_accept(fd, None, SockFlag::empty());
sqe
}))? as RawFd;
Poll::Ready(Ok(UnixStream::from_fd(fd, self.ring().clone())))
}
}
impl<D: Drive> Drop for UnixListener<D> {
fn drop(&mut self) {
match self.active {
Op::Closed => { }
Op::Nothing => unsafe { libc::close(self.fd); }
_ => self.cancel(),
}
}
}
pub struct Accept<'a, D: Drive> {
socket: Pin<&'a mut UnixListener<D>>,
}
impl<'a, D: Drive + Clone> Future for Accept<'a, D> {
type Output = io::Result<UnixStream<D>>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
self.socket.as_mut().poll_accept(ctx)
}
}
pub struct Incoming<'a, D: Drive> {
accept: Accept<'a, D>,
}
impl<'a, D: Drive> Incoming<'a, D> {
fn inner(self: Pin<&mut Self>) -> Pin<&mut Accept<'a, D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.accept) }
}
}
impl<'a, D: Drive + Clone> Stream for Incoming<'a, D> {
type Item = io::Result<UnixStream<D>>;
fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = ready!(self.inner().poll(ctx));
Poll::Ready(Some(next))
}
}
pub struct Close<'a, D: Drive> {
socket: Pin<&'a mut UnixListener<D>>,
}
impl<'a, D: Drive> Future for Close<'a, D> {
type Output = io::Result<()>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.socket.as_mut().guard_op(Op::Close);
let fd = self.socket.fd;
ready!(self.socket.as_mut().ring().poll(ctx, 1, |sqs| unsafe {
let mut sqe = sqs.single().unwrap();
sqe.prep_close(fd);
sqe
}))?;
self.socket.as_mut().confirm_close();
Poll::Ready(Ok(()))
}
}
================================================
FILE: src/unix/mod.rs
================================================
use std::io;
use std::os::unix::io::RawFd;
mod listener;
mod stream;
pub use listener::{UnixListener, Close, Accept, Incoming};
pub use stream::{UnixStream, Connect};
use nix::sys::socket as nix;
fn socket() -> io::Result<RawFd> {
match nix::socket(nix::AddressFamily::Unix, nix::SockType::Stream, nix::SockFlag::SOCK_CLOEXEC, None) {
Ok(fd) => Ok(fd),
Err(_) => Err(io::Error::last_os_error()),
}
}
fn socketpair() -> io::Result<(RawFd, RawFd)> {
match nix::socketpair(nix::AddressFamily::Unix, nix::SockType::Stream, None, nix::SockFlag::SOCK_CLOEXEC) {
Ok((fd1, fd2)) => Ok((fd1, fd2)),
Err(_) => Err(io::Error::last_os_error()),
}
}
================================================
FILE: src/unix/stream.rs
================================================
use std::io;
use std::future::Future;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::ready;
use futures_io::{AsyncRead, AsyncBufRead, AsyncWrite};
use iou::sqe::SockAddr;
use nix::sys::socket::UnixAddr;
use crate::drive::{Drive, demo::DemoDriver};
use crate::event;
use crate::ring::Ring;
use crate::Submission;
use super::{socket, socketpair};
use crate::net::TcpStream;
pub struct UnixStream<D: Drive = DemoDriver> {
inner: TcpStream<D>,
}
impl UnixStream {
pub fn connect(path: &impl AsRef<Path>) -> Connect {
UnixStream::connect_on_driver(path, DemoDriver::default())
}
pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
UnixStream::pair_on_driver(DemoDriver::default())
}
}
impl<D: Drive + Clone> UnixStream<D> {
pub fn connect_on_driver(path: &impl AsRef<Path>, driver: D) -> Connect<D> {
let fd = match socket() {
Ok(fd) => fd,
Err(e) => return Connect(Err(Some(e))),
};
let addr = Box::new(SockAddr::Unix(UnixAddr::new(path.as_ref()).unwrap()));
Connect(Ok(driver.submit(event::Connect { fd, addr })))
}
pub fn pair_on_driver(driver: D) -> io::Result<(UnixStream<D>, UnixStream<D>)> {
let (fd1, fd2) = socketpair()?;
let ring1 = Ring::new(driver.clone());
let ring2 = Ring::new(driver);
Ok((UnixStream::from_fd(fd1, ring1), UnixStream::from_fd(fd2, ring2)))
}
}
impl<D: Drive> UnixStream<D> {
pub(super) fn from_fd(fd: RawFd, ring: Ring<D>) -> UnixStream<D> {
UnixStream {
inner: TcpStream::from_fd(fd, ring),
}
}
#[inline(always)]
fn inner(self: Pin<&mut Self>) -> Pin<&mut TcpStream<D>> {
unsafe { Pin::map_unchecked_mut(self, |this| &mut this.inner) }
}
}
pub struct Connect<D: Drive = DemoDriver>(
Result<Submission<event::Connect, D>, Option<io::Error>>
);
impl<D: Drive + Clone> Future for Connect<D> {
type Output = io::Result<UnixStream<D>>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe {
match &mut Pin::get_unchecked_mut(self).0 {
Ok(submission) => {
let mut submission = Pin::new_unchecked(submission);
let (connect, result) = ready!(submission.as_mut().poll(ctx));
result?;
let driver = submission.driver().clone();
Poll::Ready(Ok(UnixStream::from_fd(connect.fd, Ring::new(driver))))
}
Err(err) => {
let err = err.take().expect("polled Connect future after completion");
Poll::Ready(Err(err))
}
}
}
}
}
impl<D: Drive> AsyncRead for UnixStream<D> {
fn poll_read(self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8])
-> Poll<io::Result<usize>>
{
self.inner().poll_read(ctx, buf)
}
}
impl<D: Drive> AsyncBufRead for UnixStream<D> {
fn poll_fill_buf(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
self.inner().poll_fill_buf(ctx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.inner().consume(amt)
}
}
impl<D: Drive> AsyncWrite for UnixStream<D> {
fn poll_write(self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[u8]) -> Poll<io::Result<usize>> {
self.inner().poll_write(ctx, slice)
}
fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.inner().poll_flush(ctx)
}
fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.inner().poll_close(ctx)
}
}
================================================
FILE: tests/basic-read.rs
================================================
use std::fs::File;
use std::os::unix::io::AsRawFd;
use ringbahn::Submission;
use ringbahn::event::Read;
use ringbahn::drive::demo;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn read_file() {
let file = File::open("props.txt").unwrap();
let read = Read {
fd: file.as_raw_fd(),
buf: vec![0; 4096].into(),
offset: 0,
};
let (read, result) = futures::executor::block_on(Submission::new(read, demo::driver()));
assert!(result.is_ok());
assert_eq!(&read.buf[0..ASSERT.len()], ASSERT);
}
================================================
FILE: tests/basic-readv.rs
================================================
use std::fs::File;
use std::os::unix::io::AsRawFd;
use ringbahn::Submission;
use ringbahn::event::ReadVectored;
use ringbahn::drive::demo;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn readv_file() {
let file = File::open("props.txt").unwrap();
let vec1: Box<[u8]> = Box::new([0; 4]);
let vec2: Box<[u8]> = Box::new([0; 5]);
let vec3: Box<[u8]> = Box::new([0; 10]);
let readv = ReadVectored {
fd: file.as_raw_fd(),
bufs: vec![vec1, vec2, vec3].into_boxed_slice(),
offset: 0,
};
let (readv, result) = futures::executor::block_on(Submission::new(readv, demo::driver()));
assert!(result.is_ok());
assert_eq!(readv.bufs[0][..], ASSERT[0..4]);
assert_eq!(readv.bufs[1][..], ASSERT[4..9]);
assert_eq!(readv.bufs[2][..], ASSERT[9..19]);
}
================================================
FILE: tests/basic-write.rs
================================================
use std::io::Read;
use std::os::unix::io::AsRawFd;
use ringbahn::Submission;
use ringbahn::event::Write;
use ringbahn::drive::demo;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn write_file() {
let mut file = tempfile::tempfile().unwrap();
let write = Write {
fd: file.as_raw_fd(),
buf: Box::from(ASSERT),
offset: 0,
};
let (_, result) = futures::executor::block_on(Submission::new(write, demo::driver()));
assert_eq!(result.unwrap() as usize, ASSERT.len());
let mut buf = vec![];
assert_eq!(file.read_to_end(&mut buf).unwrap(), ASSERT.len());
assert_eq!(&buf[0..ASSERT.len()], ASSERT);
}
================================================
FILE: tests/basic-writev.rs
================================================
use std::io::Read;
use std::os::unix::io::AsRawFd;
use ringbahn::Submission;
use ringbahn::event::WriteVectored;
use ringbahn::drive::demo;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn writev_file() {
let mut file = tempfile::tempfile().unwrap();
let vec1 = &ASSERT[0..4];
let vec2 = &ASSERT[4..9];
let vec3 = &ASSERT[9..];
let writev = WriteVectored {
fd: file.as_raw_fd(),
bufs: vec![vec1.into(), vec2.into(), vec3.into()].into(),
offset: 0,
};
let (_, result) = futures::executor::block_on(Submission::new(writev, demo::driver()));
assert_eq!(result.unwrap() as usize, ASSERT.len());
let mut buf = vec![];
assert_eq!(file.read_to_end(&mut buf).unwrap(), ASSERT.len());
assert_eq!(&buf[0..ASSERT.len()], ASSERT);
}
================================================
FILE: tests/file-close.rs
================================================
use futures::{AsyncReadExt, AsyncWriteExt};
use ringbahn::fs::File;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn read_and_close_file() {
futures::executor::block_on(async move {
let mut file = File::open("props.txt").await.unwrap();
let mut buf = vec![0; 4096];
assert!(file.read(&mut buf).await.is_ok());
assert_eq!(&buf[0..ASSERT.len()], ASSERT);
file.close().await.unwrap();
});
}
================================================
FILE: tests/file-read.rs
================================================
use futures::AsyncReadExt;
use ringbahn::fs::File;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn read_file() {
futures::executor::block_on(async move {
let mut file = File::open("props.txt").await.unwrap();
let mut buf = vec![0; 4096];
assert!(file.read(&mut buf).await.is_ok());
assert_eq!(&buf[0..ASSERT.len()], ASSERT);
});
}
#[test]
fn read_to_end() {
futures::executor::block_on(async move {
let mut file = File::open("props.txt").await.unwrap();
let mut buf = Vec::new();
assert!(file.read_to_end(&mut buf).await.is_ok());
assert_eq!(&buf[0..ASSERT.len()], ASSERT);
});
}
================================================
FILE: tests/file-seek.rs
================================================
use std::io::SeekFrom;
use futures::{AsyncSeekExt, AsyncReadExt, AsyncWriteExt};
use ringbahn::fs::File;
#[test]
fn seek_to_end() {
futures::executor::block_on(async move {
let mut file = File::open("props.txt").await.unwrap();
assert_eq!(file.seek(SeekFrom::End(0)).await.unwrap(), 792);
});
}
#[test]
fn seek_and_then_io() {
futures::executor::block_on(async move {
let mut file: File = tempfile::tempfile().unwrap().into();
assert_eq!(file.seek(SeekFrom::End(0)).await.unwrap(), 0);
file.write(b"abcdef").await.unwrap();
let mut buf = [0; 16];
assert_eq!(file.seek(SeekFrom::Start(0)).await.unwrap(), 0);
file.read(&mut buf).await.unwrap();
assert_eq!(&buf[0..6], b"abcdef");
});
}
================================================
FILE: tests/file-write.rs
================================================
use std::io::SeekFrom;
use futures::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
use ringbahn::fs::File;
const ASSERT: &[u8] = b"But this formidable power of death -";
#[test]
fn write_file() {
let file = tempfile::tempfile().unwrap();
let mut file = File::from(file);
futures::executor::block_on(async move {
assert_eq!(file.write(ASSERT).await.unwrap(), ASSERT.len());
let mut buf = vec![];
assert!(file.seek(SeekFrom::Start(0)).await.is_ok());
assert_eq!(file.read_to_end(&mut buf).await.unwrap(), ASSERT.len());
assert_eq!(&buf[0..ASSERT.len()], ASSERT);
});
}
#[test]
fn select_complete_many_futures() {
async fn act() {
let file = tempfile::tempfile().unwrap();
let mut file = File::from(file);
file.write_all(b"hello, world!").await.unwrap();
}
futures::executor::block_on(async move {
use futures::FutureExt;
let mut f1 = Box::pin(act().fuse());
let mut f2 = Box::pin(act().fuse());
let mut f3 = Box::pin(act().fuse());
let mut f4 = Box::pin(act().fuse());
let mut f5 = Box::pin(act().fuse());
let mut f6 = Box::pin(act().fuse());
let mut f7 = Box::pin(act().fuse());
let mut f8 = Box::pin(act().fuse());
loop {
futures::select! {
_ = f1 => (),
_ = f2 => (),
_ = f3 => (),
_ = f4 => (),
_ = f5 => (),
_ = f6 => (),
_ = f7 => (),
_ = f8 => (),
complete => break,
}
}
});
}
================================================
FILE: tests/println.rs
================================================
use ringbahn::drive::demo;
#[test]
fn println() {
futures::executor::block_on(async {
ringbahn::println!(demo::driver(), "Hello, world!").await
})
}
#[test]
fn print() {
futures::executor::block_on(async {
ringbahn::print!(demo::driver(), "Hello, world!").await
})
}
#[test]
fn eprintln() {
futures::executor::block_on(async {
ringbahn::eprintln!(demo::driver(), "Hello, world!").await
})
}
#[test]
fn eprint() {
futures::executor::block_on(async {
ringbahn::eprint!(demo::driver(), "Hello, world!").await
})
}
================================================
FILE: tests/registered-fd.rs
================================================
use std::os::unix::io::IntoRawFd;
use ringbahn::event::*;
use ringbahn::drive::{demo, Drive};
use iou::sqe::*;
#[test]
fn test_registered_fd_ops() {
// open and register file
let file = std::fs::File::open("props.txt").unwrap();
let fd = demo::registrar().unwrap()
.register_files(&[file.into_raw_fd()]).unwrap().next().unwrap();
futures::executor::block_on(async move {
// read file and print contents to stdout
let buf = vec![0; 1024].into_boxed_slice();
let (event, result) = demo::driver().submit(Read { fd, buf, offset: 0 }).await;
let n = result.unwrap() as _;
let data = String::from_utf8_lossy(&event.buf[..n]).to_owned();
ringbahn::println!(demo::driver(), "{}", data).await;
// statx file and print statx to stdout
let (event, result) = demo::driver().submit(Statx::without_path(
fd,
StatxFlags::empty(),
StatxMode::all(),
)).await;
result.unwrap();
ringbahn::println!(demo::driver(), "{:?}", event.statx).await;
// close file with ringbahn
let (_, result) = demo::driver().submit(Close { fd }).await;
result.unwrap();
});
}
================================================
FILE: tests/stdio.rs
================================================
use futures::AsyncWriteExt;
use ringbahn::{drive::demo};
const ASSERT: &[u8] = b"Hello, world!\n";
#[test]
fn write_stdout() {
futures::executor::block_on(async {
let n = ringbahn::io::stdout().write(ASSERT).await.unwrap();
assert_eq!(n, ASSERT.len());
let mut stdout = ringbahn::io::stdout_on_driver(demo::driver());
let n = stdout.write(ASSERT).await.unwrap();
assert_eq!(n, ASSERT.len());
});
}
gitextract_7bu16q06/
├── .gitignore
├── Cargo.toml
├── README.md
├── examples/
│ ├── copy-file.rs
│ ├── echo-client.rs
│ ├── echo-server.rs
│ └── read-event.rs
├── props.txt
├── src/
│ ├── buf.rs
│ ├── drive/
│ │ ├── demo.rs
│ │ └── mod.rs
│ ├── event/
│ │ ├── accept.rs
│ │ ├── close.rs
│ │ ├── connect.rs
│ │ ├── epoll_ctl.rs
│ │ ├── fadvise.rs
│ │ ├── fallocate.rs
│ │ ├── files_update.rs
│ │ ├── fsync.rs
│ │ ├── mod.rs
│ │ ├── openat.rs
│ │ ├── provide_buffers.rs
│ │ ├── read.rs
│ │ ├── readv.rs
│ │ ├── recv.rs
│ │ ├── send.rs
│ │ ├── splice.rs
│ │ ├── statx.rs
│ │ ├── timeout.rs
│ │ ├── write.rs
│ │ └── writev.rs
│ ├── fs.rs
│ ├── io.rs
│ ├── lib.rs
│ ├── net/
│ │ ├── listener.rs
│ │ ├── mod.rs
│ │ └── stream.rs
│ ├── ring/
│ │ ├── cancellation.rs
│ │ ├── completion.rs
│ │ └── mod.rs
│ ├── submission.rs
│ └── unix/
│ ├── listener.rs
│ ├── mod.rs
│ └── stream.rs
└── tests/
├── basic-read.rs
├── basic-readv.rs
├── basic-write.rs
├── basic-writev.rs
├── file-close.rs
├── file-read.rs
├── file-seek.rs
├── file-write.rs
├── println.rs
├── registered-fd.rs
└── stdio.rs
SYMBOL INDEX (367 symbols across 50 files)
FILE: examples/copy-file.rs
function main (line 6) | fn main() {
FILE: examples/echo-client.rs
function main (line 8) | fn main() {
FILE: examples/echo-server.rs
function main (line 7) | fn main() {
FILE: examples/read-event.rs
function main (line 6) | fn main() -> io::Result<()> {
FILE: src/buf.rs
type Buffer (line 10) | pub struct Buffer {
method buffered_from_read (line 17) | pub fn buffered_from_read(&self) -> &[u8] {
method fill_buf (line 21) | pub fn fill_buf(&mut self, fill: impl FnOnce(&mut [u8]) -> Poll<io::Re...
method consume (line 37) | pub fn consume(&mut self, amt: usize) {
method clear (line 41) | pub fn clear(&mut self) {
method into_boxed_slice (line 46) | pub fn into_boxed_slice(self) -> Option<Box<[u8]>> {
method cancellation (line 50) | pub fn cancellation(&mut self) -> Cancellation {
FILE: src/drive/demo.rs
constant ENTRIES (line 15) | const ENTRIES: u32 = 32;
type Queues (line 21) | type Queues = (
type DemoDriver (line 31) | pub struct DemoDriver {
method poll_submit_inner (line 36) | fn poll_submit_inner(&mut self, ctx: &mut Context<'_>, sq: &mut Submis...
method default (line 60) | fn default() -> Self {
method clone (line 66) | fn clone(&self) -> DemoDriver {
method poll_prepare (line 72) | fn poll_prepare<'cx>(
method poll_submit (line 89) | fn poll_submit(
function driver (line 98) | pub fn driver() -> DemoDriver {
function registrar (line 108) | pub fn registrar() -> Option<&'static Registrar<'static>> {
function init (line 117) | fn init() -> Queues {
function start_completion_thread (line 128) | fn start_completion_thread() {
FILE: src/drive/mod.rs
type Completion (line 20) | pub struct Completion<'cx> {
function new (line 26) | pub(crate) fn new(mut sqe: SQE<'_>, _sqes: SQEs<'_>, cx: &mut Context<'c...
type Drive (line 41) | pub trait Drive {
method poll_prepare (line 55) | fn poll_prepare<'cx>(
method poll_submit (line 76) | fn poll_submit(
method submit (line 81) | fn submit<E: Event>(self, event: E) -> Submission<E, Self> where Self:...
FILE: src/event/accept.rs
type Accept (line 9) | pub struct Accept<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 24) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/close.rs
type Close (line 7) | pub struct Close<FD = RawFd> {
method sqes_needed (line 12) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 14) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
FILE: src/event/connect.rs
type Connect (line 9) | pub struct Connect<FD = RawFd> {
method sqes_needed (line 15) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 17) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 23) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/epoll_ctl.rs
type EpollCtl (line 8) | pub struct EpollCtl {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 24) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/fadvise.rs
type Fadvise (line 8) | pub struct Fadvise<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
FILE: src/event/fallocate.rs
type Fallocate (line 8) | pub struct Fallocate<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
FILE: src/event/files_update.rs
type FilesUpdate (line 6) | pub struct FilesUpdate {
method sqes_needed (line 12) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 14) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 20) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/fsync.rs
type Fsync (line 8) | pub struct Fsync<FD = RawFd> {
method sqes_needed (line 14) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 16) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
FILE: src/event/mod.rs
type Event (line 60) | pub trait Event {
method sqes_needed (line 61) | fn sqes_needed(&self) -> u32;
method prepare (line 79) | unsafe fn prepare<'a>(&mut self, sqs: &mut SQEs<'a>) -> SQE<'a>;
method cancel (line 86) | fn cancel(_: ManuallyDrop<Self>) -> Cancellation where Self: Sized {
FILE: src/event/openat.rs
type OpenAt (line 11) | pub struct OpenAt {
method without_dir (line 19) | pub fn without_dir(path: impl AsRef<Path>, flags: OFlag, mode: Mode) -...
method sqes_needed (line 26) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 28) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 34) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/provide_buffers.rs
type ProvideBuffers (line 6) | pub struct ProvideBuffers {
method sqes_needed (line 14) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 16) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 22) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
type RemoveBuffers (line 27) | pub struct RemoveBuffers {
method sqes_needed (line 33) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 35) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
FILE: src/event/read.rs
type Read (line 9) | pub struct Read<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 24) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
type ReadFixed (line 29) | pub struct ReadFixed<FD = RawFd> {
method sqes_needed (line 36) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 38) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 44) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/readv.rs
type ReadVectored (line 10) | pub struct ReadVectored<FD = RawFd> {
function as_iovecs (line 17) | fn as_iovecs(buffers: &mut [Box<[u8]>]) -> &mut [IoSliceMut] {
method sqes_needed (line 37) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 39) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 45) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/recv.rs
type Recv (line 9) | pub struct Recv<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 24) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/send.rs
type Send (line 9) | pub struct Send<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 24) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/splice.rs
type Splice (line 7) | pub struct Splice {
method sqes_needed (line 17) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 19) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
FILE: src/event/statx.rs
type Statx (line 12) | pub struct Statx<FD = RawFd> {
method without_dir (line 21) | pub fn without_dir(path: impl AsRef<Path>, flags: StatxFlags, mask: St...
function without_path (line 29) | pub fn without_path(fd: FD, mut flags: StatxFlags, mask: StatxMode) -> S...
method sqes_needed (line 41) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 43) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 49) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/timeout.rs
type StaticTimeout (line 8) | pub struct StaticTimeout {
method new (line 15) | pub const fn new(duration: Duration, events: u32, flags: TimeoutFlags)...
method sqes_needed (line 24) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 26) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
type Timeout (line 33) | pub struct Timeout {
method new (line 40) | pub fn new(duration: Duration, events: u32, flags: TimeoutFlags) -> Ti...
method sqes_needed (line 49) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 51) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 57) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
function timespec (line 62) | const fn timespec(duration: Duration) -> uring_sys::__kernel_timespec {
FILE: src/event/write.rs
type Write (line 9) | pub struct Write<FD = RawFd> {
method sqes_needed (line 16) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 18) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 24) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
type WriteFixed (line 29) | pub struct WriteFixed<FD = RawFd> {
method sqes_needed (line 36) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 38) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 44) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/event/writev.rs
type WriteVectored (line 10) | pub struct WriteVectored<FD = RawFd> {
function iovecs (line 17) | fn iovecs(&self) -> &[IoSlice] {
method sqes_needed (line 23) | fn sqes_needed(&self) -> u32 { 1 }
method prepare (line 25) | unsafe fn prepare<'sq>(&mut self, sqs: &mut SQEs<'sq>) -> SQE<'sq> {
method cancel (line 31) | fn cancel(this: ManuallyDrop<Self>) -> Cancellation {
FILE: src/fs.rs
type FileBuf (line 24) | type FileBuf = Either<Buffer, Box<libc::statx>>;
type File (line 27) | pub struct File<D: Drive = DemoDriver> {
method open (line 47) | pub fn open(path: impl AsRef<Path>) -> Open {
method create (line 52) | pub fn create(path: impl AsRef<Path>) -> Create {
method from (line 295) | fn from(file: fs::File) -> File {
type Op (line 36) | enum Op {
function open_on_driver (line 59) | pub fn open_on_driver(path: impl AsRef<Path>, driver: D) -> Open<D> {
function create_on_driver (line 65) | pub fn create_on_driver(path: impl AsRef<Path>, driver: D) -> Create<D> {
function run_on_driver (line 73) | pub fn run_on_driver(file: fs::File, driver: D) -> File<D> {
function from_fd (line 78) | fn from_fd(fd: RawFd, driver: D) -> File<D> {
function read_buffered (line 93) | pub fn read_buffered(&self) -> &[u8] {
function guard_op (line 99) | fn guard_op(self: Pin<&mut Self>, op: Op) {
function cancel (line 110) | fn cancel(&mut self) {
function poll_file_size (line 116) | fn poll_file_size(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Po...
function split (line 136) | fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut FileBuf, &mut...
function split_with_buf (line 144) | fn split_with_buf(self: Pin<&mut Self>)
function split_with_statx (line 153) | fn split_with_statx(self: Pin<&mut Self>)
function ring (line 165) | fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
function buf (line 170) | fn buf(self: Pin<&mut Self>) -> &mut Buffer {
function pos (line 175) | fn pos(self: Pin<&mut Self>) -> &mut u64 {
function confirm_close (line 179) | fn confirm_close(self: Pin<&mut Self>) {
method poll_read (line 185) | fn poll_read(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut ...
method poll_fill_buf (line 196) | fn poll_fill_buf(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Pol...
method consume (line 213) | fn consume(self: Pin<&mut Self>, amt: usize) {
method poll_write (line 219) | fn poll_write(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[...
method poll_flush (line 238) | fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::R...
method poll_close (line 243) | fn poll_close(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<i...
method poll_seek (line 259) | fn poll_seek(mut self: Pin<&mut Self>, ctx: &mut Context, pos: io::SeekF...
function from (line 301) | fn from(mut file: File<D>) -> fs::File {
method drop (line 311) | fn drop(&mut self) {
type Open (line 321) | pub struct Open<D: Drive = DemoDriver>(Submission<OpenAt, D>);
function inner (line 324) | fn inner(self: Pin<&mut Self>) -> Pin<&mut Submission<OpenAt, D>> {
type Output (line 330) | type Output = io::Result<File<D>>;
method poll (line 332) | fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<...
type Create (line 342) | pub struct Create<D: Drive = DemoDriver>(Submission<OpenAt, D>);
function inner (line 345) | fn inner(self: Pin<&mut Self>) -> Pin<&mut Submission<OpenAt, D>> {
type Output (line 351) | type Output = io::Result<File<D>>;
method poll (line 353) | fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<...
FILE: src/io.rs
function __print (line 52) | pub async fn __print<D: Drive>(driver: D, bytes: impl Into<Cow<'static, ...
function __eprint (line 62) | pub async fn __eprint<D: Drive>(driver: D, bytes: impl Into<Cow<'static,...
type Print (line 71) | struct Print<D: Drive> {
function split (line 79) | fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, RawFd, &[u8], &mut...
type Output (line 89) | type Output = io::Result<()>;
method poll (line 91) | fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Outpu...
type Stdout (line 114) | pub struct Stdout<D: Drive> {
function stdout (line 131) | pub fn stdout() -> Stdout<DemoDriver> {
function stdout_on_driver (line 136) | pub fn stdout_on_driver<D: Drive>(driver: D) -> Stdout<D> {
function split (line 145) | fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut Buffer) {
method poll_write (line 154) | fn poll_write(self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[u8])
method poll_flush (line 173) | fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::R...
method poll_close (line 178) | fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::R...
method as_raw_fd (line 184) | fn as_raw_fd(&self) -> RawFd {
FILE: src/net/listener.rs
type TcpListener (line 17) | pub struct TcpListener<D: Drive = DemoDriver> {
method bind (line 33) | pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
type Op (line 25) | enum Op {
function bind_on_driver (line 39) | pub fn bind_on_driver<A: ToSocketAddrs>(addr: A, driver: D) -> io::Resul...
function close (line 54) | pub fn close(&mut self) -> Close<D> where D: Unpin {
function close_pinned (line 58) | pub fn close_pinned(self: Pin<&mut Self>) -> Close<D> {
function guard_op (line 62) | fn guard_op(self: Pin<&mut Self>, op: Op) {
function cancel (line 72) | fn cancel(&mut self) {
function drop_addr (line 83) | fn drop_addr(self: Pin<&mut Self>) {
function ring (line 87) | fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
function split (line 91) | fn split(self: Pin<&mut Self>)
function split_with_addr (line 100) | fn split_with_addr(self: Pin<&mut Self>)
function confirm_close (line 110) | fn confirm_close(self: Pin<&mut Self>) {
function accept (line 116) | pub fn accept(&mut self) -> Accept<'_, D> where D: Unpin {
function accept_pinned (line 120) | pub fn accept_pinned(self: Pin<&mut Self>) -> Accept<'_, D> {
function incoming (line 124) | pub fn incoming(&mut self) -> Incoming<'_, D> where D: Unpin {
function incoming_pinned (line 128) | pub fn incoming_pinned(self: Pin<&mut Self>) -> Incoming<'_, D> {
function accept_no_addr (line 132) | pub fn accept_no_addr(&mut self) -> AcceptNoAddr<'_, D> where D: Unpin {
function accept_no_addr_pinned (line 136) | pub fn accept_no_addr_pinned(self: Pin<&mut Self>) -> AcceptNoAddr<'_, D> {
function incoming_no_addr (line 140) | pub fn incoming_no_addr(&mut self) -> IncomingNoAddr<'_, D> where D: Unp...
function incoming_no_addr_pinned (line 144) | pub fn incoming_no_addr_pinned(self: Pin<&mut Self>) -> IncomingNoAddr<'...
function poll_accept (line 148) | pub fn poll_accept(mut self: Pin<&mut Self>, ctx: &mut Context<'_>)
function poll_accept_no_addr (line 173) | pub fn poll_accept_no_addr(mut self: Pin<&mut Self>, ctx: &mut Context<'_>)
method drop (line 190) | fn drop(&mut self) {
type Accept (line 199) | pub struct Accept<'a, D: Drive> {
type Output (line 204) | type Output = io::Result<(TcpStream<D>, SocketAddr)>;
method poll (line 206) | fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::O...
type AcceptNoAddr (line 211) | pub struct AcceptNoAddr<'a, D: Drive> {
type Output (line 216) | type Output = io::Result<TcpStream<D>>;
method poll (line 218) | fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::O...
type Incoming (line 223) | pub struct Incoming<'a, D: Drive> {
function inner (line 228) | fn inner(self: Pin<&mut Self>) -> Pin<&mut Accept<'a, D>> {
type Item (line 234) | type Item = io::Result<(TcpStream<D>, SocketAddr)>;
method poll_next (line 236) | fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option...
type IncomingNoAddr (line 242) | pub struct IncomingNoAddr<'a, D: Drive> {
function inner (line 247) | fn inner(self: Pin<&mut Self>) -> Pin<&mut AcceptNoAddr<'a, D>> {
type Item (line 253) | type Item = io::Result<TcpStream<D>>;
method poll_next (line 255) | fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option...
type Close (line 261) | pub struct Close<'a, D: Drive> {
type Output (line 266) | type Output = io::Result<()>;
method poll (line 268) | fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Res...
FILE: src/net/mod.rs
function socket (line 13) | fn socket<A: ToSocketAddrs>(addr: A, protocol: nix::SockProtocol) -> io:...
FILE: src/net/stream.rs
type TcpStream (line 21) | pub struct TcpStream<D: Drive = DemoDriver> {
method connect (line 38) | pub fn connect<A: ToSocketAddrs>(addr: A) -> Connect {
type Op (line 29) | enum Op {
function connect_on_driver (line 44) | pub fn connect_on_driver<A: ToSocketAddrs>(addr: A, driver: D) -> Connec...
function from_fd (line 55) | pub(crate) fn from_fd(fd: RawFd, ring: Ring<D>) -> TcpStream<D> {
function guard_op (line 63) | fn guard_op(self: Pin<&mut Self>, op: Op) {
function cancel (line 73) | fn cancel(&mut self) {
function ring (line 79) | fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
function buf (line 84) | fn buf(self: Pin<&mut Self>) -> &mut Buffer {
function split (line 89) | fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut Buffer, &mut ...
function confirm_close (line 96) | fn confirm_close(self: Pin<&mut Self>) {
type Connect (line 101) | pub struct Connect<D: Drive = DemoDriver>(
type Output (line 106) | type Output = io::Result<TcpStream<D>>;
method poll (line 108) | fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Outpu...
function project (line 125) | fn project(self: Pin<&mut Self>)
method poll_read (line 138) | fn poll_read(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut ...
method poll_fill_buf (line 149) | fn poll_fill_buf(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Pol...
method consume (line 165) | fn consume(self: Pin<&mut Self>, amt: usize) {
method poll_write (line 171) | fn poll_write(mut self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[...
method poll_flush (line 189) | fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::R...
method poll_close (line 194) | fn poll_close(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<i...
method drop (line 210) | fn drop(&mut self) {
FILE: src/ring/cancellation.rs
type Cancellation (line 16) | pub struct Cancellation {
method new (line 114) | fn new<T: Cancel>(object: T) -> Cancellation {
method from (line 121) | fn from(object: T) -> Cancellation {
method from (line 127) | fn from(object: Option<T>) -> Cancellation {
method from (line 133) | fn from(buffer: Buffer) -> Cancellation {
method from (line 139) | fn from(object: Either<T, U>) -> Cancellation {
type Cancel (line 22) | pub unsafe trait Cancel {
method into_raw (line 23) | fn into_raw(self) -> (*mut (), usize);
method drop_raw (line 24) | unsafe fn drop_raw(data: *mut (), metadata: usize);
method into_raw (line 28) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 32) | unsafe fn drop_raw(data: *mut (), _: usize) {
method into_raw (line 38) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 43) | unsafe fn drop_raw(data: *mut (), len: usize) {
method into_raw (line 55) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 60) | unsafe fn drop_raw(data: *mut (), metadata: usize) {
method into_raw (line 67) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 71) | unsafe fn drop_raw(data: *mut (), metadata: usize) {
method into_raw (line 77) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 81) | unsafe fn drop_raw(data: *mut (), _: usize) {
method into_raw (line 87) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 91) | unsafe fn drop_raw(_: *mut (), _: usize) { }
method into_raw (line 100) | fn into_raw(self) -> (*mut (), usize) {
method drop_raw (line 106) | unsafe fn drop_raw(data: *mut (), metadata: usize) {
type TraitObject (line 49) | struct TraitObject {
type CancelNarrow (line 94) | pub unsafe trait CancelNarrow: Cancel { }
method drop (line 148) | fn drop(&mut self) {
FILE: src/ring/completion.rs
type Completion (line 23) | pub struct Completion {
method new (line 37) | pub fn new(waker: Waker) -> Completion {
method addr (line 45) | pub fn addr(&self) -> u64 {
method check (line 52) | pub fn check(self, waker: &Waker) -> Result<io::Result<u32>, Completio...
method cancel (line 72) | pub fn cancel(self, callback: Cancellation) {
method complete (line 88) | fn complete(self, result: io::Result<u32>) {
type State (line 27) | enum State {
function complete (line 105) | pub fn complete(cqe: CQE) {
FILE: src/ring/mod.rs
type Ring (line 31) | pub struct Ring<D: Drive> {
type State (line 36) | enum State {
method cancel (line 204) | fn cancel(&mut self, cancellation: Cancellation) {
method default (line 46) | fn default() -> Ring<D> {
method clone (line 52) | fn clone(&self) -> Ring<D> {
function new (line 60) | pub fn new(driver: D) -> Ring<D> {
function driver (line 67) | pub fn driver(&self) -> &D {
function poll (line 78) | pub fn poll(
function poll_prepare (line 105) | fn poll_prepare(
function poll_submit (line 135) | fn poll_submit(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<()> {
function poll_complete (line 148) | fn poll_complete(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io...
function cancel (line 184) | pub fn cancel(&mut self, cancellation: Cancellation) {
function cancel_pinned (line 191) | pub fn cancel_pinned(self: Pin<&mut Self>, cancellation: Cancellation) {
function split (line 195) | fn split(self: Pin<&mut Self>) -> (Pin<&mut D>, &mut State) {
FILE: src/submission.rs
type Submission (line 12) | pub struct Submission<E: Event, D: Drive> {
function new (line 19) | pub fn new(event: E, driver: D) -> Submission<E, D> {
function driver (line 27) | pub fn driver(&self) -> &D {
function replace_event (line 31) | pub fn replace_event(self: Pin<&mut Self>, event: E) {
function split (line 39) | fn split(self: Pin<&mut Self>) -> (Pin<&mut Ring<D>>, &mut Option<E>) {
type Output (line 51) | type Output = (E, io::Result<u32>);
method poll (line 53) | fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Outpu...
method drop (line 69) | fn drop(&mut self) {
FILE: src/unix/listener.rs
type UnixListener (line 16) | pub struct UnixListener<D: Drive = DemoDriver> {
method bind (line 31) | pub fn bind(path: impl AsRef<Path>) -> io::Result<UnixListener> {
type Op (line 23) | enum Op {
function bind_on_driver (line 37) | pub fn bind_on_driver(path: impl AsRef<Path>, driver: D) -> io::Result<U...
function close (line 49) | pub fn close(&mut self) -> Close<D> where D: Unpin {
function close_pinned (line 53) | pub fn close_pinned(self: Pin<&mut Self>) -> Close<D> {
function guard_op (line 57) | fn guard_op(self: Pin<&mut Self>, op: Op) {
function cancel (line 68) | fn cancel(&mut self) {
function ring (line 75) | fn ring(self: Pin<&mut Self>) -> Pin<&mut Ring<D>> {
function confirm_close (line 79) | fn confirm_close(self: Pin<&mut Self>) {
function accept (line 85) | pub fn accept(&mut self) -> Accept<'_, D> where D: Unpin {
function accept_pinned (line 89) | pub fn accept_pinned(self: Pin<&mut Self>) -> Accept<'_, D> {
function incoming (line 93) | pub fn incoming(&mut self) -> Incoming<'_, D> where D: Unpin {
function incoming_pinned (line 97) | pub fn incoming_pinned(self: Pin<&mut Self>) -> Incoming<'_, D> {
function poll_accept (line 101) | pub fn poll_accept(mut self: Pin<&mut Self>, ctx: &mut Context<'_>)
method drop (line 117) | fn drop(&mut self) {
type Accept (line 126) | pub struct Accept<'a, D: Drive> {
type Output (line 131) | type Output = io::Result<UnixStream<D>>;
method poll (line 133) | fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::O...
type Incoming (line 138) | pub struct Incoming<'a, D: Drive> {
function inner (line 143) | fn inner(self: Pin<&mut Self>) -> Pin<&mut Accept<'a, D>> {
type Item (line 149) | type Item = io::Result<UnixStream<D>>;
method poll_next (line 151) | fn poll_next(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option...
type Close (line 158) | pub struct Close<'a, D: Drive> {
type Output (line 163) | type Output = io::Result<()>;
method poll (line 165) | fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Res...
FILE: src/unix/mod.rs
function socket (line 12) | fn socket() -> io::Result<RawFd> {
function socketpair (line 19) | fn socketpair() -> io::Result<(RawFd, RawFd)> {
FILE: src/unix/stream.rs
type UnixStream (line 22) | pub struct UnixStream<D: Drive = DemoDriver> {
method connect (line 27) | pub fn connect(path: &impl AsRef<Path>) -> Connect {
method pair (line 31) | pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
function connect_on_driver (line 37) | pub fn connect_on_driver(path: &impl AsRef<Path>, driver: D) -> Connect<...
function pair_on_driver (line 46) | pub fn pair_on_driver(driver: D) -> io::Result<(UnixStream<D>, UnixStrea...
function from_fd (line 55) | pub(super) fn from_fd(fd: RawFd, ring: Ring<D>) -> UnixStream<D> {
function inner (line 62) | fn inner(self: Pin<&mut Self>) -> Pin<&mut TcpStream<D>> {
type Connect (line 67) | pub struct Connect<D: Drive = DemoDriver>(
type Output (line 72) | type Output = io::Result<UnixStream<D>>;
method poll (line 74) | fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Outpu...
method poll_read (line 94) | fn poll_read(self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8])
method poll_fill_buf (line 102) | fn poll_fill_buf(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io...
method consume (line 106) | fn consume(self: Pin<&mut Self>, amt: usize) {
method poll_write (line 112) | fn poll_write(self: Pin<&mut Self>, ctx: &mut Context<'_>, slice: &[u8])...
method poll_flush (line 116) | fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::R...
method poll_close (line 120) | fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::R...
FILE: tests/basic-read.rs
constant ASSERT (line 8) | const ASSERT: &[u8] = b"But this formidable power of death -";
function read_file (line 11) | fn read_file() {
FILE: tests/basic-readv.rs
constant ASSERT (line 8) | const ASSERT: &[u8] = b"But this formidable power of death -";
function readv_file (line 11) | fn readv_file() {
FILE: tests/basic-write.rs
constant ASSERT (line 8) | const ASSERT: &[u8] = b"But this formidable power of death -";
function write_file (line 11) | fn write_file() {
FILE: tests/basic-writev.rs
constant ASSERT (line 8) | const ASSERT: &[u8] = b"But this formidable power of death -";
function writev_file (line 11) | fn writev_file() {
FILE: tests/file-close.rs
constant ASSERT (line 5) | const ASSERT: &[u8] = b"But this formidable power of death -";
function read_and_close_file (line 8) | fn read_and_close_file() {
FILE: tests/file-read.rs
constant ASSERT (line 5) | const ASSERT: &[u8] = b"But this formidable power of death -";
function read_file (line 8) | fn read_file() {
function read_to_end (line 18) | fn read_to_end() {
FILE: tests/file-seek.rs
function seek_to_end (line 7) | fn seek_to_end() {
function seek_and_then_io (line 15) | fn seek_and_then_io() {
FILE: tests/file-write.rs
constant ASSERT (line 7) | const ASSERT: &[u8] = b"But this formidable power of death -";
function write_file (line 10) | fn write_file() {
function select_complete_many_futures (line 24) | fn select_complete_many_futures() {
FILE: tests/println.rs
function println (line 4) | fn println() {
function print (line 11) | fn print() {
function eprintln (line 18) | fn eprintln() {
function eprint (line 25) | fn eprint() {
FILE: tests/registered-fd.rs
function test_registered_fd_ops (line 9) | fn test_registered_fd_ops() {
FILE: tests/stdio.rs
constant ASSERT (line 5) | const ASSERT: &[u8] = b"Hello, world!\n";
function write_stdout (line 8) | fn write_stdout() {
Condensed preview — 55 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (108K chars).
[
{
"path": ".gitignore",
"chars": 19,
"preview": "/target\nCargo.lock\n"
},
{
"path": "Cargo.toml",
"chars": 575,
"preview": "[package]\nname = \"ringbahn\"\nversion = \"0.0.0-experimental.3\"\nauthors = [\"Without Boats <woboats@gmail.com>\"]\ndescription"
},
{
"path": "README.md",
"chars": 1284,
"preview": "# ringbahn - a safe interface to io-uring\n\nThe Berlin Ringbahn is a double-tracked commuter rail line which forms a comp"
},
{
"path": "examples/copy-file.rs",
"chars": 485,
"preview": "use futures::io::AsyncReadExt;\nuse futures::io::AsyncWriteExt;\n\nuse ringbahn::fs::File;\n\nfn main() {\n futures::execut"
},
{
"path": "examples/echo-client.rs",
"chars": 709,
"preview": "use ringbahn::net::TcpStream;\n\nuse std::io::{self, BufRead, Write};\n\nuse futures::io::{AsyncBufReadExt, AsyncWriteExt};\n"
},
{
"path": "examples/echo-server.rs",
"chars": 984,
"preview": "use ringbahn::net::TcpListener;\n\nuse futures::StreamExt;\nuse futures::io::{AsyncReadExt, AsyncWriteExt};\nuse futures::ex"
},
{
"path": "examples/read-event.rs",
"chars": 741,
"preview": "use ringbahn::*;\nuse std::fs::{metadata, File};\nuse std::io;\nuse std::os::unix::io::AsRawFd;\n\nfn main() -> io::Result<()"
},
{
"path": "props.txt",
"chars": 792,
"preview": "But this formidable power of death - and this is perhaps what accounts for part of its force and\nthe cynicism with which"
},
{
"path": "src/buf.rs",
"chars": 1252,
"preview": "use std::cmp;\nuse std::io;\nuse std::task::Poll;\n\nuse futures_core::ready;\n\nuse crate::ring::Cancellation;\n\n#[derive(Defa"
},
{
"path": "src/drive/demo.rs",
"chars": 3802,
"preview": "//! A demo driver for experimentation purposes\n\nuse std::future::Future;\nuse std::io;\nuse std::pin::Pin;\nuse std::sync::"
},
{
"path": "src/drive/mod.rs",
"chars": 3374,
"preview": "//! Drive IO on io-uring\n\npub mod demo;\n\nuse std::io;\nuse std::marker::PhantomData;\nuse std::pin::Pin;\nuse std::task::{C"
},
{
"path": "src/event/accept.rs",
"chars": 725,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::sqe::{SockFlag, SockAddrStorage};\nuse iou::registrar"
},
{
"path": "src/event/close.rs",
"chars": 401,
"preview": "use std::os::unix::io::RawFd;\n\nuse iou::registrar::UringFd;\n\nuse super::{Event, SQE, SQEs};\n\npub struct Close<FD = RawFd"
},
{
"path": "src/event/connect.rs",
"chars": 648,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::sqe::SockAddr;\nuse iou::registrar::UringFd;\n\nuse sup"
},
{
"path": "src/event/epoll_ctl.rs",
"chars": 695,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::sqe::{EpollOp, EpollEvent};\n\nuse super::{Event, SQE,"
},
{
"path": "src/event/fadvise.rs",
"chars": 552,
"preview": "use std::os::unix::io::RawFd;\n\nuse iou::sqe::PosixFadviseAdvice;\nuse iou::registrar::UringFd;\n\nuse super::{Event, SQE, S"
},
{
"path": "src/event/fallocate.rs",
"chars": 550,
"preview": "use std::os::unix::io::RawFd;\n\nuse iou::registrar::UringFd;\nuse iou::sqe::FallocateFlags;\n\nuse super::{Event, SQE, SQEs}"
},
{
"path": "src/event/files_update.rs",
"chars": 581,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse super::{Event, SQE, SQEs, Cancellation};\n\npub struct File"
},
{
"path": "src/event/fsync.rs",
"chars": 466,
"preview": "use std::os::unix::io::RawFd;\n\nuse iou::registrar::UringFd;\nuse iou::sqe::FsyncFlags;\n\nuse super::{Event, SQE, SQEs};\n\np"
},
{
"path": "src/event/mod.rs",
"chars": 3108,
"preview": "//! Events that can be scheduled on io-uring with a [`Submission`](crate::Submission)\n\nmod accept;\nmod close;\nmod connec"
},
{
"path": "src/event/openat.rs",
"chars": 978,
"preview": "use std::ffi::CString;\nuse std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\nuse std::os::unix::ffi::OsStrExt;\nuse s"
},
{
"path": "src/event/provide_buffers.rs",
"chars": 1006,
"preview": "use std::mem::ManuallyDrop;\nuse iou::sqe::BufferGroupId;\n\nuse super::{Event, SQE, SQEs, Cancellation};\n\npub struct Provi"
},
{
"path": "src/event/read.rs",
"chars": 1211,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::registrar::{UringFd, RegisteredBuf};\n\nuse super::{Ev"
},
{
"path": "src/event/readv.rs",
"chars": 1639,
"preview": "use std::io::IoSliceMut; \nuse std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::registrar::UringFd;\n\nuse s"
},
{
"path": "src/event/recv.rs",
"chars": 672,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::sqe::MsgFlags;\nuse iou::registrar::UringFd;\n\nuse sup"
},
{
"path": "src/event/send.rs",
"chars": 668,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::sqe::MsgFlags;\nuse iou::registrar::UringFd;\n\nuse sup"
},
{
"path": "src/event/splice.rs",
"chars": 553,
"preview": "use std::os::unix::io::RawFd;\n\nuse iou::sqe::SpliceFlags;\n\nuse super::{Event, SQE, SQEs};\n\npub struct Splice {\n pub f"
},
{
"path": "src/event/statx.rs",
"chars": 1653,
"preview": "use std::ffi::CString;\nuse std::mem::{self, ManuallyDrop};\nuse std::os::unix::io::RawFd;\nuse std::os::unix::ffi::OsStrEx"
},
{
"path": "src/event/timeout.rs",
"chars": 1654,
"preview": "use std::mem::ManuallyDrop;\nuse std::time::Duration;\n\nuse super::{Event, SQE, SQEs, Cancellation};\n\nuse iou::sqe::Timeou"
},
{
"path": "src/event/write.rs",
"chars": 1214,
"preview": "use std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::registrar::{UringFd, RegisteredBuf};\n\nuse super::{Ev"
},
{
"path": "src/event/writev.rs",
"chars": 879,
"preview": "use std::io::IoSlice; \nuse std::mem::ManuallyDrop;\nuse std::os::unix::io::RawFd;\n\nuse iou::registrar::UringFd;\n\nuse supe"
},
{
"path": "src/fs.rs",
"chars": 11252,
"preview": "//! Interact with the file system using io-uring\n\nuse std::fs;\nuse std::future::Future;\nuse std::io;\nuse std::mem::{self"
},
{
"path": "src/io.rs",
"chars": 5033,
"preview": "use std::borrow::Cow;\nuse std::future::Future;\nuse std::io;\nuse std::os::unix::io::{AsRawFd, RawFd};\nuse std::pin::Pin;\n"
},
{
"path": "src/lib.rs",
"chars": 231,
"preview": "pub mod fs;\npub mod net;\npub mod unix;\n\npub mod drive;\npub mod event;\npub mod ring;\n\npub mod io;\n\nmod buf;\nmod submissio"
},
{
"path": "src/net/listener.rs",
"chars": 8535,
"preview": "use std::io;\nuse std::future::Future;\nuse std::net::{ToSocketAddrs, SocketAddr};\nuse std::os::unix::io::{RawFd};\nuse std"
},
{
"path": "src/net/mod.rs",
"chars": 987,
"preview": "mod listener;\nmod stream;\n\nuse std::io;\nuse std::net::{SocketAddr, ToSocketAddrs};\nuse std::os::unix::io::RawFd;\n\npub us"
},
{
"path": "src/net/stream.rs",
"chars": 6258,
"preview": "use std::io;\nuse std::future::Future;\nuse std::net::ToSocketAddrs;\nuse std::os::unix::io::RawFd;\nuse std::pin::Pin;\nuse "
},
{
"path": "src/ring/cancellation.rs",
"chars": 4034,
"preview": "use std::any::Any;\nuse std::ffi::CString;\nuse std::mem;\nuse std::ptr;\n\nuse either::Either;\n\nuse crate::buf::Buffer;\n\n///"
},
{
"path": "src/ring/completion.rs",
"chars": 4313,
"preview": "use std::io;\nuse std::mem::{self, ManuallyDrop};\nuse std::task::Waker;\n\nuse parking_lot::Mutex;\n\nuse crate::ring::Cancel"
},
{
"path": "src/ring/mod.rs",
"chars": 7078,
"preview": "mod cancellation;\npub(crate) mod completion;\n\nuse std::io;\nuse std::mem;\nuse std::pin::Pin;\nuse std::task::{Context, Pol"
},
{
"path": "src/submission.rs",
"chars": 2003,
"preview": "use std::future::Future;\nuse std::io;\nuse std::mem::ManuallyDrop;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nus"
},
{
"path": "src/unix/listener.rs",
"chars": 5006,
"preview": "use std::io;\nuse std::future::Future;\nuse std::os::unix::io::{RawFd};\nuse std::path::Path;\nuse std::pin::Pin;\nuse std::t"
},
{
"path": "src/unix/mod.rs",
"chars": 702,
"preview": "use std::io;\nuse std::os::unix::io::RawFd;\n\nmod listener;\nmod stream;\n\npub use listener::{UnixListener, Close, Accept, I"
},
{
"path": "src/unix/stream.rs",
"chars": 3789,
"preview": "use std::io;\nuse std::future::Future;\nuse std::os::unix::io::RawFd;\nuse std::path::Path;\nuse std::pin::Pin;\nuse std::tas"
},
{
"path": "tests/basic-read.rs",
"chars": 560,
"preview": "use std::fs::File;\nuse std::os::unix::io::AsRawFd;\n\nuse ringbahn::Submission;\nuse ringbahn::event::Read;\nuse ringbahn::d"
},
{
"path": "tests/basic-readv.rs",
"chars": 834,
"preview": "use std::fs::File;\nuse std::os::unix::io::AsRawFd;\n\nuse ringbahn::Submission;\nuse ringbahn::event::ReadVectored;\nuse rin"
},
{
"path": "tests/basic-write.rs",
"chars": 676,
"preview": "use std::io::Read;\nuse std::os::unix::io::AsRawFd;\n\nuse ringbahn::Submission;\nuse ringbahn::event::Write;\nuse ringbahn::"
},
{
"path": "tests/basic-writev.rs",
"chars": 819,
"preview": "use std::io::Read; \nuse std::os::unix::io::AsRawFd;\n\nuse ringbahn::Submission;\nuse ringbahn::event::WriteVectored;\nuse r"
},
{
"path": "tests/file-close.rs",
"chars": 464,
"preview": "use futures::{AsyncReadExt, AsyncWriteExt};\n\nuse ringbahn::fs::File;\n\nconst ASSERT: &[u8] = b\"But this formidable power "
},
{
"path": "tests/file-read.rs",
"chars": 690,
"preview": "use futures::AsyncReadExt;\n\nuse ringbahn::fs::File;\n\nconst ASSERT: &[u8] = b\"But this formidable power of death -\";\n\n#[t"
},
{
"path": "tests/file-seek.rs",
"chars": 776,
"preview": "use std::io::SeekFrom;\nuse futures::{AsyncSeekExt, AsyncReadExt, AsyncWriteExt};\n\nuse ringbahn::fs::File;\n\n#[test]\nfn se"
},
{
"path": "tests/file-write.rs",
"chars": 1648,
"preview": "use std::io::SeekFrom;\n\nuse futures::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};\n\nuse ringbahn::fs::File;\n\nconst ASSERT"
},
{
"path": "tests/println.rs",
"chars": 579,
"preview": "use ringbahn::drive::demo;\n\n#[test]\nfn println() {\n futures::executor::block_on(async {\n ringbahn::println!(de"
},
{
"path": "tests/registered-fd.rs",
"chars": 1227,
"preview": "use std::os::unix::io::IntoRawFd;\n\nuse ringbahn::event::*;\nuse ringbahn::drive::{demo, Drive};\n\nuse iou::sqe::*;\n\n#[test"
},
{
"path": "tests/stdio.rs",
"chars": 449,
"preview": "use futures::AsyncWriteExt;\n\nuse ringbahn::{drive::demo};\n\nconst ASSERT: &[u8] = b\"Hello, world!\\n\";\n\n#[test]\nfn write_s"
}
]
About this extraction
This page contains the full source code of the withoutboats/ringbahn GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 55 files (99.4 KB), approximately 28.5k tokens, and a symbol index with 367 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.