user_driver/
backoff.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Async-friendly spin loop support.
5
6use pal_async::driver::Driver;
7use pal_async::timer::PolledTimer;
8use std::future::Future;
9use std::pin::Pin;
10use std::task::Context;
11use std::task::Poll;
12use std::time::Duration;
13
14const YIELD_ATTEMPTS: u64 = 250;
15const SHORT_SLEEP_ATTEMPTS: u64 = 250;
16const SHORT_SLEEP_DURATION: Duration = Duration::from_millis(1);
17const LONG_SLEEP_DURATION: Duration = Duration::from_millis(15);
18
19/// An object to yield execution of an async task while in a spin loop.
20///
21/// This is useful when waiting for some condition (such as a device register to
22/// change states) that can only be polled and does not have an interrupt-based
23/// notification.
24pub struct Backoff<'a> {
25    n: u64,
26    timer: Option<PolledTimer>,
27    driver: &'a dyn Driver,
28}
29
30impl<'a> Backoff<'a> {
31    /// Returns a new backoff object, usable for the lifetime of one spin loop.
32    pub fn new(driver: &'a dyn Driver) -> Self {
33        Self {
34            n: 0,
35            timer: None,
36            driver,
37        }
38    }
39
40    /// Yields execution.
41    ///
42    /// Initially just yields to any other pending tasks. Yields for longer the
43    /// more times this is called.
44    pub async fn back_off(&mut self) {
45        if self.n < 250 {
46            yield_once().await;
47        } else {
48            let delay = if self.n - YIELD_ATTEMPTS < SHORT_SLEEP_ATTEMPTS {
49                SHORT_SLEEP_DURATION
50            } else {
51                LONG_SLEEP_DURATION
52            };
53            self.timer
54                .get_or_insert_with(|| PolledTimer::new(self.driver))
55                .sleep(delay)
56                .await;
57        }
58        self.n += 1;
59    }
60}
61
62fn yield_once() -> YieldOnce {
63    YieldOnce { yielded: false }
64}
65
66struct YieldOnce {
67    yielded: bool,
68}
69
70impl Future for YieldOnce {
71    type Output = ();
72
73    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
74        if !self.yielded {
75            self.yielded = true;
76            cx.waker().wake_by_ref();
77            return Poll::Pending;
78        }
79        ().into()
80    }
81}