1
0
Fork 0
forked from wry/wry

async: rebase wheel on top of async engine

This commit is contained in:
Julian Orth 2022-05-12 17:23:17 +02:00
parent 87a90a8ae4
commit 3875c63172
13 changed files with 218 additions and 285 deletions

View file

@ -1,7 +1,6 @@
mod ae_fd; mod ae_fd;
mod ae_queue; mod ae_queue;
mod ae_task; mod ae_task;
mod ae_timeout;
mod ae_timer; mod ae_timer;
mod ae_yield; mod ae_yield;
@ -9,18 +8,15 @@ pub use {
crate::async_engine::ae_yield::Yield, crate::async_engine::ae_yield::Yield,
ae_fd::{AsyncFd, FdStatus}, ae_fd::{AsyncFd, FdStatus},
ae_task::SpawnedFuture, ae_task::SpawnedFuture,
ae_timeout::Timeout,
ae_timer::Timer, ae_timer::Timer,
}; };
use { use {
crate::{ crate::{
event_loop::{EventLoop, EventLoopError}, event_loop::{EventLoop, EventLoopError},
utils::{copyhashmap::CopyHashMap, numcell::NumCell, oserror::OsError}, utils::{copyhashmap::CopyHashMap, numcell::NumCell, oserror::OsError},
wheel::{Wheel, WheelError},
}, },
ae_fd::AsyncFdData, ae_fd::AsyncFdData,
ae_queue::{DispatchQueue, Dispatcher}, ae_queue::{DispatchQueue, Dispatcher},
ae_timeout::TimeoutData,
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
future::Future, future::Future,
@ -32,8 +28,6 @@ use {
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum AsyncError { pub enum AsyncError {
#[error("The timer wheel returned an error")]
WheelError(#[from] WheelError),
#[error("The event loop caused an error")] #[error("The event loop caused an error")]
EventLoopError(#[from] EventLoopError), EventLoopError(#[from] EventLoopError),
#[error("Could not read from a timer")] #[error("Could not read from a timer")]
@ -54,37 +48,21 @@ pub enum Phase {
const NUM_PHASES: usize = 4; const NUM_PHASES: usize = 4;
pub struct AsyncEngine { pub struct AsyncEngine {
wheel: Rc<Wheel>,
el: Rc<EventLoop>, el: Rc<EventLoop>,
queue: Rc<DispatchQueue>, queue: Rc<DispatchQueue>,
fds: CopyHashMap<i32, Rc<AsyncFdData>>, fds: CopyHashMap<i32, Rc<AsyncFdData>>,
} }
impl AsyncEngine { impl AsyncEngine {
pub fn install(el: &Rc<EventLoop>, wheel: &Rc<Wheel>) -> Result<Rc<Self>, AsyncError> { pub fn install(el: &Rc<EventLoop>) -> Result<Rc<Self>, AsyncError> {
let queue = Dispatcher::install(el)?; let queue = Dispatcher::install(el)?;
Ok(Rc::new(Self { Ok(Rc::new(Self {
wheel: wheel.clone(),
el: el.clone(), el: el.clone(),
queue, queue,
fds: CopyHashMap::new(), fds: CopyHashMap::new(),
})) }))
} }
pub fn timeout(&self, ms: u64) -> Result<Timeout, AsyncError> {
let data = Rc::new(TimeoutData {
expired: Cell::new(false),
waker: RefCell::new(None),
});
let id = self.wheel.id();
self.wheel.timeout(id, ms, data.clone())?;
Ok(Timeout {
id,
wheel: self.wheel.clone(),
data,
})
}
pub fn timer(self: &Rc<Self>, clock_id: c::c_int) -> Result<Timer, AsyncError> { pub fn timer(self: &Rc<Self>, clock_id: c::c_int) -> Result<Timer, AsyncError> {
Timer::new(self, clock_id) Timer::new(self, clock_id)
} }

View file

@ -146,10 +146,6 @@ impl AsyncFd {
self.data.fd.raw() self.data.fd.raw()
} }
pub fn eng(&self) -> &Rc<AsyncEngine> {
&self.engine
}
pub fn readable(&self) -> AsyncFdReadable { pub fn readable(&self) -> AsyncFdReadable {
AsyncFdReadable { AsyncFdReadable {
fd: self, fd: self,

View file

@ -1,51 +0,0 @@
use {
crate::wheel::{Wheel, WheelDispatcher, WheelId},
std::{
cell::{Cell, RefCell},
error::Error,
future::Future,
pin::Pin,
rc::Rc,
task::{Context, Poll, Waker},
},
};
pub(super) struct TimeoutData {
pub expired: Cell<bool>,
pub waker: RefCell<Option<Waker>>,
}
impl WheelDispatcher for TimeoutData {
fn dispatch(self: Rc<Self>) -> Result<(), Box<dyn Error>> {
self.expired.set(true);
if let Some(w) = self.waker.borrow_mut().take() {
w.wake();
}
Ok(())
}
}
pub struct Timeout {
pub(super) id: WheelId,
pub(super) wheel: Rc<Wheel>,
pub(super) data: Rc<TimeoutData>,
}
impl Future for Timeout {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.data.expired.get() {
Poll::Ready(())
} else {
*self.data.waker.borrow_mut() = Some(cx.waker().clone());
Poll::Pending
}
}
}
impl Drop for Timeout {
fn drop(&mut self) {
self.wheel.remove(self.id);
}
}

View file

@ -23,9 +23,8 @@ pub async fn client(data: Rc<Client>) {
} }
drop(recv); drop(recv);
data.flush_request.trigger(); data.flush_request.trigger();
match data.state.eng.timeout(5000) { match data.state.wheel.timeout(5000).await {
Ok(timeout) => { Ok(_) => {
timeout.await;
log::error!("Could not shut down client {} within 5 seconds", data.id.0); log::error!("Could not shut down client {} within 5 seconds", data.id.0);
} }
Err(e) => { Err(e) => {
@ -101,7 +100,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.clone()); let mut out = BufFdOut::new(data.socket.clone(), &data.state.wheel);
let mut buffers = VecDeque::new(); let mut buffers = VecDeque::new();
loop { loop {
data.flush_request.triggered().await; data.flush_request.triggered().await;

View file

@ -118,8 +118,8 @@ fn start_compositor2(
sighand::install(&el)?; sighand::install(&el)?;
let xkb_ctx = XkbContext::new().unwrap(); let xkb_ctx = XkbContext::new().unwrap();
let xkb_keymap = xkb_ctx.keymap_from_str(include_str!("keymap.xkb")).unwrap(); let xkb_keymap = xkb_ctx.keymap_from_str(include_str!("keymap.xkb")).unwrap();
let wheel = Wheel::install(&el)?; let engine = AsyncEngine::install(&el)?;
let engine = AsyncEngine::install(&el, &wheel)?; let wheel = Wheel::new(&engine)?;
let io_uring = IoUring::new(&engine, 32)?; let io_uring = IoUring::new(&engine, 32)?;
let (_run_toplevel_future, run_toplevel) = RunToplevel::install(&engine); let (_run_toplevel_future, run_toplevel) = RunToplevel::install(&engine);
let node_ids = NodeIds::default(); let node_ids = NodeIds::default();

View file

@ -241,7 +241,7 @@ impl ForkerProxy {
} }
async fn outgoing(self: Rc<Self>, state: Rc<State>, socket: AsyncFd) { async fn outgoing(self: Rc<Self>, state: Rc<State>, socket: AsyncFd) {
let mut io = IoOut::new(socket); let mut io = IoOut::new(socket, &state.wheel);
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(..) {
@ -305,6 +305,7 @@ enum ForkerMessage {
struct Forker { struct Forker {
socket: AsyncFd, socket: AsyncFd,
ae: Rc<AsyncEngine>, ae: Rc<AsyncEngine>,
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<()>>,
@ -331,11 +332,12 @@ impl Forker {
}) })
}); });
let el = EventLoop::new().unwrap(); let el = EventLoop::new().unwrap();
let wheel = Wheel::install(&el).unwrap(); let ae = AsyncEngine::install(&el).unwrap();
let ae = AsyncEngine::install(&el, &wheel).unwrap(); let wheel = Wheel::new(&ae).unwrap();
let forker = Rc::new(Forker { let forker = Rc::new(Forker {
socket: ae.fd(&socket).unwrap(), socket: ae.fd(&socket).unwrap(),
ae: ae.clone(), ae: ae.clone(),
wheel,
fds: RefCell::new(vec![]), fds: RefCell::new(vec![]),
outgoing: Default::default(), outgoing: Default::default(),
pending_spawns: Default::default(), pending_spawns: Default::default(),
@ -347,7 +349,7 @@ impl Forker {
} }
async fn outgoing(self: Rc<Self>) { async fn outgoing(self: Rc<Self>) {
let mut io = IoOut::new(self.socket.clone()); let mut io = IoOut::new(self.socket.clone(), &self.wheel);
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,6 +11,7 @@ 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,
@ -62,9 +63,9 @@ pub struct IoOut {
} }
impl IoOut { impl IoOut {
pub fn new(fd: AsyncFd) -> Self { pub fn new(fd: AsyncFd, wheel: &Rc<Wheel>) -> Self {
Self { Self {
outgoing: BufFdOut::new(fd), outgoing: BufFdOut::new(fd, wheel),
scratch: vec![], scratch: vec![],
fds: vec![], fds: vec![],
} }

View file

@ -141,7 +141,7 @@ fn run_test(it_run: &ItRun, test: &'static dyn TestCase, cfg: Rc<TestConfig>) {
Box::new(async move { Box::new(async move {
let future: Pin<_> = test.run(testrun.clone()).into(); let future: Pin<_> = test.run(testrun.clone()).into();
let future = state.eng.spawn2(Phase::Present, future); let future = state.eng.spawn2(Phase::Present, future);
let timeout = state.eng.timeout(5000).unwrap(); let timeout = state.wheel.timeout(5000);
match future::select(future, timeout).await { match future::select(future, timeout).await {
Either::Left((Ok(..), _)) => {} Either::Left((Ok(..), _)) => {}
Either::Left((Err(e), _)) => { Either::Left((Err(e), _)) => {

View file

@ -141,7 +141,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.fd.clone()), buf: BufFdOut::new(self.fd.clone(), &self.run.state.wheel),
buffers: Default::default(), buffers: Default::default(),
} }
.run(), .run(),

View file

@ -127,14 +127,14 @@ impl ToolClient {
Ok(e) => e, Ok(e) => e,
Err(e) => return Err(ToolClientError::CreateEventLoop(e)), Err(e) => return Err(ToolClientError::CreateEventLoop(e)),
}; };
let wheel = match Wheel::install(&el) { let eng = match AsyncEngine::install(&el) {
Ok(w) => w,
Err(e) => return Err(ToolClientError::CreateWheel(e)),
};
let eng = match AsyncEngine::install(&el, &wheel) {
Ok(e) => e, Ok(e) => e,
Err(e) => return Err(ToolClientError::CreateEngine(e)), Err(e) => return Err(ToolClientError::CreateEngine(e)),
}; };
let wheel = match Wheel::new(&eng) {
Ok(w) => w,
Err(e) => return Err(ToolClientError::CreateWheel(e)),
};
let xrd = match xrd() { let xrd = match xrd() {
Some(d) => d, Some(d) => d,
_ => return Err(ToolClientError::XrdNotSet), _ => return Err(ToolClientError::XrdNotSet),
@ -206,7 +206,7 @@ impl ToolClient {
slf.eng.spawn( slf.eng.spawn(
Outgoing { Outgoing {
tc: slf.clone(), tc: slf.clone(),
buf: BufFdOut::new(fd.clone()), buf: BufFdOut::new(fd.clone(), &slf.wheel),
buffers: Default::default(), buffers: Default::default(),
} }
.run(), .run(),

View file

@ -1,7 +1,8 @@
use { use {
crate::{ crate::{
async_engine::{AsyncFd, Timeout}, async_engine::AsyncFd,
utils::buffd::{BufFdError, BUF_SIZE, CMSG_BUF_SIZE}, utils::buffd::{BufFdError, BUF_SIZE, CMSG_BUF_SIZE},
wheel::{Wheel, WheelTimeoutFuture},
}, },
futures_util::{future::Fuse, select, FutureExt}, futures_util::{future::Fuse, select, FutureExt},
std::{ std::{
@ -79,14 +80,16 @@ impl OutBufferSwapchain {
pub struct BufFdOut { pub struct BufFdOut {
fd: AsyncFd, fd: AsyncFd,
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: AsyncFd) -> Self { pub fn new(fd: AsyncFd, wheel: &Rc<Wheel>) -> Self {
Self { Self {
fd, fd,
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![],
} }
@ -95,12 +98,12 @@ impl BufFdOut {
pub async fn flush( pub async fn flush(
&mut self, &mut self,
buf: &mut OutBuffer, buf: &mut OutBuffer,
timeout: &mut Option<Fuse<Timeout>>, timeout: &mut Option<Fuse<WheelTimeoutFuture>>,
) -> Result<(), BufFdError> { ) -> Result<(), BufFdError> {
while buf.read_pos < buf.write_pos { while buf.read_pos < buf.write_pos {
if self.flush_sync(buf)? { if self.flush_sync(buf)? {
if timeout.is_none() { if timeout.is_none() {
*timeout = Some(self.fd.eng().timeout(5000)?.fuse()); *timeout = Some(self.wheel.timeout(5000).fuse());
} }
select! { select! {
_ = timeout.as_mut().unwrap() => { _ = timeout.as_mut().unwrap() => {

View file

@ -1,112 +1,174 @@
use { use {
crate::{ crate::{
event_loop::{EventLoop, EventLoopDispatcher, EventLoopError, EventLoopId}, async_engine::{AsyncEngine, AsyncError, AsyncFd, SpawnedFuture},
time::{Time, TimeError}, time::{Time, TimeError},
utils::{copyhashmap::CopyHashMap, numcell::NumCell}, utils::{
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, oserror::OsError,
stack::Stack,
},
}, },
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
cmp::Reverse, cmp::Reverse,
collections::BinaryHeap, collections::BinaryHeap,
error::Error, future::Future,
pin::Pin,
rc::Rc, rc::Rc,
task::{Context, Poll, Waker},
time::Duration, time::Duration,
}, },
thiserror::Error, thiserror::Error,
uapi::{c, OwnedFd}, uapi::c,
}; };
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum WheelError { pub enum WheelError {
#[error("Could not create the timerfd: {0}")] #[error("Could not create the timerfd")]
CreateFailed(crate::utils::oserror::OsError), CreateFailed(#[source] OsError),
#[error("Could not set the timerfd: {0}")] #[error("Could not set the timerfd")]
SetFailed(crate::utils::oserror::OsError), SetFailed(#[source] OsError),
#[error("The timerfd is in an error state")] #[error("An async error occurred")]
ErrorEvent, AsyncError(#[from] AsyncError),
#[error("An event loop error occurred: {0}")] #[error("Cannot determine the time")]
EventLoopError(#[from] EventLoopError),
#[error("Cannot determine the time: {0}")]
TimeError(#[from] TimeError), TimeError(#[from] TimeError),
#[error("The timer wheel is already destroyed")] #[error("The timer wheel is already destroyed")]
Destroyed, Destroyed,
} #[error("Could not read from the timerfd")]
Read(#[source] OsError),
pub trait WheelDispatcher {
fn dispatch(self: Rc<Self>) -> Result<(), Box<dyn std::error::Error>>;
} }
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
struct WheelEntry { struct WheelEntry {
expiration: Time, expiration: Time,
id: WheelId, id: u64,
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct WheelId(u64);
pub struct Wheel { pub struct Wheel {
data: Rc<WheelData>,
}
impl Drop for Wheel {
fn drop(&mut self) {
self.data.kill();
}
}
struct WheelTimeoutData {
id: u64,
expired: Cell<Option<Result<(), WheelError>>>,
wheel: Rc<WheelData>,
waker: Cell<Option<Waker>>,
}
impl WheelTimeoutData {
fn complete(&self, res: Result<(), WheelError>) {
self.expired.set(Some(res));
if let Some(waker) = self.waker.take() {
waker.wake();
}
}
}
pub struct WheelTimeoutFuture {
data: Rc<WheelTimeoutData>,
}
impl Drop for WheelTimeoutFuture {
fn drop(&mut self) {
self.data.wheel.dispatchers.remove(&self.data.id);
self.data.waker.set(None);
if !self.data.wheel.destroyed.get() {
self.data.expired.take();
self.data.wheel.cached_futures.push(self.data.clone());
}
}
}
impl Future for WheelTimeoutFuture {
type Output = Result<(), WheelError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(res) = self.data.expired.take() {
Poll::Ready(res)
} else {
self.data.waker.set(Some(cx.waker().clone()));
Poll::Pending
}
}
}
pub struct WheelData {
destroyed: Cell<bool>, destroyed: Cell<bool>,
fd: OwnedFd, fd: AsyncFd,
next_id: NumCell<u64>, next_id: NumCell<u64>,
start: Time, start: Time,
current_expiration: Cell<Option<Time>>, current_expiration: Cell<Option<Time>>,
dispatchers: CopyHashMap<WheelId, Rc<dyn WheelDispatcher>>, dispatchers: CopyHashMap<u64, Rc<WheelTimeoutData>>,
periodic_dispatchers: CopyHashMap<WheelId, Rc<PeriodicDispatcher>>,
expirations: RefCell<BinaryHeap<Reverse<WheelEntry>>>, expirations: RefCell<BinaryHeap<Reverse<WheelEntry>>>,
id: EventLoopId, dispatcher: Cell<Option<SpawnedFuture<()>>>,
el: Rc<EventLoop>, cached_futures: Stack<Rc<WheelTimeoutData>>,
} }
impl Wheel { impl Wheel {
pub fn install(el: &Rc<EventLoop>) -> Result<Rc<Self>, WheelError> { pub fn new(eng: &Rc<AsyncEngine>) -> Result<Rc<Self>, WheelError> {
let fd = match uapi::timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC | c::TFD_NONBLOCK) { let fd = match uapi::timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC | c::TFD_NONBLOCK) {
Ok(fd) => fd, Ok(fd) => Rc::new(fd),
Err(e) => return Err(WheelError::CreateFailed(e.into())), Err(e) => return Err(WheelError::CreateFailed(e.into())),
}; };
let id = el.id(); let fd = eng.fd(&fd)?;
let wheel = Rc::new(Self { let data = Rc::new(WheelData {
destroyed: Cell::new(false), destroyed: Cell::new(false),
fd, fd,
next_id: Default::default(), next_id: NumCell::new(1),
start: Time::now()?, start: Time::now()?,
current_expiration: Cell::new(None), current_expiration: Default::default(),
dispatchers: CopyHashMap::new(), dispatchers: Default::default(),
periodic_dispatchers: Default::default(), expirations: Default::default(),
expirations: RefCell::new(Default::default()), dispatcher: Default::default(),
id, cached_futures: Default::default(),
el: el.clone(),
}); });
let wrapper = Rc::new(WheelWrapper { data.dispatcher
wheel: wheel.clone(), .set(Some(eng.spawn(data.clone().dispatch())));
}); Ok(Rc::new(Wheel { data }))
el.insert(id, Some(wheel.fd.raw()), c::EPOLLIN, wrapper)?;
Ok(wheel)
} }
pub fn id(&self) -> WheelId { fn future(&self) -> WheelTimeoutFuture {
WheelId(self.next_id.fetch_add(1)) let data = self.data.cached_futures.pop().unwrap_or_else(|| {
Rc::new(WheelTimeoutData {
id: self.data.next_id.fetch_add(1),
expired: Cell::new(None),
wheel: self.data.clone(),
waker: Cell::new(None),
})
});
WheelTimeoutFuture { data }
} }
fn check_destroyed(&self) -> Result<(), WheelError> { pub fn timeout(&self, ms: u64) -> WheelTimeoutFuture {
if self.destroyed.get() { if self.data.destroyed.get() {
return Err(WheelError::Destroyed); return WheelTimeoutFuture {
data: Rc::new(WheelTimeoutData {
id: 0,
expired: Cell::new(Some(Err(WheelError::Destroyed))),
wheel: self.data.clone(),
waker: Default::default(),
}),
};
} }
Ok(()) let future = self.future();
} let now = match Time::now() {
Ok(n) => n,
pub fn timeout( Err(e) => {
&self, future.data.expired.set(Some(Err(WheelError::TimeError(e))));
id: WheelId, return future;
ms: u64, }
dispatcher: Rc<dyn WheelDispatcher>, };
) -> Result<(), WheelError> { let expiration = (now + Duration::from_millis(ms)).round_to_ms();
self.check_destroyed()?; let current = self.data.current_expiration.get();
let expiration = (Time::now()? + Duration::from_millis(ms)).round_to_ms(); if current.is_none() || expiration - self.data.start < current.unwrap() - self.data.start {
let current = self.current_expiration.get(); log::info!("programming timer {}", self.data.fd.raw());
if current.is_none() || expiration - self.start < current.unwrap() - self.start {
let res = uapi::timerfd_settime( let res = uapi::timerfd_settime(
self.fd.raw(), self.data.fd.raw(),
c::TFD_TIMER_ABSTIME, c::TFD_TIMER_ABSTIME,
&c::itimerspec { &c::itimerspec {
it_interval: uapi::pod_zeroed(), it_interval: uapi::pod_zeroed(),
@ -114,101 +176,82 @@ impl Wheel {
}, },
); );
if let Err(e) = res { if let Err(e) = res {
return Err(WheelError::SetFailed(e.into())); future
.data
.expired
.set(Some(Err(WheelError::SetFailed(e.into()))));
return future;
} }
self.current_expiration.set(Some(expiration)); self.data.current_expiration.set(Some(expiration));
}
self.expirations
.borrow_mut()
.push(Reverse(WheelEntry { expiration, id }));
self.dispatchers.set(id, dispatcher);
Ok(())
}
#[allow(dead_code)]
pub fn periodic(
&self,
id: WheelId,
us: u64,
dispatcher: Rc<dyn WheelDispatcher>,
) -> Result<(), WheelError> {
self.check_destroyed()?;
let fd = match uapi::timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC | c::TFD_NONBLOCK) {
Ok(fd) => fd,
Err(e) => return Err(WheelError::CreateFailed(e.into())),
};
let tv_sec = (us / 1_000_000) as _;
let tv_nsec = (us % 1_000_000 * 1_000) as _;
let res = uapi::timerfd_settime(
fd.raw(),
0,
&c::itimerspec {
it_interval: c::timespec { tv_sec, tv_nsec },
it_value: c::timespec { tv_sec, tv_nsec },
},
);
if let Err(e) = res {
return Err(WheelError::SetFailed(e.into()));
}
let el_id = self.el.id();
let pd = Rc::new(PeriodicDispatcher {
fd,
id: el_id,
el: self.el.clone(),
dispatcher,
});
self.el
.insert(el_id, Some(pd.fd.raw()), c::EPOLLIN, pd.clone())?;
self.periodic_dispatchers.set(id, pd);
Ok(())
}
pub fn remove(&self, id: WheelId) {
// log::trace!("removing {:?} from wheel", id);
self.dispatchers.remove(&id);
if let Some(d) = self.periodic_dispatchers.remove(&id) {
let _ = self.el.remove(d.id);
} }
self.data.expirations.borrow_mut().push(Reverse(WheelEntry {
expiration,
id: future.data.id,
}));
self.data
.dispatchers
.set(future.data.id, future.data.clone());
future
} }
} }
struct WheelWrapper { impl WheelData {
wheel: Rc<Wheel>, fn kill(&self) {
} self.destroyed.set(true);
self.dispatcher.set(None);
impl EventLoopDispatcher for WheelWrapper { self.cached_futures.take();
fn dispatch( for (_, dispatcher) in self.dispatchers.lock().drain() {
self: Rc<Self>, dispatcher.complete(Err(WheelError::Destroyed));
_fd: Option<i32>,
events: i32,
) -> Result<(), Box<dyn std::error::Error>> {
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
return Err(Box::new(WheelError::ErrorEvent));
} }
}
async fn dispatch(self: Rc<Self>) {
loop {
if let Err(e) = self.fd.readable().await {
log::error!(
"Could not wait for the timerfd to become readable: {}",
ErrorFmt(e)
);
self.kill();
return;
}
if let Err(e) = self.dispatch_once() {
log::error!("Could not dispatch wheel expirations: {}", ErrorFmt(e));
self.kill();
return;
}
}
}
fn dispatch_once(&self) -> Result<(), WheelError> {
let mut n = 0u64; let mut n = 0u64;
while uapi::read(self.wheel.fd.raw(), &mut n).is_ok() {} loop {
let now = match Time::now() { if let Err(e) = uapi::read(self.fd.raw(), &mut n) {
Ok(n) => n, if e.0 == c::EAGAIN {
Err(e) => return Err(Box::new(e)),
};
let dist = now - self.wheel.start;
let mut to_dispatch = vec![];
{
let mut expirations = self.wheel.expirations.borrow_mut();
while let Some(Reverse(entry)) = expirations.peek() {
if entry.expiration - self.wheel.start > dist {
break; break;
} }
if let Some(dispatcher) = self.wheel.dispatchers.remove(&entry.id) { return Err(WheelError::Read(e.into()));
}
}
let now = Time::now()?;
let dist = now - self.start;
let mut to_dispatch = vec![];
{
let mut expirations = self.expirations.borrow_mut();
while let Some(Reverse(entry)) = expirations.peek() {
if entry.expiration - self.start > dist {
break;
}
if let Some(dispatcher) = self.dispatchers.remove(&entry.id) {
to_dispatch.push(dispatcher); to_dispatch.push(dispatcher);
} }
expirations.pop(); expirations.pop();
} }
self.wheel.current_expiration.set(None); self.current_expiration.set(None);
while let Some(Reverse(entry)) = expirations.peek() { while let Some(Reverse(entry)) = expirations.peek() {
if self.wheel.dispatchers.get(&entry.id).is_some() { if self.dispatchers.get(&entry.id).is_some() {
let res = uapi::timerfd_settime( let res = uapi::timerfd_settime(
self.wheel.fd.raw(), self.fd.raw(),
c::TFD_TIMER_ABSTIME, c::TFD_TIMER_ABSTIME,
&c::itimerspec { &c::itimerspec {
it_interval: uapi::pod_zeroed(), it_interval: uapi::pod_zeroed(),
@ -216,49 +259,17 @@ impl EventLoopDispatcher for WheelWrapper {
}, },
); );
if let Err(e) = res { if let Err(e) = res {
return Err(Box::new(WheelError::SetFailed(e.into()))); return Err(WheelError::SetFailed(e.into()));
} }
self.wheel.current_expiration.set(Some(entry.expiration)); self.current_expiration.set(Some(entry.expiration));
break; break;
} }
expirations.pop(); expirations.pop();
} }
} }
for dispatcher in to_dispatch { for dispatcher in to_dispatch {
dispatcher.dispatch()?; dispatcher.complete(Ok(()));
} }
Ok(()) Ok(())
} }
} }
impl Drop for WheelWrapper {
fn drop(&mut self) {
self.wheel.destroyed.set(true);
self.wheel.dispatchers.clear();
let _ = self.wheel.el.remove(self.wheel.id);
}
}
struct PeriodicDispatcher {
fd: OwnedFd,
id: EventLoopId,
el: Rc<EventLoop>,
dispatcher: Rc<dyn WheelDispatcher>,
}
impl EventLoopDispatcher for PeriodicDispatcher {
fn dispatch(self: Rc<Self>, _fd: Option<i32>, events: i32) -> Result<(), Box<dyn Error>> {
if events & (c::EPOLLERR | c::EPOLLHUP) != 0 {
return Err(Box::new(WheelError::ErrorEvent));
}
let mut n = 0u64;
while uapi::read(self.fd.raw(), &mut n).is_ok() {}
self.dispatcher.clone().dispatch()
}
}
impl Drop for PeriodicDispatcher {
fn drop(&mut self) {
let _ = self.el.remove(self.id);
}
}

View file

@ -2385,13 +2385,7 @@ struct XToWaylandTransfer {
impl XToWaylandTransfer { impl XToWaylandTransfer {
async fn run(self) { async fn run(self) {
let timeout = match self.state.eng.timeout(5000) { let timeout = self.state.wheel.timeout(5000);
Ok(to) => to,
Err(e) => {
log::error!("Could not create a timeout: {}", ErrorFmt(e));
return;
}
};
pin_mut!(timeout); pin_mut!(timeout);
let mut pos = 0; let mut pos = 0;
while pos < self.data.len() { while pos < self.data.len() {