1
0
Fork 0
forked from wry/wry

io-uring: add sendmsg

This commit is contained in:
Julian Orth 2022-05-13 17:37:20 +02:00
parent 9416efeabe
commit e4f97287bc
17 changed files with 493 additions and 191 deletions

View file

@ -3,6 +3,7 @@ use {
async_engine::Phase, async_engine::Phase,
client::{Client, ClientError}, client::{Client, ClientError},
object::ObjectId, object::ObjectId,
time::Time,
utils::{ utils::{
buffd::{BufFdIn, BufFdOut, MsgParser}, buffd::{BufFdIn, BufFdOut, MsgParser},
errorfmt::ErrorFmt, errorfmt::ErrorFmt,
@ -100,7 +101,7 @@ async fn receive(data: Rc<Client>) {
async fn send(data: Rc<Client>) { async fn send(data: Rc<Client>) {
let send = async { let send = async {
let mut out = BufFdOut::new(&data.socket, &data.state.ring, &data.state.wheel); let mut out = BufFdOut::new(&data.socket, &data.state.ring);
let mut buffers = VecDeque::new(); let mut buffers = VecDeque::new();
loop { loop {
data.flush_request.triggered().await; data.flush_request.triggered().await;
@ -109,9 +110,9 @@ async fn send(data: Rc<Client>) {
swapchain.commit(); swapchain.commit();
mem::swap(&mut swapchain.pending, &mut buffers); mem::swap(&mut swapchain.pending, &mut buffers);
} }
let mut timeout = None; let timeout = Time::in_ms(5000).unwrap();
while let Some(mut cur) = buffers.pop_front() { while let Some(mut cur) = buffers.pop_front() {
out.flush(&mut cur, &mut timeout).await?; out.flush(&mut cur, timeout).await?;
data.swapchain.borrow_mut().free.push(cur); data.swapchain.borrow_mut().free.push(cur);
} }
} }
@ -122,9 +123,9 @@ async fn send(data: Rc<Client>) {
log::info!("Client {} terminated the connection", data.id.0); log::info!("Client {} terminated the connection", data.id.0);
} else { } else {
log::error!( log::error!(
"An error occurred while sending data to client {}: {:#}", "An error occurred while sending data to client {}: {}",
data.id.0, data.id.0,
e ErrorFmt(e)
); );
} }
} }

View file

@ -15,7 +15,6 @@ use {
buffd::BufFdError, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, buffd::BufFdError, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
queue::AsyncQueue, queue::AsyncQueue,
}, },
wheel::Wheel,
xwayland, xwayland,
}, },
bincode::{ bincode::{
@ -237,7 +236,7 @@ impl ForkerProxy {
} }
async fn outgoing(self: Rc<Self>, state: Rc<State>) { async fn outgoing(self: Rc<Self>, state: Rc<State>) {
let mut io = IoOut::new(&self.socket, &state.ring, &state.wheel); let mut io = IoOut::new(&self.socket, &state.ring);
loop { loop {
let msg = self.outgoing.pop().await; let msg = self.outgoing.pop().await;
for fd in self.fds.borrow_mut().drain(..) { for fd in self.fds.borrow_mut().drain(..) {
@ -301,7 +300,6 @@ struct Forker {
socket: Rc<OwnedFd>, socket: Rc<OwnedFd>,
ae: Rc<AsyncEngine>, ae: Rc<AsyncEngine>,
ring: Rc<IoUring>, ring: Rc<IoUring>,
wheel: Rc<Wheel>,
fds: RefCell<Vec<Rc<OwnedFd>>>, fds: RefCell<Vec<Rc<OwnedFd>>>,
outgoing: AsyncQueue<ForkerMessage>, outgoing: AsyncQueue<ForkerMessage>,
pending_spawns: CopyHashMap<c::pid_t, SpawnedFuture<()>>, pending_spawns: CopyHashMap<c::pid_t, SpawnedFuture<()>>,
@ -329,12 +327,10 @@ impl Forker {
}); });
let ae = AsyncEngine::new(); let ae = AsyncEngine::new();
let ring = IoUring::new(&ae, 32).unwrap(); let ring = IoUring::new(&ae, 32).unwrap();
let wheel = Wheel::new(&ae, &ring).unwrap();
let forker = Rc::new(Forker { let forker = Rc::new(Forker {
socket, socket,
ae: ae.clone(), ae: ae.clone(),
ring: ring.clone(), ring: ring.clone(),
wheel,
fds: RefCell::new(vec![]), fds: RefCell::new(vec![]),
outgoing: Default::default(), outgoing: Default::default(),
pending_spawns: Default::default(), pending_spawns: Default::default(),
@ -346,7 +342,7 @@ impl Forker {
} }
async fn outgoing(self: Rc<Self>) { async fn outgoing(self: Rc<Self>) {
let mut io = IoOut::new(&self.socket, &self.ring, &self.wheel); let mut io = IoOut::new(&self.socket, &self.ring);
loop { loop {
let msg = self.outgoing.pop().await; let msg = self.outgoing.pop().await;
for fd in self.fds.borrow_mut().drain(..) { for fd in self.fds.borrow_mut().drain(..) {

View file

@ -11,7 +11,6 @@ use {
buffd::{BufFdIn, BufFdOut}, buffd::{BufFdIn, BufFdOut},
vec_ext::VecExt, vec_ext::VecExt,
}, },
wheel::Wheel,
}, },
jay_config::_private::bincode_ops, jay_config::_private::bincode_ops,
uapi::OwnedFd, uapi::OwnedFd,
@ -63,9 +62,9 @@ pub struct IoOut {
} }
impl IoOut { impl IoOut {
pub fn new(fd: &Rc<OwnedFd>, ring: &Rc<IoUring>, wheel: &Rc<Wheel>) -> Self { pub fn new(fd: &Rc<OwnedFd>, ring: &Rc<IoUring>) -> Self {
Self { Self {
outgoing: BufFdOut::new(fd, ring, wheel), outgoing: BufFdOut::new(fd, ring),
scratch: vec![], scratch: vec![],
fds: vec![], fds: vec![],
} }

View file

@ -1,8 +1,12 @@
pub use ops::TaskResultExt;
use { use {
crate::{ crate::{
async_engine::AsyncEngine, async_engine::AsyncEngine,
io_uring::{ io_uring::{
ops::{async_cancel::AsyncCancelTask, poll::PollTask, write::WriteTask}, ops::{
async_cancel::AsyncCancelTask, poll::PollTask, sendmsg::SendmsgTask,
timeout::TimeoutTask, write::WriteTask,
},
pending_result::PendingResults, pending_result::PendingResults,
sys::{ sys::{
io_uring_cqe, io_uring_enter, io_uring_params, io_uring_setup, io_uring_sqe, io_uring_cqe, io_uring_enter, io_uring_params, io_uring_setup, io_uring_sqe,
@ -24,7 +28,7 @@ use {
}, },
}, },
std::{ std::{
cell::{Cell, UnsafeCell}, cell::{Cell, RefCell, UnsafeCell},
mem::{self}, mem::{self},
rc::Rc, rc::Rc,
sync::atomic::{ sync::atomic::{
@ -49,7 +53,6 @@ macro_rules! map_err {
} }
}}; }};
} }
pub use ops::TaskResultExt;
mod ops; mod ops;
mod pending_result; mod pending_result;
@ -58,7 +61,7 @@ mod sys;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum IoUringError { pub enum IoUringError {
#[error(transparent)] #[error(transparent)]
OsError(OsError), OsError(#[from] OsError),
#[error("Could not create an io-uring")] #[error("Could not create an io-uring")]
CreateUring(#[source] OsError), CreateUring(#[source] OsError),
#[error("The kernel does not support the IORING_FEAT_NODROP feature")] #[error("The kernel does not support the IORING_FEAT_NODROP feature")]
@ -201,6 +204,9 @@ impl IoUring {
cached_writes: Default::default(), cached_writes: Default::default(),
cached_cancels: Default::default(), cached_cancels: Default::default(),
cached_polls: Default::default(), cached_polls: Default::default(),
cached_sendmsg: Default::default(),
cached_timeouts: Default::default(),
fd_ids_scratch: Default::default(),
}); });
Ok(Rc::new(Self { ring: data })) Ok(Rc::new(Self { ring: data }))
} }
@ -248,6 +254,10 @@ struct IoUringData {
cached_writes: Stack<Box<WriteTask>>, cached_writes: Stack<Box<WriteTask>>,
cached_cancels: Stack<Box<AsyncCancelTask>>, cached_cancels: Stack<Box<AsyncCancelTask>>,
cached_polls: Stack<Box<PollTask>>, cached_polls: Stack<Box<PollTask>>,
cached_sendmsg: Stack<Box<SendmsgTask>>,
cached_timeouts: Stack<Box<TimeoutTask>>,
fd_ids_scratch: RefCell<Vec<c::c_int>>,
} }
unsafe trait Task { unsafe trait Task {

View file

@ -2,6 +2,8 @@ use crate::{io_uring::IoUringError, utils::oserror::OsError};
pub mod async_cancel; pub mod async_cancel;
pub mod poll; pub mod poll;
pub mod sendmsg;
pub mod timeout;
pub mod write; pub mod write;
pub type TaskResult<T> = Result<Result<T, OsError>, IoUringError>; pub type TaskResult<T> = Result<Result<T, OsError>, IoUringError>;

130
src/io_uring/ops/sendmsg.rs Normal file
View file

@ -0,0 +1,130 @@
use {
crate::{
io_uring::{
pending_result::PendingResult,
sys::{io_uring_sqe, IORING_OP_SENDMSG, IOSQE_IO_LINK},
IoUring, IoUringData, IoUringError, Task,
},
time::Time,
utils::{buf::Buf, vec_ext::UninitVecExt},
},
std::{
mem::{self, MaybeUninit},
ptr,
rc::Rc,
},
uapi::{c, OwnedFd},
};
impl IoUring {
pub async fn sendmsg(
&self,
fd: &Rc<OwnedFd>,
buf: Buf,
fds: Vec<Rc<OwnedFd>>,
timeout: Option<Time>,
) -> Result<usize, IoUringError> {
self.ring.check_destroyed()?;
let id = self.ring.id();
let pr = self.ring.pending_results.acquire();
{
let mut st = self.ring.cached_sendmsg.pop().unwrap_or_default();
st.fds = fds;
if st.fds.len() > 0 {
let mut fd_ids = self.ring.fd_ids_scratch.borrow_mut();
fd_ids.clear();
fd_ids.extend(st.fds.iter().map(|f| f.raw()));
let space = uapi::cmsg_space(mem::size_of_val(&fd_ids[..]));
st.cmsg.clear();
st.cmsg.reserve(space);
st.cmsg.set_len_safe(space);
let hdr = c::cmsghdr {
cmsg_len: 0,
cmsg_level: c::SOL_SOCKET,
cmsg_type: c::SCM_RIGHTS,
};
uapi::cmsg_write(&mut &mut st.cmsg[..], hdr, &fd_ids[..]).unwrap();
st.msghdr.msg_control = st.cmsg.as_ptr() as _;
st.msghdr.msg_controllen = st.cmsg.len() as _;
} else {
st.msghdr.msg_control = ptr::null_mut();
st.msghdr.msg_controllen = 0;
}
st.id = id.id;
st.fd = fd.raw();
st.iovec.iov_base = buf.as_ptr() as _;
st.iovec.iov_len = buf.len() as _;
st.msghdr.msg_iov = &st.iovec as *const _ as _;
st.msghdr.msg_iovlen = 1;
st.data = Some(SendmsgTaskData {
_fd: fd.clone(),
_buf: buf,
res: pr.clone(),
});
st.has_timeout = timeout.is_some();
self.ring.schedule(st);
if let Some(timeout) = timeout {
self.schedule_timeout(timeout);
}
}
Ok(pr.await? as _)
}
}
struct SendmsgTaskData {
_fd: Rc<OwnedFd>,
_buf: Buf,
res: PendingResult,
}
pub struct SendmsgTask {
id: u64,
iovec: c::iovec,
msghdr: c::msghdr,
fd: i32,
has_timeout: bool,
fds: Vec<Rc<OwnedFd>>,
cmsg: Vec<MaybeUninit<u8>>,
data: Option<SendmsgTaskData>,
}
impl Default for SendmsgTask {
fn default() -> Self {
unsafe {
SendmsgTask {
id: 0,
iovec: MaybeUninit::zeroed().assume_init(),
msghdr: MaybeUninit::zeroed().assume_init(),
fd: 0,
has_timeout: false,
fds: vec![],
cmsg: vec![],
data: None,
}
}
}
}
unsafe impl Task for SendmsgTask {
fn id(&self) -> u64 {
self.id
}
fn complete(mut self: Box<Self>, ring: &IoUringData, res: i32) {
self.fds.clear();
if let Some(data) = self.data.take() {
data.res.complete(res);
}
ring.cached_sendmsg.push(self);
}
fn encode(&self, sqe: &mut io_uring_sqe) {
sqe.opcode = IORING_OP_SENDMSG;
sqe.fd = self.fd;
sqe.u2.addr = &self.msghdr as *const _ as _;
sqe.u3.msg_flags = c::MSG_NOSIGNAL as _;
if self.has_timeout {
sqe.flags = IOSQE_IO_LINK;
}
}
}

View file

@ -0,0 +1,54 @@
use {
crate::{
io_uring::{
sys::{io_uring_sqe, IORING_OP_LINK_TIMEOUT, IORING_TIMEOUT_ABS},
IoUring, IoUringData, Task,
},
time::Time,
},
uapi::c,
};
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Default)]
struct timespec64 {
tv_sec: i64,
tv_nsec: c::c_long,
}
#[derive(Default)]
pub struct TimeoutTask {
id: u64,
timespec: timespec64,
}
impl IoUring {
pub(super) fn schedule_timeout(&self, timeout: Time) {
let id = self.ring.id_raw();
{
let mut to = self.ring.cached_timeouts.pop().unwrap_or_default();
to.id = id;
to.timespec.tv_sec = timeout.0.tv_sec as _;
to.timespec.tv_nsec = timeout.0.tv_nsec as _;
self.ring.schedule(to);
}
}
}
unsafe impl Task for TimeoutTask {
fn id(&self) -> u64 {
self.id
}
fn complete(self: Box<Self>, ring: &IoUringData, _res: i32) {
ring.cached_timeouts.push(self);
}
fn encode(&self, sqe: &mut io_uring_sqe) {
sqe.opcode = IORING_OP_LINK_TIMEOUT;
sqe.u2.addr = &self.timespec as *const _ as _;
sqe.len = 1;
sqe.u3.timeout_flags = IORING_TIMEOUT_ABS;
}
}

View file

@ -1,26 +1,22 @@
use { use {
crate::io_uring::{ crate::{
ops::TaskResult, io_uring::{
pending_result::PendingResult, ops::TaskResult,
sys::{io_uring_sqe, IORING_OP_WRITE}, pending_result::PendingResult,
IoUring, IoUringData, Task, sys::{io_uring_sqe, IORING_OP_WRITE},
IoUring, IoUringData, Task,
},
utils::buf::Buf,
}, },
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
ops::Range,
rc::Rc, rc::Rc,
}, },
uapi::OwnedFd, uapi::OwnedFd,
}; };
impl IoUring { impl IoUring {
pub async fn write( pub async fn write(&self, fd: &Rc<OwnedFd>, buf: Buf) -> TaskResult<usize> {
&self,
fd: &Rc<OwnedFd>,
buf: &Rc<Vec<u8>>,
offset: usize,
n: usize,
) -> TaskResult<usize> {
self.ring.check_destroyed()?; self.ring.check_destroyed()?;
let id = self.ring.id(); let id = self.ring.id();
let pr = self.ring.pending_results.acquire(); let pr = self.ring.pending_results.acquire();
@ -34,8 +30,7 @@ impl IoUring {
pw.id.set(id.id); pw.id.set(id.id);
*pw.data.borrow_mut() = Some(WriteTaskData { *pw.data.borrow_mut() = Some(WriteTaskData {
fd: fd.clone(), fd: fd.clone(),
buf: buf.clone(), buf,
range: offset..offset + n,
res: pr.clone(), res: pr.clone(),
}); });
self.ring.schedule(pw); self.ring.schedule(pw);
@ -46,8 +41,7 @@ impl IoUring {
struct WriteTaskData { struct WriteTaskData {
fd: Rc<OwnedFd>, fd: Rc<OwnedFd>,
buf: Rc<Vec<u8>>, buf: Buf,
range: Range<usize>,
res: PendingResult, res: PendingResult,
} }
@ -74,8 +68,8 @@ unsafe impl Task for WriteTask {
sqe.opcode = IORING_OP_WRITE; sqe.opcode = IORING_OP_WRITE;
sqe.fd = data.fd.raw(); sqe.fd = data.fd.raw();
sqe.u1.off = !0; sqe.u1.off = !0;
sqe.u2.addr = data.buf[data.range.clone()].as_ptr() as _; sqe.u2.addr = data.buf.as_ptr() as _;
sqe.u3.rw_flags = 0; sqe.u3.rw_flags = 0;
sqe.len = data.range.len() as _; sqe.len = data.buf.len() as _;
} }
} }

View file

@ -142,7 +142,7 @@ impl TestTransport {
self.run.state.eng.spawn( self.run.state.eng.spawn(
Outgoing { Outgoing {
tc: self.clone(), tc: self.clone(),
buf: BufFdOut::new(&self.socket, &self.run.state.ring, &self.run.state.wheel), buf: BufFdOut::new(&self.socket, &self.run.state.ring),
buffers: Default::default(), buffers: Default::default(),
} }
.run(), .run(),

View file

@ -36,6 +36,11 @@ impl Time {
Ok(Self(time)) Ok(Self(time))
} }
pub fn in_ms(ms: u64) -> Result<Time, TimeError> {
let now = Self::now()?;
Ok(now + Duration::from_millis(ms))
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn now_unchecked() -> Time { pub fn now_unchecked() -> Time {
let mut time = uapi::pod_zeroed(); let mut time = uapi::pod_zeroed();

View file

@ -195,7 +195,7 @@ impl ToolClient {
slf.eng.spawn( slf.eng.spawn(
Outgoing { Outgoing {
tc: slf.clone(), tc: slf.clone(),
buf: BufFdOut::new(&socket, &slf.ring, &slf.wheel), buf: BufFdOut::new(&socket, &slf.ring),
buffers: Default::default(), buffers: Default::default(),
} }
.run(), .run(),

View file

@ -2,6 +2,7 @@ pub mod array;
pub mod asyncevent; pub mod asyncevent;
pub mod bitfield; pub mod bitfield;
pub mod bitflags; pub mod bitflags;
pub mod buf;
pub mod buffd; pub mod buffd;
pub mod bufio; pub mod bufio;
pub mod clonecell; pub mod clonecell;

155
src/utils/buf.rs Normal file
View file

@ -0,0 +1,155 @@
use {
crate::utils::{numcell::NumCell, ptr_ext::PtrExt},
std::{
alloc::Layout,
collections::Bound,
ops::{Deref, DerefMut, Range, RangeBounds},
ptr::NonNull,
slice,
},
};
const METADATA_SIZE: u32 = 8;
const METADATA_ALIGN: usize = 4;
const RC_OFF: u32 = 4;
const RC_OFF_INV: u32 = METADATA_SIZE - RC_OFF;
pub struct Buf {
storage: NonNull<u8>,
range: Range<u32>,
}
impl Buf {
pub fn from_slice(vec: &[u8]) -> Buf {
let len = vec.len();
assert!(len <= (u32::MAX - METADATA_SIZE) as usize);
let len = len as u32;
let size = len + METADATA_SIZE;
let layout = Layout::from_size_align(size as _, METADATA_ALIGN).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
unsafe {
*ptr.cast::<u32>() = size;
*ptr.add(RC_OFF as _).cast::<u32>() = 1;
let mut buf = Buf {
storage: NonNull::new_unchecked(ptr.add(METADATA_SIZE as _)),
range: Range { start: 0, end: len },
};
buf[..].copy_from_slice(vec);
buf
}
}
pub fn new(len: usize) -> Buf {
assert!(len <= (u32::MAX - METADATA_SIZE) as usize);
let len = len as u32;
let size = len + METADATA_SIZE;
let layout = Layout::from_size_align(size as _, METADATA_ALIGN).unwrap();
let ptr = unsafe { std::alloc::alloc_zeroed(layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
unsafe {
*ptr.cast::<u32>() = size;
*ptr.add(RC_OFF as _).cast::<u32>() = 1;
Buf {
storage: NonNull::new_unchecked(ptr.add(METADATA_SIZE as _)),
range: Range { start: 0, end: len },
}
}
}
pub fn clone(&mut self) -> Buf {
self.rc().fetch_add(1);
Buf {
storage: self.storage,
range: self.range.clone(),
}
}
pub fn slice(&mut self, range: impl RangeBounds<usize>) -> Buf {
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.wrapping_add(1),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.wrapping_add(1),
Bound::Excluded(&n) => n,
Bound::Unbounded => self.len(),
};
self.slice_(start as _, end as _)
}
fn slice_(&mut self, start: u32, end: u32) -> Buf {
assert!(start <= end);
assert!(end <= self.len32());
self.rc().fetch_add(1);
Buf {
storage: self.storage,
range: Range {
start: self.range.start + start,
end: self.range.start + end,
},
}
}
fn rc(&self) -> &NumCell<u32> {
unsafe {
self.storage
.as_ptr()
.sub(RC_OFF_INV as _)
.cast::<NumCell<u32>>()
.deref()
}
}
fn assert_unique(&self) {
assert_eq!(self.rc().get(), 1);
}
pub fn len32(&self) -> u32 {
self.range.end - self.range.start
}
pub fn len(&self) -> usize {
self.len32() as _
}
pub fn as_ptr(&self) -> *mut u8 {
unsafe { self.storage.as_ptr().add(self.range.start as _) }
}
}
impl Deref for Buf {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.assert_unique();
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
}
impl DerefMut for Buf {
fn deref_mut(&mut self) -> &mut Self::Target {
self.assert_unique();
unsafe { slice::from_raw_parts_mut(self.as_ptr(), self.len()) }
}
}
impl Drop for Buf {
fn drop(&mut self) {
unsafe {
let prev = self.rc().fetch_sub(1);
if prev != 1 {
return;
}
let ptr = self.storage.as_ptr().sub(METADATA_SIZE as _).cast::<u32>();
let size = *ptr as _;
let layout = Layout::from_size_align_unchecked(size, METADATA_ALIGN);
std::alloc::dealloc(ptr as _, layout);
}
}
}

View file

@ -1,10 +1,13 @@
use { use {
crate::{ crate::{
io_uring::IoUring, io_uring::{IoUring, IoUringError},
utils::buffd::{BufFdError, BUF_SIZE, CMSG_BUF_SIZE}, time::Time,
wheel::{Wheel, WheelTimeoutFuture}, utils::{
buf::Buf,
buffd::{BufFdError, BUF_SIZE, CMSG_BUF_SIZE},
oserror::OsError,
},
}, },
futures_util::{future::Fuse, select, FutureExt},
std::{ std::{
collections::VecDeque, collections::VecDeque,
mem::{self, MaybeUninit}, mem::{self, MaybeUninit},
@ -21,37 +24,33 @@ pub(super) struct MsgFds {
pub(super) fds: Vec<Rc<OwnedFd>>, pub(super) fds: Vec<Rc<OwnedFd>>,
} }
pub struct OutBuffer { pub(super) struct OutBufferMeta {
pub(super) read_pos: usize, pub(super) read_pos: usize,
pub(super) write_pos: usize, pub(super) write_pos: usize,
pub(super) buf: *mut [MaybeUninit<u8>; OUT_BUF_SIZE],
pub(super) fds: VecDeque<MsgFds>, pub(super) fds: VecDeque<MsgFds>,
} }
pub struct OutBuffer {
pub(super) meta: OutBufferMeta,
pub(super) buf: Buf,
}
impl Default for OutBuffer { impl Default for OutBuffer {
fn default() -> Self { fn default() -> Self {
Self { Self {
read_pos: 0, meta: OutBufferMeta {
write_pos: 0, read_pos: 0,
buf: Box::into_raw(Box::new([MaybeUninit::<u32>::uninit(); OUT_BUF_SIZE / 4])) as _, write_pos: 0,
fds: Default::default(), fds: Default::default(),
},
buf: Buf::new(OUT_BUF_SIZE),
} }
} }
} }
impl OutBuffer { impl OutBuffer {
pub fn write(&mut self, bytes: &[MaybeUninit<u8>]) {
if bytes.len() > OUT_BUF_SIZE - self.write_pos {
panic!("Out buffer overflow");
}
unsafe {
(*self.buf)[self.write_pos..self.write_pos + bytes.len()].copy_from_slice(bytes);
}
self.write_pos += bytes.len();
}
pub fn is_full(&self) -> bool { pub fn is_full(&self) -> bool {
self.write_pos > BUF_SIZE self.meta.write_pos > BUF_SIZE
} }
} }
@ -70,7 +69,7 @@ impl OutBufferSwapchain {
} }
pub fn commit(&mut self) { pub fn commit(&mut self) {
if self.cur.write_pos > 0 { if self.cur.meta.write_pos > 0 {
let new = self.free.pop().unwrap_or_default(); let new = self.free.pop().unwrap_or_default();
let old = mem::replace(&mut self.cur, new); let old = mem::replace(&mut self.cur, new);
self.pending.push_back(old); self.pending.push_back(old);
@ -81,103 +80,66 @@ impl OutBufferSwapchain {
pub struct BufFdOut { pub struct BufFdOut {
fd: Rc<OwnedFd>, fd: Rc<OwnedFd>,
ring: Rc<IoUring>, ring: Rc<IoUring>,
wheel: Rc<Wheel>,
cmsg_buf: Box<[MaybeUninit<u8>; CMSG_BUF_SIZE]>, cmsg_buf: Box<[MaybeUninit<u8>; CMSG_BUF_SIZE]>,
fd_ids: Vec<i32>, fd_ids: Vec<i32>,
} }
impl BufFdOut { impl BufFdOut {
pub fn new(fd: &Rc<OwnedFd>, ring: &Rc<IoUring>, wheel: &Rc<Wheel>) -> Self { pub fn new(fd: &Rc<OwnedFd>, ring: &Rc<IoUring>) -> Self {
Self { Self {
fd: fd.clone(), fd: fd.clone(),
ring: ring.clone(), ring: ring.clone(),
wheel: wheel.clone(),
cmsg_buf: Box::new([MaybeUninit::uninit(); CMSG_BUF_SIZE]), cmsg_buf: Box::new([MaybeUninit::uninit(); CMSG_BUF_SIZE]),
fd_ids: vec![], fd_ids: vec![],
} }
} }
pub async fn flush( pub async fn flush(&mut self, buf: &mut OutBuffer, timeout: Time) -> Result<(), BufFdError> {
&mut self, while buf.meta.read_pos < buf.meta.write_pos {
buf: &mut OutBuffer, self.flush_buffer(buf, Some(timeout)).await?;
timeout: &mut Option<Fuse<WheelTimeoutFuture>>,
) -> Result<(), BufFdError> {
while buf.read_pos < buf.write_pos {
if self.flush_sync(buf)? {
if timeout.is_none() {
*timeout = Some(self.wheel.timeout(5000).fuse());
}
select! {
_ = timeout.as_mut().unwrap() => {
return Err(BufFdError::Timeout);
},
res = self.ring.writable(&self.fd).fuse() => {
res?;
},
}
}
} }
buf.read_pos = 0; buf.meta.read_pos = 0;
buf.write_pos = 0; buf.meta.write_pos = 0;
Ok(()) Ok(())
} }
pub async fn flush_no_timeout(&mut self, buf: &mut OutBuffer) -> Result<(), BufFdError> { pub async fn flush_no_timeout(&mut self, buf: &mut OutBuffer) -> Result<(), BufFdError> {
while buf.read_pos < buf.write_pos { while buf.meta.read_pos < buf.meta.write_pos {
if self.flush_sync(buf)? { self.flush_buffer(buf, None).await?;
let _ = self.ring.writable(&self.fd).await?;
}
} }
buf.read_pos = 0; buf.meta.read_pos = 0;
buf.write_pos = 0; buf.meta.write_pos = 0;
Ok(()) Ok(())
} }
fn flush_sync(&mut self, buffer: &mut OutBuffer) -> Result<bool, BufFdError> { async fn flush_buffer(
while buffer.read_pos < buffer.write_pos { &mut self,
let mut buf = unsafe { &(*buffer.buf)[buffer.read_pos..buffer.write_pos] }; buffer: &mut OutBuffer,
let mut cmsg_len = 0; timeout: Option<Time>,
let mut fds_opt = None; ) -> Result<(), BufFdError> {
{ let mut buf = buffer
let mut f = buffer.fds.front().map(|f| f.pos); .buf
if f == Some(buffer.read_pos) { .slice(buffer.meta.read_pos..buffer.meta.write_pos);
let fds = buffer.fds.pop_front().unwrap(); let mut fds = vec![];
self.fd_ids.clear(); {
self.fd_ids.extend(fds.fds.iter().map(|f| f.raw())); let mut f = buffer.meta.fds.front().map(|f| f.pos);
let hdr = c::cmsghdr { if f == Some(buffer.meta.read_pos) {
cmsg_len: 0, fds = buffer.meta.fds.pop_front().unwrap().fds;
cmsg_level: c::SOL_SOCKET, f = buffer.meta.fds.front().map(|f| f.pos)
cmsg_type: c::SCM_RIGHTS, }
}; if let Some(next_pos) = f {
let mut cmsg_buf = &mut self.cmsg_buf[..]; buf = buffer.buf.slice(buffer.meta.read_pos..next_pos);
cmsg_len = uapi::cmsg_write(&mut cmsg_buf, hdr, &self.fd_ids[..]).unwrap();
fds_opt = Some(fds);
f = buffer.fds.front().map(|f| f.pos)
}
if let Some(next_pos) = f {
buf = &buf[..next_pos - buffer.read_pos];
}
} }
let hdr = uapi::Msghdr {
iov: slice::from_ref(&buf),
control: Some(&self.cmsg_buf[..cmsg_len]),
name: uapi::sockaddr_none_ref(),
};
let bytes_sent =
match uapi::sendmsg(self.fd.raw(), &hdr, c::MSG_DONTWAIT | c::MSG_NOSIGNAL) {
Ok(b) => b,
Err(Errno(c::EAGAIN)) => {
if let Some(fds) = fds_opt {
buffer.fds.push_front(fds);
}
return Ok(true);
}
Err(Errno(c::ECONNRESET)) => return Err(BufFdError::Closed),
Err(e) => return Err(BufFdError::Io(e.into())),
};
buffer.read_pos += bytes_sent;
} }
Ok(false) match self.ring.sendmsg(&self.fd, buf, fds, timeout).await {
Ok(n) => {
buffer.meta.read_pos += n;
Ok(())
}
Err(IoUringError::OsError(OsError(c::ECONNRESET))) => return Err(BufFdError::Closed),
Err(IoUringError::OsError(OsError(c::ETIME))) => return Err(BufFdError::Timeout),
Err(e) => return Err(BufFdError::Ring(e)),
}
} }
pub async fn flush2( pub async fn flush2(
@ -238,11 +200,3 @@ impl BufFdOut {
Ok(false) Ok(false)
} }
} }
impl Drop for OutBuffer {
fn drop(&mut self) {
unsafe {
Box::from_raw(self.buf as *mut [MaybeUninit<u32>; OUT_BUF_SIZE / 4]);
}
}
}

View file

@ -2,14 +2,15 @@ use {
crate::{ crate::{
fixed::Fixed, fixed::Fixed,
object::ObjectId, object::ObjectId,
utils::buffd::buf_out::{MsgFds, OutBuffer}, utils::buffd::buf_out::{MsgFds, OutBuffer, OutBufferMeta, OUT_BUF_SIZE},
}, },
std::{mem, mem::MaybeUninit, rc::Rc}, std::{mem, rc::Rc},
uapi::OwnedFd, uapi::{OwnedFd, Packed},
}; };
pub struct MsgFormatter<'a> { pub struct MsgFormatter<'a> {
buf: &'a mut OutBuffer, buf: &'a mut [u8],
meta: &'a mut OutBufferMeta,
pos: usize, pos: usize,
fds: &'a mut Vec<Rc<OwnedFd>>, fds: &'a mut Vec<Rc<OwnedFd>>,
} }
@ -17,24 +18,33 @@ pub struct MsgFormatter<'a> {
impl<'a> MsgFormatter<'a> { impl<'a> MsgFormatter<'a> {
pub fn new(buf: &'a mut OutBuffer, fds: &'a mut Vec<Rc<OwnedFd>>) -> Self { pub fn new(buf: &'a mut OutBuffer, fds: &'a mut Vec<Rc<OwnedFd>>) -> Self {
Self { Self {
pos: buf.write_pos, pos: buf.meta.write_pos,
buf, buf: &mut buf.buf[..],
fds, fds,
meta: &mut buf.meta,
} }
} }
fn write(&mut self, bytes: &[u8]) {
if bytes.len() > OUT_BUF_SIZE - self.meta.write_pos {
panic!("Out buffer overflow");
}
self.buf[self.meta.write_pos..self.meta.write_pos + bytes.len()].copy_from_slice(bytes);
self.meta.write_pos += bytes.len();
}
pub fn int(&mut self, int: i32) -> &mut Self { pub fn int(&mut self, int: i32) -> &mut Self {
self.buf.write(uapi::as_maybe_uninit_bytes(&int)); self.write(uapi::as_bytes(&int));
self self
} }
pub fn uint(&mut self, int: u32) -> &mut Self { pub fn uint(&mut self, int: u32) -> &mut Self {
self.buf.write(uapi::as_maybe_uninit_bytes(&int)); self.write(uapi::as_bytes(&int));
self self
} }
pub fn fixed(&mut self, fixed: Fixed) -> &mut Self { pub fn fixed(&mut self, fixed: Fixed) -> &mut Self {
self.buf.write(uapi::as_maybe_uninit_bytes(&fixed)); self.write(uapi::as_bytes(&fixed.0));
self self
} }
@ -50,9 +60,9 @@ impl<'a> MsgFormatter<'a> {
let len = s.len() + 1; let len = s.len() + 1;
let cap = (len + 3) & !3; let cap = (len + 3) & !3;
self.uint(len as u32); self.uint(len as u32);
self.buf.write(uapi::as_maybe_uninit_bytes(s)); self.write(uapi::as_bytes(s));
let none = [MaybeUninit::new(0); 4]; let none = [0; 4];
self.buf.write(&none[..cap - len + 1]); self.write(&none[..cap - len + 1]);
self self
} }
@ -71,46 +81,43 @@ impl<'a> MsgFormatter<'a> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn array<F: FnOnce(&mut MsgFormatter<'_>)>(&mut self, f: F) -> &mut Self { pub fn array<F: FnOnce(&mut MsgFormatter<'_>)>(&mut self, f: F) -> &mut Self {
let pos = self.buf.write_pos; let pos = self.meta.write_pos;
self.uint(0); self.uint(0);
let len = { let len = {
let mut fmt = MsgFormatter { let mut fmt = MsgFormatter {
buf: self.buf, buf: self.buf,
meta: self.meta,
pos, pos,
fds: self.fds, fds: self.fds,
}; };
f(&mut fmt); f(&mut fmt);
let len = self.buf.write_pos - pos - 4; let len = self.meta.write_pos - pos - 4;
let none = [MaybeUninit::new(0); 4]; let none = [0; 4];
self.buf self.write(&none[..self.meta.write_pos.wrapping_neg() & 3]);
.write(&none[..self.buf.write_pos.wrapping_neg() & 3]);
len as u32 len as u32
}; };
unsafe { self.buf[pos..pos + 4].copy_from_slice(uapi::as_bytes(&len));
(*self.buf.buf)[pos..pos + 4].copy_from_slice(uapi::as_maybe_uninit_bytes(&len));
}
self self
} }
pub fn binary<T: ?Sized>(&mut self, t: &T) -> &mut Self { pub fn binary<T: ?Sized + Packed>(&mut self, t: &T) -> &mut Self {
self.uint(mem::size_of_val(t) as u32); self.uint(mem::size_of_val(t) as u32);
self.buf.write(uapi::as_maybe_uninit_bytes(t)); self.write(uapi::as_bytes(t));
let none = [MaybeUninit::new(0); 4]; let none = [0; 4];
self.buf self.write(&none[..self.meta.write_pos.wrapping_neg() & 3]);
.write(&none[..self.buf.write_pos.wrapping_neg() & 3]);
self self
} }
pub fn write_len(self) { pub fn write_len(self) {
assert!(self.buf.write_pos - self.pos >= 8); assert!(self.meta.write_pos - self.pos >= 8);
assert_eq!(self.pos % 4, 0); assert_eq!(self.pos % 4, 0);
unsafe { unsafe {
let second_ptr = (self.buf.buf as *mut u8).add(self.pos + 4) as *mut u32; let second_ptr = self.buf.as_ptr().add(self.pos + 4) as *mut u32;
let len = ((self.buf.write_pos - self.pos) as u32) << 16; let len = ((self.meta.write_pos - self.pos) as u32) << 16;
*second_ptr |= len; *second_ptr |= len;
} }
if self.fds.len() > 0 { if self.fds.len() > 0 {
self.buf.fds.push_back(MsgFds { self.meta.fds.push_back(MsgFds {
pos: self.pos, pos: self.pos,
fds: mem::take(self.fds), fds: mem::take(self.fds),
}) })

View file

@ -53,7 +53,7 @@ impl Drop for Wheel {
} }
struct WheelTimeoutData { struct WheelTimeoutData {
id: u64, id: Cell<u64>,
expired: Cell<Option<Result<(), WheelError>>>, expired: Cell<Option<Result<(), WheelError>>>,
wheel: Rc<WheelData>, wheel: Rc<WheelData>,
waker: Cell<Option<Waker>>, waker: Cell<Option<Waker>>,
@ -74,7 +74,7 @@ pub struct WheelTimeoutFuture {
impl Drop for WheelTimeoutFuture { impl Drop for WheelTimeoutFuture {
fn drop(&mut self) { fn drop(&mut self) {
self.data.wheel.dispatchers.remove(&self.data.id); self.data.wheel.dispatchers.remove(&self.data.id.get());
self.data.waker.set(None); self.data.waker.set(None);
if !self.data.wheel.destroyed.get() { if !self.data.wheel.destroyed.get() {
self.data.expired.take(); self.data.expired.take();
@ -135,12 +135,13 @@ impl Wheel {
fn future(&self) -> WheelTimeoutFuture { fn future(&self) -> WheelTimeoutFuture {
let data = self.data.cached_futures.pop().unwrap_or_else(|| { let data = self.data.cached_futures.pop().unwrap_or_else(|| {
Rc::new(WheelTimeoutData { Rc::new(WheelTimeoutData {
id: self.data.next_id.fetch_add(1), id: Cell::new(0),
expired: Cell::new(None), expired: Cell::new(None),
wheel: self.data.clone(), wheel: self.data.clone(),
waker: Cell::new(None), waker: Cell::new(None),
}) })
}); });
data.id.set(self.data.next_id.fetch_add(1));
WheelTimeoutFuture { data } WheelTimeoutFuture { data }
} }
@ -148,7 +149,7 @@ impl Wheel {
if self.data.destroyed.get() { if self.data.destroyed.get() {
return WheelTimeoutFuture { return WheelTimeoutFuture {
data: Rc::new(WheelTimeoutData { data: Rc::new(WheelTimeoutData {
id: 0, id: Cell::new(0),
expired: Cell::new(Some(Err(WheelError::Destroyed))), expired: Cell::new(Some(Err(WheelError::Destroyed))),
wheel: self.data.clone(), wheel: self.data.clone(),
waker: Default::default(), waker: Default::default(),
@ -186,11 +187,11 @@ impl Wheel {
} }
self.data.expirations.borrow_mut().push(Reverse(WheelEntry { self.data.expirations.borrow_mut().push(Reverse(WheelEntry {
expiration, expiration,
id: future.data.id, id: future.data.id.get(),
})); }));
self.data self.data
.dispatchers .dispatchers
.set(future.data.id, future.data.clone()); .set(future.data.id.get(), future.data.clone());
future future
} }
} }
@ -235,7 +236,6 @@ impl WheelData {
} }
let now = Time::now()?; let now = Time::now()?;
let dist = now - self.start; let dist = now - self.start;
let mut to_dispatch = vec![];
{ {
let mut expirations = self.expirations.borrow_mut(); let mut expirations = self.expirations.borrow_mut();
while let Some(Reverse(entry)) = expirations.peek() { while let Some(Reverse(entry)) = expirations.peek() {
@ -243,7 +243,7 @@ impl WheelData {
break; break;
} }
if let Some(dispatcher) = self.dispatchers.remove(&entry.id) { if let Some(dispatcher) = self.dispatchers.remove(&entry.id) {
to_dispatch.push(dispatcher); dispatcher.complete(Ok(()));
} }
expirations.pop(); expirations.pop();
} }
@ -267,9 +267,6 @@ impl WheelData {
expirations.pop(); expirations.pop();
} }
} }
for dispatcher in to_dispatch {
dispatcher.complete(Ok(()));
}
Ok(()) Ok(())
} }
} }

View file

@ -23,7 +23,7 @@ use {
state::State, state::State,
tree::ToplevelNode, tree::ToplevelNode,
utils::{ utils::{
bitflags::BitflagsExt, clonecell::CloneCell, copyhashmap::CopyHashMap, bitflags::BitflagsExt, buf::Buf, clonecell::CloneCell, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt, linkedlist::LinkedList, numcell::NumCell, oserror::OsError, errorfmt::ErrorFmt, linkedlist::LinkedList, numcell::NumCell, oserror::OsError,
rc_eq::rc_eq, tri::Try, rc_eq::rc_eq, tri::Try,
}, },
@ -1678,7 +1678,7 @@ impl Wm {
log::error!("Could not get converted property: {}", e); log::error!("Could not get converted property: {}", e);
return Ok(()); return Ok(());
} }
let data = Rc::new(data); let mut data = Buf::from_slice(&data);
for transfer in transfers { for transfer in transfers {
if event.target != transfer.mime_type { if event.target != transfer.mime_type {
log::error!("Conversion yielded an incompatible mime type"); log::error!("Conversion yielded an incompatible mime type");
@ -1687,7 +1687,7 @@ impl Wm {
let id = self.transfer_ids.fetch_add(1); let id = self.transfer_ids.fetch_add(1);
let transfer = XToWaylandTransfer { let transfer = XToWaylandTransfer {
id, id,
data: data.clone(), data: data.slice(..),
fd: transfer.fd, fd: transfer.fd,
state: self.state.clone(), state: self.state.clone(),
shared: self.shared.clone(), shared: self.shared.clone(),
@ -2372,22 +2372,19 @@ impl Wm {
struct XToWaylandTransfer { struct XToWaylandTransfer {
id: u64, id: u64,
data: Rc<Vec<u8>>, data: Buf,
fd: Rc<OwnedFd>, fd: Rc<OwnedFd>,
state: Rc<State>, state: Rc<State>,
shared: Rc<XwmShared>, shared: Rc<XwmShared>,
} }
impl XToWaylandTransfer { impl XToWaylandTransfer {
async fn run(self) { async fn run(mut self) {
let timeout = self.state.wheel.timeout(5000); let timeout = self.state.wheel.timeout(5000);
pin_mut!(timeout); pin_mut!(timeout);
let mut pos = 0; let mut pos = 0;
while pos < self.data.len() { while pos < self.data.len() {
let f1 = self let f1 = self.state.ring.write(&self.fd, self.data.slice(pos..));
.state
.ring
.write(&self.fd, &self.data, pos, self.data.len() - pos);
pin_mut!(f1); pin_mut!(f1);
match future::select(f1, &mut timeout).await { match future::select(f1, &mut timeout).await {
Either::Left((res, _)) => match res.merge() { Either::Left((res, _)) => match res.merge() {