Skip to main content

nuclei/time/
sleep.rs

1//! [`Sleep`]: a future that completes at a deadline, on the timer wheel of
2//! the shard that polls it, and the timer both it and
3//! [`SleepPrecise`](super::SleepPrecise) are built on, generic over the wheel
4//! it arms: the millisecond one, or the microsecond one off it.
5
6use core::cell::UnsafeCell;
7use core::fmt;
8use core::future::Future;
9use core::marker::PhantomData;
10use core::mem::ManuallyDrop;
11use core::pin::Pin;
12use core::task::{Context, Poll, Waker};
13use std::sync::Arc;
14use std::task::Wake;
15use std::time::Duration;
16
17use super::{Instant, clock};
18
19use super::wheel::{TimerKey, Wheel};
20use crate::rt::context;
21use crate::rt::shard::{Shard, Shared};
22use crate::sync::atomic_waker::AtomicWaker;
23use crate::sync::internal::{AtomicBool, Ordering};
24
25/// Waits until `duration` has passed.
26///
27/// The wait is at least `duration`, rounded up to the timer wheel's
28/// millisecond; a busy shard may let it run later by the time its current
29/// task takes to yield. [`sleep_precise`](super::sleep_precise) waits to the
30/// microsecond instead.
31#[inline(always)]
32pub fn sleep(duration: Duration) -> Sleep {
33    Sleep {
34        timer: Timer::after(duration),
35    }
36}
37
38/// Waits until `deadline`.
39pub fn sleep_until(deadline: impl Into<Instant>) -> Sleep {
40    Sleep {
41        timer: Timer::new(deadline.into()),
42    }
43}
44
45/// A deadline nobody reaches: about 30 years from now.
46pub(super) fn far_future() -> Instant {
47    Instant::now() + Duration::from_secs(86_400 * 365 * 30)
48}
49
50/// A future that completes at its deadline. Made by [`sleep`] and
51/// [`sleep_until`].
52///
53/// Polled on one of nuclei's shards, it arms a timer in that shard's wheel;
54/// polled anywhere else (another runtime's thread), it asks a shard of the
55/// current runtime (or the default one) to fire it. Dropping it cancels the
56/// timer.
57#[must_use = "a sleep does nothing unless awaited"]
58pub struct Sleep {
59    timer: Timer<Coarse>,
60}
61
62impl Sleep {
63    /// The deadline.
64    pub fn deadline(&self) -> Instant {
65        self.timer.deadline()
66    }
67
68    /// Whether the deadline has passed.
69    pub fn is_elapsed(&self) -> bool {
70        self.timer.is_elapsed()
71    }
72
73    /// Moves the deadline; the sleep is pending again until it.
74    pub fn reset(self: Pin<&mut Self>, deadline: impl Into<Instant>) {
75        self.get_mut().timer.reset(deadline.into());
76    }
77}
78
79impl Future for Sleep {
80    type Output = ();
81
82    #[inline(always)]
83    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
84        self.get_mut().timer.poll(cx)
85    }
86}
87
88impl fmt::Debug for Sleep {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        f.debug_struct("Sleep")
91            .field("deadline", &self.deadline())
92            .field("elapsed", &self.is_elapsed())
93            .finish()
94    }
95}
96
97/// Which of a shard's wheels a timer is armed in.
98pub(crate) trait Grain {
99    /// The microsecond wheel, off the millisecond one.
100    const PRECISE: bool;
101}
102
103/// The millisecond wheel, of [`Sleep`].
104pub(crate) struct Coarse;
105
106impl Grain for Coarse {
107    const PRECISE: bool = false;
108}
109
110/// The microsecond wheel, of [`SleepPrecise`](super::SleepPrecise).
111pub(crate) struct Fine;
112
113impl Grain for Fine {
114    const PRECISE: bool = true;
115}
116
117/// A timer that completes at its deadline, in a wheel of grain `G` of the
118/// shard that polls it. Dropping it cancels it.
119pub(super) struct Timer<G: Grain> {
120    deadline: Deadline,
121    /// Never dropped as it is: the drop releases what it holds, which leaves
122    /// it holding nothing.
123    state: ManuallyDrop<State>,
124    grain: PhantomData<G>,
125}
126
127/// A timer's deadline, as it was made: the expiry checks and the wheels read
128/// it in nanoseconds after the clock's origin, which only the poll that arms
129/// the timer asks for.
130#[derive(Clone, Copy)]
131enum Deadline {
132    /// Made from a duration: nanoseconds after the origin, which name its
133    /// instant exactly.
134    Nanos(u64),
135    /// Given as an instant (`sleep_until`, `reset`), which its nanoseconds
136    /// may not reach (before the origin, past their range).
137    Given(Instant),
138}
139
140enum State {
141    /// Not armed yet.
142    Idle,
143    /// Armed in the wheel of `owner`'s shard.
144    Local { owner: Arc<Shared>, key: TimerKey },
145    /// Armed for this thread by `shard`, which fires `cell`.
146    Remote {
147        shard: Arc<Shared>,
148        cell: Arc<RemoteTimer>,
149    },
150    /// Fired.
151    Done,
152}
153
154impl<G: Grain> Timer<G> {
155    pub(super) fn new(deadline: Instant) -> Self {
156        Self {
157            deadline: Deadline::Given(deadline),
158            state: ManuallyDrop::new(State::Idle),
159            grain: PhantomData,
160        }
161    }
162
163    /// A timer `duration` from now on the runtime's clock: the clock read
164    /// once, in nanoseconds; one past their range is [`far_future`]'s, as an
165    /// instant would be.
166    #[inline(always)]
167    pub(super) fn after(duration: Duration) -> Self {
168        let span = duration
169            .as_secs()
170            .checked_mul(1_000_000_000)
171            .and_then(|whole| whole.checked_add(u64::from(duration.subsec_nanos())));
172        // Read outside a closure, which the clock's read would keep from
173        // being inlined.
174        let now = clock::now_nanos();
175        match span.and_then(|span| now.checked_add(span)) {
176            Some(at) => Self {
177                deadline: Deadline::Nanos(at),
178                state: ManuallyDrop::new(State::Idle),
179                grain: PhantomData,
180            },
181            None => Self::new(far_future()),
182        }
183    }
184
185    /// The deadline.
186    pub(super) fn deadline(&self) -> Instant {
187        match self.deadline {
188            Deadline::Nanos(at) => Instant::from_std(clock::instant(at)),
189            Deadline::Given(at) => at,
190        }
191    }
192
193    /// The deadline in nanoseconds after the clock's origin.
194    #[inline]
195    fn nanos(&self) -> u64 {
196        match self.deadline {
197            Deadline::Nanos(at) => at,
198            Deadline::Given(at) => clock::nanos(at.into_std()),
199        }
200    }
201
202    /// Whether the deadline has passed.
203    pub(super) fn is_elapsed(&self) -> bool {
204        matches!(*self.state, State::Done) || clock::now_nanos() >= self.nanos()
205    }
206
207    /// Moves the deadline; the timer is pending again until it.
208    pub(super) fn reset(&mut self, deadline: Instant) {
209        self.release();
210        self.deadline = Deadline::Given(deadline);
211    }
212
213    /// Lets go of the timer, wherever it is armed, and leaves the timer idle.
214    #[inline]
215    fn release(&mut self) {
216        match *self.state {
217            State::Idle => {}
218            // Fired: nothing to let go of, and pending again (a reset).
219            State::Done => *self.state = State::Idle,
220            State::Local { .. } | State::Remote { .. } => self.release_armed(),
221        }
222    }
223
224    #[inline(never)]
225    fn release_armed(&mut self) {
226        match core::mem::replace(&mut *self.state, State::Idle) {
227            State::Local { owner, key } => {
228                let here = context::current(|current| match current {
229                    Some(shard) if shard.is(&owner) => {
230                        shard.wheel_of(G::PRECISE).borrow_mut().remove(key);
231                        true
232                    }
233                    _ => false,
234                });
235                if !here {
236                    owner.post_timer(TimerOp::Remove {
237                        key,
238                        precise: G::PRECISE,
239                    });
240                }
241            }
242            State::Remote { shard, cell } => {
243                cell.cancelled.store(true, Ordering::SeqCst);
244                // The waker goes now: a fire racing the cancel finds none to
245                // wake, and the task it belongs to is not held until then.
246                drop(cell.waker.take());
247                shard.post_timer(TimerOp::Cancel(cell));
248            }
249            State::Idle | State::Done => {}
250        }
251    }
252
253    #[inline(always)]
254    fn poll_local(&mut self, shard: &Shard, cx: &mut Context<'_>) -> Poll<()> {
255        if let State::Local { owner, key } = &*self.state
256            && shard.is(owner)
257        {
258            let key = *key;
259            let mut wheel = shard.wheel_of(G::PRECISE).borrow_mut();
260            // A fired timer let go of its entry as it fired.
261            if wheel.is_fired(key) {
262                drop(wheel);
263                self.end_local();
264                return Poll::Ready(());
265            }
266            wheel.set_waker(key, cx.waker());
267            return Poll::Pending;
268        }
269        if let State::Remote { cell, .. } = &*self.state {
270            return poll_cell(cell, cx).map(|()| *self.state = State::Done);
271        }
272        // Not armed here: armed now (after letting go of an arming on
273        // another shard, if it moved).
274        self.release();
275        let at = self.nanos();
276        if clock::now_nanos() >= at {
277            *self.state = State::Done;
278            return Poll::Ready(());
279        }
280        let at = shard.time_of(G::PRECISE, at);
281        let mut wheel = shard.wheel_of(G::PRECISE).borrow_mut();
282        let key = wheel.insert(at, cx.waker());
283        if wheel.is_fired(key) {
284            drop(wheel);
285            *self.state = State::Done;
286            return Poll::Ready(());
287        }
288        drop(wheel);
289        *self.state = State::Local {
290            owner: Arc::clone(&shard.shared),
291            key,
292        };
293        Poll::Pending
294    }
295
296    fn poll_remote(&mut self, cx: &mut Context<'_>) -> Poll<()> {
297        if let State::Remote { cell, .. } = &*self.state {
298            return poll_cell(cell, cx).map(|()| *self.state = State::Done);
299        }
300        self.release();
301        let at = self.nanos();
302        if clock::now_nanos() >= at {
303            *self.state = State::Done;
304            return Poll::Ready(());
305        }
306        let cell = Arc::new(RemoteTimer {
307            fired: AtomicBool::new(false),
308            cancelled: AtomicBool::new(false),
309            orphaned: AtomicBool::new(false),
310            precise: G::PRECISE,
311            waker: AtomicWaker::new(),
312            key: UnsafeCell::new(None),
313        });
314        cell.waker.register(cx.waker());
315        let shard = crate::rt::runtime::service_shard();
316        shard.post_timer(TimerOp::Insert {
317            at,
318            cell: Arc::clone(&cell),
319        });
320        *self.state = State::Remote { shard, cell };
321        Poll::Pending
322    }
323
324    /// Ends a timer armed here that fired.
325    #[inline(always)]
326    fn end_local(&mut self) {
327        // The state is `Local`: its owner goes, and nothing else.
328        if let State::Local { owner, .. } = core::mem::replace(&mut *self.state, State::Done) {
329            drop(owner);
330        }
331    }
332
333    #[inline(always)]
334    pub(super) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<()> {
335        // A sleep's usual end: armed on the shard this thread runs, and
336        // fired.
337        if let State::Local { owner, key } = &*self.state
338            && let Some(shard) = context::running_inside(context::local())
339            && shard.is(owner)
340            && shard.wheel_of(G::PRECISE).borrow().is_fired(*key)
341        {
342            self.end_local();
343            return Poll::Ready(());
344        }
345        self.poll_any(cx)
346    }
347
348    #[inline(never)]
349    fn poll_any(&mut self, cx: &mut Context<'_>) -> Poll<()> {
350        if let State::Done = *self.state {
351            return Poll::Ready(());
352        }
353        if let State::Remote { cell, .. } = &*self.state
354            && cell.orphaned.load(Ordering::Acquire)
355        {
356            // The shard that held the timer went without firing it, and holds
357            // it no more: it is armed again where this poll runs.
358            *self.state = State::Idle;
359        }
360        // On the shard this thread runs with no bracket to take, as `current`
361        // would hand it over.
362        if let Some(shard) = context::running_inside(context::local()) {
363            return self.poll_local(shard, cx);
364        }
365        context::current(|current| match current {
366            Some(shard) => self.poll_local(shard, cx),
367            None => self.poll_remote(cx),
368        })
369    }
370}
371
372impl<G: Grain> Drop for Timer<G> {
373    fn drop(&mut self) {
374        // What the state holds goes here; idle or done, it holds nothing.
375        if matches!(*self.state, State::Local { .. } | State::Remote { .. }) {
376            self.release_armed();
377        }
378    }
379}
380
381fn poll_cell(cell: &RemoteTimer, cx: &mut Context<'_>) -> Poll<()> {
382    if cell.fired.load(Ordering::Acquire) {
383        return Poll::Ready(());
384    }
385    cell.waker.register(cx.waker());
386    if cell.fired.load(Ordering::Acquire) {
387        Poll::Ready(())
388    } else {
389        Poll::Pending
390    }
391}
392
393/// A timer a shard fires for a thread that is not one of its own.
394pub(crate) struct RemoteTimer {
395    fired: AtomicBool,
396    cancelled: AtomicBool,
397    /// The shard let go of the timer without firing it, as it went: the
398    /// sleep arms it again where it is polled next.
399    orphaned: AtomicBool,
400    /// Armed in the firing shard's microsecond wheel, not its millisecond
401    /// one.
402    precise: bool,
403    waker: AtomicWaker,
404    /// Where the timer is in the firing shard's wheel. Read and written by
405    /// that shard's thread only (arming, cancelling and firing all run there).
406    key: UnsafeCell<Option<TimerKey>>,
407}
408
409// SAFETY: every field but `key` is atomic or never written after the cell is
410// made; `key` is touched only by the thread of the shard that fires the timer
411// (see its docs).
412unsafe impl Send for RemoteTimer {}
413// SAFETY: as for `Send`.
414unsafe impl Sync for RemoteTimer {}
415
416impl RemoteTimer {
417    /// Fires the timer: called by the firing shard once the wheel let go of
418    /// it, its entry gone with the fire.
419    fn fire(&self) {
420        // SAFETY: on the firing shard's thread (see `key`).
421        unsafe { *self.key.get() = None };
422        self.fired.store(true, Ordering::Release);
423        self.waker.wake();
424    }
425
426    /// The shard that was to fire the timer let go of it unfired, as it went:
427    /// unless it fired or was cancelled, the sleep's task is woken, to arm it
428    /// again where it is polled (or be dropped, its own shard gone too). The
429    /// cell keeps the task's waker no longer, so the task, which holds the
430    /// cell, is not kept by it for good (proofs/tla/timer, `NoStrandedWaker`).
431    pub(crate) fn orphan(&self) {
432        if self.fired.load(Ordering::Acquire) || self.cancelled.load(Ordering::SeqCst) {
433            return;
434        }
435        self.orphaned.store(true, Ordering::Release);
436        self.waker.wake();
437    }
438}
439
440/// A remote timer as its shard's wheel holds it: fired when the wheel wakes
441/// it, orphaned when the wheel drops it unfired (the shard went).
442struct Armed(Arc<RemoteTimer>);
443
444impl Wake for Armed {
445    fn wake(self: Arc<Self>) {
446        self.0.fire();
447    }
448
449    fn wake_by_ref(self: &Arc<Self>) {
450        self.0.fire();
451    }
452}
453
454impl Drop for Armed {
455    fn drop(&mut self) {
456        // A fire or a cancel came first: nothing to do.
457        self.0.orphan();
458    }
459}
460
461/// Work on a shard's timer wheels from another thread.
462pub(crate) enum TimerOp {
463    /// Arm a timer that fires `cell` at `at` (nanoseconds after the clock's
464    /// origin), for a thread with no shard, in the wheel `cell` names.
465    Insert { at: u64, cell: Arc<RemoteTimer> },
466    /// Remove a timer of this shard (of its microsecond wheel when
467    /// `precise`) armed by a sleep that moved away or was dropped on another
468    /// thread.
469    Remove { key: TimerKey, precise: bool },
470    /// Cancel a timer armed by `Insert`.
471    Cancel(Arc<RemoteTimer>),
472}
473
474impl TimerOp {
475    /// Whether the shard must look at the op now (an arm moves its next
476    /// deadline); a removal can wait for its next look.
477    pub(crate) fn needs_wake(&self) -> bool {
478        matches!(self, Self::Insert { .. })
479    }
480
481    /// The op's shard went and applies it never: an arm request orphans its
482    /// timer; a removal or a cancel had nothing left to do.
483    pub(crate) fn orphan(self) {
484        if let Self::Insert { cell, .. } = self {
485            cell.orphan();
486        }
487    }
488
489    /// Applies the op on its shard's thread; `wheel` is the shard's
490    /// millisecond wheel, which the caller holds.
491    pub(crate) fn apply(self, shard: &Shard, wheel: &mut Wheel) {
492        match self {
493            Self::Insert { at, cell } => {
494                if cell.cancelled.load(Ordering::SeqCst) {
495                    return;
496                }
497                let waker = Waker::from(Arc::new(Armed(Arc::clone(&cell))));
498                let at = shard.time_of(cell.precise, at);
499                let fired = if cell.precise {
500                    arm(&mut shard.wheel_of(true).borrow_mut(), at, &waker)
501                } else {
502                    arm(wheel, at, &waker)
503                };
504                match fired {
505                    None => {
506                        cell.fired.store(true, Ordering::Release);
507                        cell.waker.wake();
508                    }
509                    // SAFETY: on this shard's thread (see `RemoteTimer::key`).
510                    Some(key) => unsafe { *cell.key.get() = Some(key) },
511                }
512            }
513            Self::Remove {
514                key,
515                precise: false,
516            } => wheel.remove(key),
517            Self::Remove { key, precise: true } => shard.wheel_of(true).borrow_mut().remove(key),
518            Self::Cancel(cell) => {
519                // SAFETY: on this shard's thread (see `RemoteTimer::key`).
520                if let Some(key) = unsafe { (*cell.key.get()).take() } {
521                    if cell.precise {
522                        shard.wheel_of(true).borrow_mut().remove(key);
523                    } else {
524                        wheel.remove(key);
525                    }
526                }
527            }
528        }
529    }
530}
531
532/// Arms a timer for `at` in `wheel`: its key, or `None` when the deadline
533/// passed already (it took no entry).
534fn arm(wheel: &mut Wheel, at: u64, waker: &Waker) -> Option<TimerKey> {
535    let key = wheel.insert(at, waker);
536    (!wheel.is_fired(key)).then_some(key)
537}