pub trait Extract {
// Provided method
fn extract(self) -> Extractor<Self>
where Self: Sized { ... }
}
Expand description
Extract input arguments from operations.
Because of the way that io_uring works the kernel needs mutable access to the inputs of a system call for entire duration the operation is in progress. For example when reading into a buffer the buffer needs to stay alive until the kernel has written into it, or until the kernel knows the operation is canceled and won’t write into the buffer any more. If we can’t ensure this the kernel might write into memory we don’t own causing write-after-free bugs.
To ensure the input stays alive A10 needs ownership of the input arguments
and delays the deallocation of the inputs when a Future
operation is
dropped before completion. However to give the Future
s a nice API we don’t
return the input arguments and try to match the APIs that don’t take
ownership of arguments, e.g fs::OpenOptions::open
just returns a
AsyncFd
not the path argument.
In some cases though we would like to get back the input arguments from the
operation, e.g. for performance reasons. This trait allow you to do just
that: extract the input arguments from Future
operations.
All I/O operations, that is the Future
implementations, that support
extract the input argument will implement this Extract
trait. A list of
the supported operations can be found below. For the actual
implementations see the Future
implementations on the Extractor
type.
§Examples
Extracting the arguments from fs::OpenOptions::open
and
AsyncFd::write
operations.
use std::io;
use std::path::PathBuf;
use a10::fs::OpenOptions;
use a10::{SubmissionQueue, Extract};
async fn write_all_to_file(sq: SubmissionQueue, path: PathBuf, buf: Vec<u8>) -> io::Result<(PathBuf, Vec<u8>)> {
// This `Future` returns just the opened file.
let open_file_future = OpenOptions::new().open(sq, path);
// Calling `extract` and awaiting that will return both the file and
// the path buffer.
let (file, path) = open_file_future.extract().await?;
// This just returns the number of bytes written.
let write_future = file.write(buf);
// When extracting it also returns the buffer.
let (buf, n) = write_future.extract().await?;
if n != buf.len() {
// NOTE: `WriteZero` is not entirely accurate, but just for the sake
// of the example.
return Err(io::Error::new(io::ErrorKind::WriteZero, "didn't write entire buffer"));
}
Ok((path, buf))
}