wheel: move timer wheel into workspace crate
This commit is contained in:
parent
aa70204881
commit
1c24ca26fe
5 changed files with 285 additions and 255 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -709,6 +709,7 @@ dependencies = [
|
||||||
"jay-tree-types",
|
"jay-tree-types",
|
||||||
"jay-units",
|
"jay-units",
|
||||||
"jay-utils",
|
"jay-utils",
|
||||||
|
"jay-wheel",
|
||||||
"jay-wire-buf",
|
"jay-wire-buf",
|
||||||
"jay-wire-types",
|
"jay-wire-types",
|
||||||
"jay-xcon",
|
"jay-xcon",
|
||||||
|
|
@ -909,6 +910,19 @@ dependencies = [
|
||||||
"uapi",
|
"uapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-wheel"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-async-engine",
|
||||||
|
"jay-io-uring",
|
||||||
|
"jay-time",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-wire-buf"
|
name = "jay-wire-buf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ members = [
|
||||||
"wire-buf",
|
"wire-buf",
|
||||||
"tree-types",
|
"tree-types",
|
||||||
"eventfd-cache",
|
"eventfd-cache",
|
||||||
|
"wheel",
|
||||||
"toml-config",
|
"toml-config",
|
||||||
"algorithms",
|
"algorithms",
|
||||||
"toml-spec",
|
"toml-spec",
|
||||||
|
|
@ -73,6 +74,7 @@ jay-wire-types = { version = "0.1.0", path = "wire-types" }
|
||||||
jay-wire-buf = { version = "0.1.0", path = "wire-buf" }
|
jay-wire-buf = { version = "0.1.0", path = "wire-buf" }
|
||||||
jay-tree-types = { version = "0.1.0", path = "tree-types" }
|
jay-tree-types = { version = "0.1.0", path = "tree-types" }
|
||||||
jay-eventfd-cache = { version = "0.1.0", path = "eventfd-cache" }
|
jay-eventfd-cache = { version = "0.1.0", path = "eventfd-cache" }
|
||||||
|
jay-wheel = { version = "0.1.0", path = "wheel" }
|
||||||
|
|
||||||
uapi = "0.2.13"
|
uapi = "0.2.13"
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
|
|
|
||||||
256
src/wheel.rs
256
src/wheel.rs
|
|
@ -1,255 +1 @@
|
||||||
use {
|
pub use jay_wheel::*;
|
||||||
crate::{
|
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
|
||||||
io_uring::{IoUring, IoUringError},
|
|
||||||
time::Time,
|
|
||||||
utils::{
|
|
||||||
buf::TypedBuf,
|
|
||||||
copyhashmap::CopyHashMap,
|
|
||||||
errorfmt::ErrorFmt,
|
|
||||||
hash_map_ext::HashMapExt,
|
|
||||||
numcell::NumCell,
|
|
||||||
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
|
||||||
stack::Stack,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
std::{
|
|
||||||
cell::{Cell, RefCell},
|
|
||||||
cmp::Reverse,
|
|
||||||
collections::BinaryHeap,
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
rc::Rc,
|
|
||||||
task::{Context, Poll, Waker},
|
|
||||||
time::Duration,
|
|
||||||
},
|
|
||||||
thiserror::Error,
|
|
||||||
uapi::{OwnedFd, c},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum WheelError {
|
|
||||||
#[error("Could not create the timerfd")]
|
|
||||||
CreateFailed(#[source] OsError),
|
|
||||||
#[error("Could not set the timerfd")]
|
|
||||||
SetFailed(#[source] OsError),
|
|
||||||
#[error("The timer wheel is already destroyed")]
|
|
||||||
Destroyed,
|
|
||||||
#[error("Could not read from the timerfd")]
|
|
||||||
Read(#[source] IoUringError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
|
|
||||||
struct WheelEntry {
|
|
||||||
expiration: Time,
|
|
||||||
id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Wheel {
|
|
||||||
data: Rc<WheelData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Wheel {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.data.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WheelTimeoutData {
|
|
||||||
id: Cell<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.get());
|
|
||||||
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>,
|
|
||||||
ring: Rc<IoUring>,
|
|
||||||
eng: Rc<AsyncEngine>,
|
|
||||||
fd: Rc<OwnedFd>,
|
|
||||||
next_id: NumCell<u64>,
|
|
||||||
start: Time,
|
|
||||||
current_expiration: Cell<Option<Time>>,
|
|
||||||
dispatchers: CopyHashMap<u64, Rc<WheelTimeoutData>>,
|
|
||||||
expirations: RefCell<BinaryHeap<Reverse<WheelEntry>>>,
|
|
||||||
dispatcher: Cell<Option<SpawnedFuture<()>>>,
|
|
||||||
cached_futures: Stack<Rc<WheelTimeoutData>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wheel {
|
|
||||||
pub fn new(eng: &Rc<AsyncEngine>, ring: &Rc<IoUring>) -> Result<Rc<Self>, WheelError> {
|
|
||||||
let fd = uapi::timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC)
|
|
||||||
.map(Rc::new)
|
|
||||||
.map_os_err(WheelError::CreateFailed)?;
|
|
||||||
let data = Rc::new(WheelData {
|
|
||||||
destroyed: Cell::new(false),
|
|
||||||
ring: ring.clone(),
|
|
||||||
eng: eng.clone(),
|
|
||||||
fd,
|
|
||||||
next_id: NumCell::new(1),
|
|
||||||
start: eng.now(),
|
|
||||||
current_expiration: Default::default(),
|
|
||||||
dispatchers: Default::default(),
|
|
||||||
expirations: Default::default(),
|
|
||||||
dispatcher: Default::default(),
|
|
||||||
cached_futures: Default::default(),
|
|
||||||
});
|
|
||||||
data.dispatcher
|
|
||||||
.set(Some(eng.spawn("wheel", data.clone().dispatch())));
|
|
||||||
Ok(Rc::new(Wheel { data }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&self) {
|
|
||||||
self.data.kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn future(&self) -> WheelTimeoutFuture {
|
|
||||||
let data = self.data.cached_futures.pop().unwrap_or_else(|| {
|
|
||||||
Rc::new(WheelTimeoutData {
|
|
||||||
id: Cell::new(0),
|
|
||||||
expired: Cell::new(None),
|
|
||||||
wheel: self.data.clone(),
|
|
||||||
waker: Cell::new(None),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
data.id.set(self.data.next_id.fetch_add(1));
|
|
||||||
WheelTimeoutFuture { data }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn timeout(&self, ms: u64) -> WheelTimeoutFuture {
|
|
||||||
if self.data.destroyed.get() {
|
|
||||||
return WheelTimeoutFuture {
|
|
||||||
data: Rc::new(WheelTimeoutData {
|
|
||||||
id: Cell::new(0),
|
|
||||||
expired: Cell::new(Some(Err(WheelError::Destroyed))),
|
|
||||||
wheel: self.data.clone(),
|
|
||||||
waker: Default::default(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let future = self.future();
|
|
||||||
let now = self.data.eng.now();
|
|
||||||
let expiration = (now + Duration::from_millis(ms)).round_to_ms();
|
|
||||||
let current = self.data.current_expiration.get();
|
|
||||||
if current.is_none() || expiration - self.data.start < current.unwrap() - self.data.start {
|
|
||||||
let res = uapi::timerfd_settime(
|
|
||||||
self.data.fd.raw(),
|
|
||||||
c::TFD_TIMER_ABSTIME,
|
|
||||||
&c::itimerspec {
|
|
||||||
it_interval: uapi::pod_zeroed(),
|
|
||||||
it_value: expiration.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if let Err(e) = res.to_os_error() {
|
|
||||||
future.data.expired.set(Some(Err(WheelError::SetFailed(e))));
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
self.data.current_expiration.set(Some(expiration));
|
|
||||||
}
|
|
||||||
self.data.expirations.borrow_mut().push(Reverse(WheelEntry {
|
|
||||||
expiration,
|
|
||||||
id: future.data.id.get(),
|
|
||||||
}));
|
|
||||||
self.data
|
|
||||||
.dispatchers
|
|
||||||
.set(future.data.id.get(), future.data.clone());
|
|
||||||
future
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WheelData {
|
|
||||||
fn kill(&self) {
|
|
||||||
self.destroyed.set(true);
|
|
||||||
self.dispatcher.set(None);
|
|
||||||
self.cached_futures.take();
|
|
||||||
for dispatcher in self.dispatchers.lock().drain_values() {
|
|
||||||
dispatcher.complete(Err(WheelError::Destroyed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn dispatch(self: Rc<Self>) {
|
|
||||||
let mut n = TypedBuf::new();
|
|
||||||
loop {
|
|
||||||
if let Err(e) = self.dispatch_once(&mut n).await {
|
|
||||||
log::error!("Could not dispatch wheel expirations: {}", ErrorFmt(e));
|
|
||||||
self.kill();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn dispatch_once(&self, n: &mut TypedBuf<u64>) -> Result<(), WheelError> {
|
|
||||||
if let Err(e) = self.ring.read(&self.fd, n.buf()).await {
|
|
||||||
return Err(WheelError::Read(e));
|
|
||||||
}
|
|
||||||
let now = self.eng.now();
|
|
||||||
let dist = now - self.start;
|
|
||||||
{
|
|
||||||
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) {
|
|
||||||
dispatcher.complete(Ok(()));
|
|
||||||
}
|
|
||||||
expirations.pop();
|
|
||||||
}
|
|
||||||
self.current_expiration.set(None);
|
|
||||||
while let Some(Reverse(entry)) = expirations.peek() {
|
|
||||||
if self.dispatchers.get(&entry.id).is_some() {
|
|
||||||
uapi::timerfd_settime(
|
|
||||||
self.fd.raw(),
|
|
||||||
c::TFD_TIMER_ABSTIME,
|
|
||||||
&c::itimerspec {
|
|
||||||
it_interval: uapi::pod_zeroed(),
|
|
||||||
it_value: entry.expiration.0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.map_os_err(WheelError::SetFailed)?;
|
|
||||||
self.current_expiration.set(Some(entry.expiration));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
expirations.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
15
wheel/Cargo.toml
Normal file
15
wheel/Cargo.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-wheel"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-async-engine = { version = "0.1.0", path = "../async-engine" }
|
||||||
|
jay-io-uring = { version = "0.1.0", path = "../io-uring" }
|
||||||
|
jay-time = { version = "0.1.0", path = "../time" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
log = { version = "0.4.20", features = ["std"] }
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
253
wheel/src/lib.rs
Normal file
253
wheel/src/lib.rs
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
use {
|
||||||
|
jay_async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
|
jay_io_uring::{IoUring, IoUringError},
|
||||||
|
jay_time::Time,
|
||||||
|
jay_utils::{
|
||||||
|
buf::TypedBuf,
|
||||||
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
|
hash_map_ext::HashMapExt,
|
||||||
|
numcell::NumCell,
|
||||||
|
oserror::{OsError, OsErrorExt, OsErrorExt2},
|
||||||
|
stack::Stack,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
cmp::Reverse,
|
||||||
|
collections::BinaryHeap,
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
|
task::{Context, Poll, Waker},
|
||||||
|
time::Duration,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{OwnedFd, c},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WheelError {
|
||||||
|
#[error("Could not create the timerfd")]
|
||||||
|
CreateFailed(#[source] OsError),
|
||||||
|
#[error("Could not set the timerfd")]
|
||||||
|
SetFailed(#[source] OsError),
|
||||||
|
#[error("The timer wheel is already destroyed")]
|
||||||
|
Destroyed,
|
||||||
|
#[error("Could not read from the timerfd")]
|
||||||
|
Read(#[source] IoUringError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
struct WheelEntry {
|
||||||
|
expiration: Time,
|
||||||
|
id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Wheel {
|
||||||
|
data: Rc<WheelData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Wheel {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.data.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WheelTimeoutData {
|
||||||
|
id: Cell<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.get());
|
||||||
|
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>,
|
||||||
|
ring: Rc<IoUring>,
|
||||||
|
eng: Rc<AsyncEngine>,
|
||||||
|
fd: Rc<OwnedFd>,
|
||||||
|
next_id: NumCell<u64>,
|
||||||
|
start: Time,
|
||||||
|
current_expiration: Cell<Option<Time>>,
|
||||||
|
dispatchers: CopyHashMap<u64, Rc<WheelTimeoutData>>,
|
||||||
|
expirations: RefCell<BinaryHeap<Reverse<WheelEntry>>>,
|
||||||
|
dispatcher: Cell<Option<SpawnedFuture<()>>>,
|
||||||
|
cached_futures: Stack<Rc<WheelTimeoutData>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wheel {
|
||||||
|
pub fn new(eng: &Rc<AsyncEngine>, ring: &Rc<IoUring>) -> Result<Rc<Self>, WheelError> {
|
||||||
|
let fd = uapi::timerfd_create(c::CLOCK_MONOTONIC, c::TFD_CLOEXEC)
|
||||||
|
.map(Rc::new)
|
||||||
|
.map_os_err(WheelError::CreateFailed)?;
|
||||||
|
let data = Rc::new(WheelData {
|
||||||
|
destroyed: Cell::new(false),
|
||||||
|
ring: ring.clone(),
|
||||||
|
eng: eng.clone(),
|
||||||
|
fd,
|
||||||
|
next_id: NumCell::new(1),
|
||||||
|
start: eng.now(),
|
||||||
|
current_expiration: Default::default(),
|
||||||
|
dispatchers: Default::default(),
|
||||||
|
expirations: Default::default(),
|
||||||
|
dispatcher: Default::default(),
|
||||||
|
cached_futures: Default::default(),
|
||||||
|
});
|
||||||
|
data.dispatcher
|
||||||
|
.set(Some(eng.spawn("wheel", data.clone().dispatch())));
|
||||||
|
Ok(Rc::new(Wheel { data }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.data.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn future(&self) -> WheelTimeoutFuture {
|
||||||
|
let data = self.data.cached_futures.pop().unwrap_or_else(|| {
|
||||||
|
Rc::new(WheelTimeoutData {
|
||||||
|
id: Cell::new(0),
|
||||||
|
expired: Cell::new(None),
|
||||||
|
wheel: self.data.clone(),
|
||||||
|
waker: Cell::new(None),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
data.id.set(self.data.next_id.fetch_add(1));
|
||||||
|
WheelTimeoutFuture { data }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timeout(&self, ms: u64) -> WheelTimeoutFuture {
|
||||||
|
if self.data.destroyed.get() {
|
||||||
|
return WheelTimeoutFuture {
|
||||||
|
data: Rc::new(WheelTimeoutData {
|
||||||
|
id: Cell::new(0),
|
||||||
|
expired: Cell::new(Some(Err(WheelError::Destroyed))),
|
||||||
|
wheel: self.data.clone(),
|
||||||
|
waker: Default::default(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let future = self.future();
|
||||||
|
let now = self.data.eng.now();
|
||||||
|
let expiration = (now + Duration::from_millis(ms)).round_to_ms();
|
||||||
|
let current = self.data.current_expiration.get();
|
||||||
|
if current.is_none() || expiration - self.data.start < current.unwrap() - self.data.start {
|
||||||
|
let res = uapi::timerfd_settime(
|
||||||
|
self.data.fd.raw(),
|
||||||
|
c::TFD_TIMER_ABSTIME,
|
||||||
|
&c::itimerspec {
|
||||||
|
it_interval: uapi::pod_zeroed(),
|
||||||
|
it_value: expiration.0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if let Err(e) = res.to_os_error() {
|
||||||
|
future.data.expired.set(Some(Err(WheelError::SetFailed(e))));
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
self.data.current_expiration.set(Some(expiration));
|
||||||
|
}
|
||||||
|
self.data.expirations.borrow_mut().push(Reverse(WheelEntry {
|
||||||
|
expiration,
|
||||||
|
id: future.data.id.get(),
|
||||||
|
}));
|
||||||
|
self.data
|
||||||
|
.dispatchers
|
||||||
|
.set(future.data.id.get(), future.data.clone());
|
||||||
|
future
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WheelData {
|
||||||
|
fn kill(&self) {
|
||||||
|
self.destroyed.set(true);
|
||||||
|
self.dispatcher.set(None);
|
||||||
|
self.cached_futures.take();
|
||||||
|
for dispatcher in self.dispatchers.lock().drain_values() {
|
||||||
|
dispatcher.complete(Err(WheelError::Destroyed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dispatch(self: Rc<Self>) {
|
||||||
|
let mut n = TypedBuf::new();
|
||||||
|
loop {
|
||||||
|
if let Err(e) = self.dispatch_once(&mut n).await {
|
||||||
|
log::error!("Could not dispatch wheel expirations: {}", ErrorFmt(e));
|
||||||
|
self.kill();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dispatch_once(&self, n: &mut TypedBuf<u64>) -> Result<(), WheelError> {
|
||||||
|
if let Err(e) = self.ring.read(&self.fd, n.buf()).await {
|
||||||
|
return Err(WheelError::Read(e));
|
||||||
|
}
|
||||||
|
let now = self.eng.now();
|
||||||
|
let dist = now - self.start;
|
||||||
|
{
|
||||||
|
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) {
|
||||||
|
dispatcher.complete(Ok(()));
|
||||||
|
}
|
||||||
|
expirations.pop();
|
||||||
|
}
|
||||||
|
self.current_expiration.set(None);
|
||||||
|
while let Some(Reverse(entry)) = expirations.peek() {
|
||||||
|
if self.dispatchers.get(&entry.id).is_some() {
|
||||||
|
uapi::timerfd_settime(
|
||||||
|
self.fd.raw(),
|
||||||
|
c::TFD_TIMER_ABSTIME,
|
||||||
|
&c::itimerspec {
|
||||||
|
it_interval: uapi::pod_zeroed(),
|
||||||
|
it_value: entry.expiration.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_os_err(WheelError::SetFailed)?;
|
||||||
|
self.current_expiration.set(Some(entry.expiration));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expirations.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue