maitake/time/timer/
sleep.rs

1use super::{Ticks, Timer};
2use crate::{
3    loom::{
4        cell::UnsafeCell,
5        sync::atomic::{AtomicBool, Ordering::*},
6    },
7    sync::wait_cell::WaitCell,
8};
9use cordyceps::{list, Linked};
10use core::{
11    future::Future,
12    marker::PhantomPinned,
13    pin::Pin,
14    ptr::{self, NonNull},
15    task::{ready, Context, Poll},
16    time::Duration,
17};
18use mycelium_util::fmt;
19use pin_project::{pin_project, pinned_drop};
20
21/// A [`Future`] that completes after a specified [`Duration`].
22///
23/// This `Future` is returned by the [`sleep`] and [`try_sleep`] functions,
24/// and by the [`Timer::sleep`] and [`Timer::try_sleep`] methods.
25///
26/// [`sleep`]: crate::time::sleep
27/// [`try_sleep`]: crate::time::try_sleep
28#[pin_project(PinnedDrop)]
29#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
30pub struct Sleep<'timer> {
31    state: State,
32    timer: &'timer Timer,
33    #[pin]
34    entry: Entry,
35}
36
37#[derive(Debug)]
38#[pin_project]
39pub(super) struct Entry {
40    /// Intrusive linked list pointers.
41    #[pin]
42    links: UnsafeCell<list::Links<Entry>>,
43
44    /// The waker of the task awaiting this future.
45    waker: WaitCell,
46
47    /// The wheel's elapsed timestamp when this `sleep` future was first polled.
48    pub(super) deadline: Ticks,
49
50    pub(in crate::time::timer) ticks: Ticks,
51
52    pub(super) linked: AtomicBool,
53
54    // This type is !Unpin due to the heuristic from:
55    // <https://github.com/rust-lang/rust/pull/82834>
56    _pin: PhantomPinned,
57}
58
59#[derive(Copy, Clone, Debug, Eq, PartialEq)]
60enum State {
61    Unregistered,
62    Registered,
63    Completed,
64}
65
66// === impl Sleep ===
67
68impl<'timer> Sleep<'timer> {
69    pub(super) fn new(timer: &'timer Timer, ticks: Ticks) -> Self {
70        let now = timer.clock().now_ticks();
71        let deadline = now + ticks;
72        debug!(
73            target: "maitake::time::sleep",
74            now,
75            sleep.ticks = ticks,
76            sleep.deadline = deadline,
77            "Sleep::new({ticks})"
78        );
79        Self {
80            state: State::Unregistered,
81            timer,
82            entry: Entry {
83                links: UnsafeCell::new(list::Links::new()),
84                waker: WaitCell::new(),
85                deadline,
86                ticks,
87                linked: AtomicBool::new(false),
88                _pin: PhantomPinned,
89            },
90        }
91    }
92
93    /// Returns the [`Duration`] that this `Sleep` future will sleep for.
94    pub fn duration(&self) -> Duration {
95        self.timer.ticks_to_dur(self.entry.ticks)
96    }
97}
98
99impl Future for Sleep<'_> {
100    type Output = ();
101
102    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
103        let mut this = self.as_mut().project();
104        trace!(
105            target: "maitake::time::sleep",
106            addr = ?fmt::ptr(&*this.entry),
107            state = ?*this.state,
108            "Sleep::poll"
109        );
110        // If necessary, register the sleep
111        match test_dbg!(*this.state) {
112            State::Unregistered => {
113                let ptr =
114                    unsafe { ptr::NonNull::from(Pin::into_inner_unchecked(this.entry.as_mut())) };
115                // Acquire the wheel lock to insert the sleep.
116                let done = this.timer.core.with_lock(|core| {
117                    // While we are holding the wheel lock, go ahead and advance the
118                    // timer, too. This way, the timer wheel gets advanced more
119                    // frequently than just when a scheduler tick completes or a
120                    // timer IRQ fires, helping to increase timer accuracy.
121                    this.timer.advance_locked(core);
122
123                    match test_dbg!(core.register_sleep(ptr)) {
124                        Poll::Ready(()) => {
125                            *this.state = State::Completed;
126                            true
127                        }
128                        Poll::Pending => {
129                            *this.state = State::Registered;
130                            false
131                        }
132                    }
133                });
134                if done {
135                    return Poll::Ready(());
136                }
137            }
138            State::Registered => {}
139            State::Completed => return Poll::Ready(()),
140        }
141
142        let _poll = ready!(test_dbg!(this.entry.waker.poll_wait(cx)));
143        debug_assert!(
144            _poll.is_err(),
145            "a Sleep's WaitCell should only be woken by closing"
146        );
147        Poll::Ready(())
148    }
149}
150
151#[pinned_drop]
152impl PinnedDrop for Sleep<'_> {
153    fn drop(mut self: Pin<&mut Self>) {
154        let this = self.project();
155        trace!(sleep.addr = ?format_args!("{:p}", this.entry), "Sleep::drop");
156        // we only need to remove the sleep from the timer wheel if it's
157        // currently part of a linked list --- if the future hasn't been polled
158        // yet, or it has already completed, we don't need to lock the timer to
159        // remove it.
160        if test_dbg!(this.entry.linked.load(Acquire)) {
161            this.timer
162                .core
163                .with_lock(|core| core.cancel_sleep(this.entry));
164        }
165    }
166}
167
168impl fmt::Debug for Sleep<'_> {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        let Self {
171            state,
172            entry,
173            timer,
174        } = self;
175        f.debug_struct("Sleep")
176            .field("duration", &self.duration())
177            .field("state", state)
178            .field("addr", &fmt::ptr(entry))
179            .field("timer", &fmt::ptr(*timer))
180            .finish()
181    }
182}
183
184// === impl Entry ===
185
186unsafe impl Linked<list::Links<Entry>> for Entry {
187    type Handle = NonNull<Entry>;
188
189    fn into_ptr(r: Self::Handle) -> NonNull<Self> {
190        r
191    }
192
193    unsafe fn from_ptr(ptr: NonNull<Self>) -> Self::Handle {
194        ptr
195    }
196
197    unsafe fn links(target: NonNull<Self>) -> NonNull<list::Links<Entry>> {
198        // Safety: using `ptr::addr_of!` avoids creating a temporary
199        // reference, which stacked borrows dislikes.
200        let links = ptr::addr_of!((*target.as_ptr()).links);
201        (*links).with_mut(|links| {
202            // Safety: since the `target` pointer is `NonNull`, we can assume
203            // that pointers to its members are also not null, making this use
204            // of `new_unchecked` fine.
205            NonNull::new_unchecked(links)
206        })
207    }
208}
209
210impl Entry {
211    pub(super) fn fire(&self) {
212        trace!(sleep.addr = ?fmt::ptr(self), "firing sleep");
213        self.waker.close();
214        let _was_linked = self.linked.compare_exchange(true, false, AcqRel, Acquire);
215        test_trace!(sleep.was_linked = _was_linked.is_ok());
216    }
217}