use futures::{FutureExt, StreamExt};
use std::{
future::Future,
ops::Deref,
pin::Pin,
sync::{
atomic::{self, AtomicBool},
Arc,
},
time::Duration,
};
#[derive(Debug, Clone)]
pub struct WaitTokenRepeat<Tk> {
tk: Tk,
frq: Duration,
}
impl WaitTokenRepeat<WaitToken> {
pub async fn next(&mut self) -> bool {
let result = tokio::select! {
_ = tokio::time::sleep(self.frq) => {true}
_ = self.tk.cancelled() => {false}
};
result
}
}
#[derive(Debug, Default)]
pub struct WaitTokenGuard {
tk: WaitToken,
}
impl Drop for WaitTokenGuard {
fn drop(&mut self) {
self.tk.cancel();
}
}
impl Deref for WaitTokenGuard {
type Target = WaitToken;
fn deref(&self) -> &Self::Target {
&self.tk
}
}
impl WaitTokenGuard {
pub fn new() -> Self {
Self {
tk: WaitToken::new(),
}
}
pub fn clone_tk(&self) -> WaitToken {
self.deref().clone()
}
}
#[derive(Debug, Clone)]
pub struct WaitToken {
inner: Arc<WaitTokenRaw>,
}
#[derive(Debug)]
struct WaitTokenRaw {
state: AtomicBool,
parent: Option<WaitToken>,
future: Option<tokio::sync::Notify>,
}
impl WaitTokenRaw {
pub fn is_cancelled(&self) -> bool {
if let Some(parent) = &self.parent {
if parent.is_cancelled() {
return true;
}
}
self.state.load(atomic::Ordering::Acquire)
}
fn notified<'a: 'b, 'b>(&'a self) -> tokio::sync::futures::Notified<'b> {
if let Some(parent) = &self.parent {
return parent.inner.notified();
}
self.future.as_ref().unwrap().notified()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CancelState {
AlreadyCancelled,
Cancelled,
}
impl CancelState {
pub fn already_cancelled(self) -> bool {
self == CancelState::AlreadyCancelled
}
pub fn just_cancelled(self) -> bool {
self == CancelState::Cancelled
}
}
impl WaitToken {
pub fn new() -> Self {
Self {
inner: Arc::new(WaitTokenRaw {
state: false.into(),
parent: None,
future: Some(tokio::sync::Notify::new()),
}),
}
}
pub fn spawn_terminator(&self) -> std::thread::JoinHandle<Result<(), std::io::Error>> {
let this = self.clone();
std::thread::spawn(move || {
let mut signals = signal_hook::iterator::Signals::new([
signal_hook::consts::SIGTERM,
signal_hook::consts::SIGINT,
])
.inspect_err(|_| {
this.cancel();
})?;
for _ in &mut signals {
this.cancel();
}
Ok(())
})
}
pub fn ready() -> Self {
Self {
inner: Arc::new(WaitTokenRaw {
state: true.into(),
parent: None,
future: Some(tokio::sync::Notify::new()),
}),
}
}
fn wake(&self) {
if let Some(parent) = &self.inner.parent {
parent.wake();
return;
}
if let Some(future) = &self.inner.future {
future.notify_waiters();
}
}
fn notified(&self) -> tokio::sync::futures::Notified {
self.inner.notified()
}
pub fn cancel(&self) -> CancelState {
if self.inner.state.swap(true, atomic::Ordering::Release) {
return CancelState::AlreadyCancelled;
}
self.wake();
CancelState::Cancelled
}
pub fn reset(&self) {
self.inner.state.store(false, atomic::Ordering::Release);
}
pub fn is_cancelled(&self) -> bool {
self.inner.is_cancelled()
}
pub fn cancelled(&self) -> WaitTokenCancelled {
WaitTokenCancelled {
token: self.clone(),
future: Box::pin(unsafe {
std::mem::transmute::<
tokio::sync::futures::Notified<'_>,
tokio::sync::futures::Notified<'_>,
>(self.notified())
}),
}
}
pub fn until_cancel(
&self,
) -> futures::stream::TakeUntil<futures::stream::Repeat<()>, WaitTokenCancelled> {
futures::stream::repeat(()).take_until(self.cancelled())
}
pub fn on_cancel(&self) -> futures::stream::Once<WaitTokenCancelled> {
futures::stream::once(self.cancelled())
}
pub async fn on_cancel_then<Fut: Future + Send>(self, fut: Fut) -> Fut::Output {
self.cancelled().then(|_| fut).await
}
pub fn repeat_until_cancel(&self, frq: Duration) -> WaitTokenRepeat<Self> {
WaitTokenRepeat {
tk: self.clone(),
frq,
}
}
pub async fn run_fn<Fut: Future>(&self, fut: Fut) -> Option<Fut::Output> {
tokio::select! {
a = fut => Some(a),
_ = self.cancelled() => None,
}
}
pub fn make_child_token(&self) -> Self {
Self {
inner: Arc::new(WaitTokenRaw {
state: false.into(),
parent: Some(self.clone()),
future: None,
}),
}
}
pub fn guard(&self) -> WaitTokenGuard {
WaitTokenGuard { tk: self.clone() }
}
}
impl Default for WaitToken {
fn default() -> Self {
Self::new()
}
}
pub struct WaitTokenCancelled {
token: WaitToken,
future: Pin<Box<tokio::sync::futures::Notified<'static>>>,
}
impl Future for WaitTokenCancelled {
type Output = ();
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
loop {
if self.token.is_cancelled() {
return std::task::Poll::Ready(());
}
if self.future.poll_unpin(cx).is_pending() {
return std::task::Poll::Pending;
}
if self.token.is_cancelled() {
return std::task::Poll::Ready(());
}
let future = unsafe {
std::mem::transmute::<
tokio::sync::futures::Notified<'_>,
tokio::sync::futures::Notified<'_>,
>(self.token.notified())
};
self.future.set(future);
}
}
}