maitake/time/timer/
sleep.rs1use 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#[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 #[pin]
42 links: UnsafeCell<list::Links<Entry>>,
43
44 waker: WaitCell,
46
47 pub(super) deadline: Ticks,
49
50 pub(in crate::time::timer) ticks: Ticks,
51
52 pub(super) linked: AtomicBool,
53
54 _pin: PhantomPinned,
57}
58
59#[derive(Copy, Clone, Debug, Eq, PartialEq)]
60enum State {
61 Unregistered,
62 Registered,
63 Completed,
64}
65
66impl<'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 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 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 let done = this.timer.core.with_lock(|core| {
117 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 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
184unsafe 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 let links = ptr::addr_of!((*target.as_ptr()).links);
201 (*links).with_mut(|links| {
202 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}