use std::cell::Cell;
use std::cell::UnsafeCell;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::task::Context;
use std::task::Poll;
use std::task::Wake;
use std::task::Waker;
use std::time::Duration;
use futures::task::AtomicWaker;
use crate::reactor::Reactor;
use crate::reactor::ReactorInstant;
use crate::reactor::ReactorTimer;
struct MutableSleep<Tmr: ReactorTimer> {
sleep: UnsafeCell<Option<Tmr>>,
wake_state: Arc<MutableSleepWaker>,
internal_waker: Waker,
}
impl<Tmr: ReactorTimer + 'static> MutableSleep<Tmr> {
fn new() -> Self {
let wake_state = Arc::new(MutableSleepWaker {
ready: AtomicBool::new(false),
external_waker: AtomicWaker::new(),
});
Self {
sleep: Default::default(),
internal_waker: Waker::from(Arc::clone(&wake_state)),
wake_state,
}
}
fn poll_ready(&self, cx: &mut Context) -> Poll<()> {
self.wake_state.external_waker.register(cx.waker());
if self.wake_state.ready.swap(false, Ordering::AcqRel) {
return Poll::Ready(());
}
#[cfg(not(miri))]
{
let sleep = unsafe { self.sleep.get().as_mut().unwrap_unchecked() };
if let Some(sleep) = sleep
&& Tmr::Instant::now() >= sleep.deadline()
{
return Poll::Ready(());
}
}
Poll::Pending
}
fn clear(&self) {
unsafe {
*self.sleep.get() = None;
}
self.wake_state.ready.store(false, Ordering::Release);
}
fn change(&self, timer: Tmr) {
self.wake_state.ready.store(false, Ordering::Release);
let pin = unsafe {
*self.sleep.get() = Some(timer);
Pin::new_unchecked(
self
.sleep
.get()
.as_mut()
.unwrap_unchecked()
.as_mut()
.unwrap_unchecked(),
)
};
let waker = &self.internal_waker;
if pin.poll(&mut Context::from_waker(waker)).is_ready() {
self.internal_waker.wake_by_ref();
}
}
}
struct MutableSleepWaker {
ready: AtomicBool,
external_waker: AtomicWaker,
}
impl Wake for MutableSleepWaker {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.ready.store(true, Ordering::Release);
self.external_waker.wake();
}
}
pub(crate) struct UserTimer<R: Reactor> {
reactor: R,
sleep: MutableSleep<R::Timer>,
base_instant: R::Instant,
refed: Cell<bool>,
}
impl<R: Reactor + Default> Default for UserTimer<R> {
fn default() -> Self {
Self::new(R::default())
}
}
impl<R: Reactor> UserTimer<R> {
pub fn new(reactor: R) -> Self {
Self {
base_instant: reactor.now(),
sleep: MutableSleep::new(),
reactor,
refed: Cell::new(false),
}
}
pub fn schedule(&self, delay: Duration) {
let deadline = self.reactor.now().checked_add(delay).unwrap();
self.sleep.change(self.reactor.timer(deadline));
}
pub fn clear(&self) {
self.sleep.clear();
}
pub fn poll_ready(&self, cx: &mut Context) -> Poll<()> {
self.sleep.poll_ready(cx)
}
pub fn now(&self) -> f64 {
self.base_instant.elapsed().as_secs_f64() * 1000.0
}
pub fn ref_timer(&self) {
self.refed.set(true);
}
pub fn unref_timer(&self) {
self.refed.set(false);
}
pub fn is_refed(&self) -> bool {
self.refed.get()
}
}
#[cfg(all(test, feature = "reactor-tokio"))]
mod tests {
use std::sync::Arc;
use std::sync::mpsc;
use std::task::Wake;
use super::*;
struct NotifyWaker(mpsc::Sender<()>);
impl Wake for NotifyWaker {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.send(()).unwrap();
}
}
#[test]
fn timer_wake_from_another_thread() {
let sleep = MutableSleep::<crate::reactor_tokio::TokioTimer>::new();
let timer_waker = sleep.internal_waker.clone();
std::thread::spawn(move || timer_waker.wake())
.join()
.unwrap();
let (notify_tx, notify_rx) = mpsc::channel();
let external_waker = Waker::from(Arc::new(NotifyWaker(notify_tx)));
let mut cx = Context::from_waker(&external_waker);
assert_eq!(sleep.poll_ready(&mut cx), Poll::Ready(()));
let timer_waker = sleep.internal_waker.clone();
std::thread::spawn(move || timer_waker.wake())
.join()
.unwrap();
notify_rx.recv_timeout(Duration::from_secs(1)).unwrap();
assert_eq!(sleep.poll_ready(&mut cx), Poll::Ready(()));
let timer_waker = sleep.internal_waker.clone();
std::thread::spawn(move || timer_waker.wake())
.join()
.unwrap();
notify_rx.recv_timeout(Duration::from_secs(1)).unwrap();
assert_eq!(sleep.poll_ready(&mut cx), Poll::Ready(()));
}
}