async: rebase wheel on top of async engine
This commit is contained in:
parent
87a90a8ae4
commit
3875c63172
13 changed files with 218 additions and 285 deletions
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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(..) {
|
||||||
|
|
|
||||||
|
|
@ -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![],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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), _)) => {
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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() => {
|
||||||
|
|
|
||||||
363
src/wheel.rs
363
src/wheel.rs
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue