252 lines
5.4 KiB
Rust
252 lines
5.4 KiB
Rust
use std::{
|
|
async_iter::AsyncIterator,
|
|
ffi::CString,
|
|
future::Future,
|
|
net::{IpAddr, SocketAddr},
|
|
os::fd::IntoRawFd,
|
|
path::PathBuf,
|
|
pin::Pin,
|
|
};
|
|
|
|
use futures_lite::Stream;
|
|
use io_uring::IoUring;
|
|
|
|
use crate::{aliases::IoResult, net::TcpStream};
|
|
|
|
pub struct PlatformLinux {
|
|
uring: io_uring::IoUring,
|
|
|
|
_pd: core::marker::PhantomData<std::cell::Cell<()>>,
|
|
}
|
|
|
|
pub type FileHandle = io_uring::types::Fd;
|
|
impl PlatformLinux {
|
|
pub fn new() -> IoResult<Self> {
|
|
Ok(Self {
|
|
uring: IoUring::new(256)?,
|
|
|
|
_pd: Default::default(),
|
|
})
|
|
}
|
|
|
|
pub async fn tick(&self) -> IoResult<()> {
|
|
self.uring.submit()?;
|
|
|
|
let cq = unsafe { self.uring.completion_shared() };
|
|
for entry in cq {
|
|
let ud = unsafe { UserData::from_u64(entry.user_data()) };
|
|
ud.tx.send(entry).await.unwrap();
|
|
if ud.persist {
|
|
Box::leak(ud);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub async fn open_file(p: &PlatformLinux, path: PathBuf) -> IoResult<FileHandle> {
|
|
let (tx, rx) = async_channel::bounded(1);
|
|
|
|
let path = CString::new(path.into_os_string().into_encoded_bytes())?;
|
|
let entry =
|
|
io_uring::opcode::OpenAt::new(io_uring::types::Fd(libc::AT_FDCWD), path.as_ptr())
|
|
.build()
|
|
.user_data(UserData::new_boxed(tx, false).into_u64());
|
|
|
|
unsafe {
|
|
p.uring.submission_shared().push(&entry).unwrap();
|
|
}
|
|
|
|
let entry = rx.recv().await.unwrap();
|
|
let fd = handle_error(entry.result())?;
|
|
let fd = io_uring::types::Fd(fd);
|
|
|
|
Ok(fd)
|
|
}
|
|
|
|
pub async fn open_tcp_socket(
|
|
p: &PlatformLinux,
|
|
socket_addr: SocketAddr,
|
|
) -> IoResult<FileHandle> {
|
|
// FIXME(Blocking)
|
|
// There is some some magic missing in the commented out code to make the
|
|
// socket clean up properly on process exit and whatnot. For now, just use
|
|
// the std implementation and cast it to a FileHandle
|
|
|
|
let listener = std::net::TcpListener::bind(socket_addr)?;
|
|
Ok(io_uring::types::Fd(listener.into_raw_fd()))
|
|
|
|
/*
|
|
// let (tx, rx) = async_channel::bounded(1);
|
|
|
|
let domain = match () {
|
|
_ if socket_addr.is_ipv4() => libc::AF_INET,
|
|
_ if socket_addr.is_ipv6() => libc::AF_INET6,
|
|
_ => return Err(std::io::Error::other("Unsupported domain")),
|
|
};
|
|
|
|
let entry = io_uring::opcode::Socket::new(domain, libc::SOCK_STREAM, 0)
|
|
.build()
|
|
.user_data(UserData::new_boxed(tx, false).into_u64());
|
|
|
|
unsafe {
|
|
p.uring.submission_shared().push(&entry).unwrap();
|
|
}
|
|
|
|
let entry = rx.recv().await.unwrap();
|
|
|
|
let fd = handle_error(entry.result())?;
|
|
|
|
let sock = libc::sockaddr_in {
|
|
sin_family: domain as _,
|
|
sin_port: socket_addr.port().to_be(),
|
|
sin_addr: libc::in_addr {
|
|
s_addr: match socket_addr.ip() {
|
|
IpAddr::V4(v) => v.to_bits().to_be(),
|
|
IpAddr::V6(_) => panic!(),
|
|
},
|
|
},
|
|
sin_zero: Default::default(),
|
|
};
|
|
|
|
// FIXME(Blocking)
|
|
handle_error(unsafe {
|
|
libc::bind(
|
|
fd,
|
|
&sock as *const _ as *const _,
|
|
core::mem::size_of_val(&sock) as u32,
|
|
)
|
|
})?;
|
|
|
|
// FIXME(Blocking)
|
|
handle_error(unsafe { libc::listen(fd, libc::SOMAXCONN) })?;
|
|
|
|
Ok(io_uring::types::Fd(fd))
|
|
*/
|
|
}
|
|
|
|
pub async fn read(
|
|
p: &PlatformLinux,
|
|
f: &mut FileHandle,
|
|
offset: usize,
|
|
mut buf: Box<[u8]>,
|
|
) -> IoResult<Box<[u8]>> {
|
|
let (tx, rx) = async_channel::bounded(1);
|
|
|
|
let entry = io_uring::opcode::Read::new(*f, buf.as_mut_ptr(), buf.len() as _)
|
|
.offset(offset as _)
|
|
.build()
|
|
.user_data(UserData::new_boxed(tx, false).into_u64());
|
|
|
|
unsafe {
|
|
p.uring.submission_shared().push(&entry).unwrap();
|
|
}
|
|
|
|
let entry = rx.recv().await.unwrap();
|
|
let read_amt = handle_error(entry.result())?;
|
|
|
|
let mut buf = buf.into_vec();
|
|
buf.truncate(read_amt as _);
|
|
|
|
Ok(buf.into_boxed_slice())
|
|
}
|
|
|
|
pub async fn write(
|
|
p: &PlatformLinux,
|
|
f: &mut FileHandle,
|
|
offset: usize,
|
|
mut buf: Box<[u8]>,
|
|
) -> IoResult<usize> {
|
|
let (tx, rx) = async_channel::bounded(1);
|
|
|
|
let entry = io_uring::opcode::Write::new(*f, buf.as_mut_ptr(), buf.len() as _)
|
|
.offset(offset as _)
|
|
.build()
|
|
.user_data(UserData::new_boxed(tx, false).into_u64());
|
|
|
|
unsafe {
|
|
p.uring.submission_shared().push(&entry).unwrap();
|
|
}
|
|
|
|
let entry = rx.recv().await.unwrap();
|
|
let write_amt = handle_error(entry.result())?;
|
|
|
|
Ok(write_amt as usize)
|
|
}
|
|
|
|
pub(crate) struct Incoming<'a> {
|
|
plat: &'a PlatformLinux,
|
|
rx: Pin<Box<CqueueEntryReceiver>>,
|
|
cb_id: u64,
|
|
}
|
|
|
|
pub fn accept_many<'a>(p: &'a PlatformLinux, f: &mut FileHandle) -> Incoming<'a> {
|
|
let (tx, rx) = async_channel::unbounded();
|
|
|
|
let cb_id = UserData::new_boxed(tx, true).into_u64();
|
|
|
|
let entry = io_uring::opcode::AcceptMulti::new(*f)
|
|
.build()
|
|
.user_data(cb_id);
|
|
|
|
unsafe {
|
|
p.uring.submission_shared().push(&entry).unwrap();
|
|
}
|
|
|
|
Incoming {
|
|
plat: p,
|
|
rx: Box::pin(rx),
|
|
cb_id,
|
|
}
|
|
}
|
|
|
|
pub fn cancel(p: &PlatformLinux, id: u64) {
|
|
let entry = io_uring::opcode::AsyncCancel::new(id).build();
|
|
|
|
unsafe {
|
|
p.uring.submission_shared().push(&entry).unwrap();
|
|
}
|
|
}
|
|
|
|
impl AsyncIterator for Incoming<'_> {
|
|
type Item = IoResult<TcpStream>;
|
|
|
|
fn poll_next(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut std::task::Context<'_>,
|
|
) -> std::task::Poll<Option<Self::Item>> {
|
|
let rx = unsafe { self.map_unchecked_mut(|s| &mut s.rx) };
|
|
let mut fut = rx.recv();
|
|
let pinned = unsafe { Pin::new_unchecked(&mut fut) };
|
|
|
|
pinned
|
|
.poll(cx)
|
|
.map(|entry| {
|
|
let fd = handle_error(entry.unwrap().result())?;
|
|
|
|
Ok(crate::net::TcpStream(io_uring::types::Fd(fd)))
|
|
})
|
|
.map(Some)
|
|
}
|
|
}
|
|
|
|
impl Stream for Incoming<'_> {
|
|
type Item = IoResult<TcpStream>;
|
|
|
|
fn poll_next(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut std::task::Context<'_>,
|
|
) -> std::task::Poll<Option<Self::Item>> {
|
|
AsyncIterator::poll_next(self, cx)
|
|
}
|
|
}
|
|
|
|
impl Drop for Incoming<'_> {
|
|
fn drop(&mut self) {
|
|
cancel(self.plat, self.cb_id);
|
|
}
|
|
}
|
|
|
|
pub type Platform = PlatformLinux;
|