Skip to main content

cargo/util/
local_poll_adapter.rs

1use crate::util::data_structures::HashMap;
2use futures::{FutureExt, future::LocalBoxFuture, stream::FuturesUnordered};
3use std::{hash::Hash, ops::Deref, task::Poll};
4
5/// A local (!Send) adapter for caching and executing an async method
6/// from a non-async context.
7///
8/// The `self_parameter`, `key`, and successful (Ok) results must all be cheap to `clone`.
9///
10/// Ensures at most one in-flight computation per key. Results are:
11/// - cached on success
12/// - not retained on error
13pub struct LocalPollAdapter<'a, S, K, R> {
14    pool: FuturesUnordered<LocalBoxFuture<'a, (K, R)>>,
15    cache: HashMap<K, Poll<R>>,
16    self_parameter: S,
17}
18
19impl<'a, S, K, V, E> LocalPollAdapter<'a, S, K, Result<V, E>>
20where
21    S: Clone + Deref + 'a,
22    K: Clone + Hash + Eq + 'a,
23    V: Clone,
24{
25    pub fn new(self_parameter: S) -> Self {
26        Self {
27            pool: FuturesUnordered::new(),
28            cache: HashMap::default(),
29            self_parameter,
30        }
31    }
32
33    /// Polls the result for `key`, spawning work if needed.
34    ///
35    /// If this function returns [`Poll::Pending`], call [`LocalPollAdapter::wait`]
36    /// to execute the work, then call this function again with the same key
37    /// to pick up the result.
38    ///
39    /// Futures that complete immediately are not queued.
40    pub fn poll<F>(&mut self, f: F, key: K) -> Poll<Result<V, E>>
41    where
42        F: AsyncFn(&S::Target, &K) -> Result<V, E> + 'a,
43    {
44        match self.cache.get(&key) {
45            // We have a cached success value, clone it and return.
46            Some(Poll::Ready(Ok(v))) => return Poll::Ready(Ok(v.clone())),
47            // We have a cached error value, remove it and return.
48            // Errors are not Clone, so they are only stored once.
49            Some(Poll::Ready(Err(_))) => return self.cache.remove(&key).unwrap(),
50            // This key is already pending.
51            Some(Poll::Pending) => return Poll::Pending,
52            // Looks like we have work to do!
53            None => {}
54        }
55
56        // Created a pinned future that executes the function,
57        // returning the key and the result.
58        let mut future = {
59            let key = key.clone();
60            let self_parameter = self.self_parameter.clone();
61            async move {
62                let v = f(self_parameter.deref(), &key).await;
63                (key, v)
64            }
65            .boxed_local()
66        };
67
68        // Attempt to run the future immediately. If it has no `await` yields,
69        // it will return here.
70        if let Some((k, v)) = (&mut future).now_or_never() {
71            if let Ok(success) = &v {
72                // Only cache successful results.
73                self.cache.insert(k, Poll::Ready(Ok(success.clone())));
74            }
75            return Poll::Ready(v);
76        }
77
78        // Insert Pending into the cache so we avoid queuing the same future twice.
79        self.cache.insert(key.clone(), Poll::Pending);
80
81        // Add the future to the pending queue.
82        self.pool.push(future);
83        Poll::Pending
84    }
85
86    /// Returns the number of pending futures.
87    pub fn pending_count(&self) -> usize {
88        self.pool.len()
89    }
90
91    /// Run all pending futures. Returns true if there was no work to do.
92    pub fn wait(&mut self) -> bool {
93        let is_empty = self.pool.is_empty();
94        for (k, v) in crate::util::block_on_stream(&mut self.pool) {
95            *self
96                .cache
97                .get_mut(&k)
98                .expect("all pending work is in the cache") = Poll::Ready(v);
99        }
100        is_empty
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::LocalPollAdapter;
107    use std::{rc::Rc, task::Poll};
108
109    /// Future that yields once.
110    fn yield_once() -> impl std::future::Future<Output = ()> {
111        let mut yielded = false;
112
113        std::future::poll_fn(move |cx| {
114            if yielded {
115                Poll::Ready(())
116            } else {
117                yielded = true;
118                cx.waker().wake_by_ref();
119                Poll::Pending
120            }
121        })
122    }
123
124    struct Thing {}
125
126    impl Thing {
127        async fn widen(&self, i: &i32) -> Result<i64, ()> {
128            if *i > 10 {
129                // Big numbers take longer to process (need to test futures that yield).
130                yield_once().await;
131            }
132            if *i % 2 != 0 {
133                // Odd numbers are not supported (need to test errors).
134                return Err(());
135            }
136            Ok(*i as i64)
137        }
138    }
139
140    /// Poll wrapper around `Thing`
141    struct PolledThing<'a> {
142        poller: LocalPollAdapter<'a, Rc<Thing>, i32, Result<i64, ()>>,
143    }
144
145    impl<'a> PolledThing<'a> {
146        fn new() -> Self {
147            Self {
148                poller: LocalPollAdapter::new(Rc::new(Thing {})),
149            }
150        }
151
152        // Non-async version of the widen method.
153        fn widen(&mut self, i: &i32) -> Poll<Result<i64, ()>> {
154            self.poller.poll(Thing::widen, i.clone())
155        }
156
157        fn wait(&mut self) -> bool {
158            self.poller.wait()
159        }
160    }
161
162    #[test]
163    fn immediate_success() {
164        let mut p = PolledThing::new();
165        assert_eq!(p.widen(&2), Poll::Ready(Ok(2)));
166        assert!(p.wait());
167    }
168
169    #[test]
170    fn immediate_error() {
171        let mut p = PolledThing::new();
172        assert_eq!(p.widen(&1), Poll::Ready(Err(())));
173        assert!(p.wait());
174    }
175
176    #[test]
177    fn deferred_error() {
178        let mut p = PolledThing::new();
179        assert_eq!(p.widen(&1001), Poll::Pending);
180        assert!(!p.wait());
181        assert_eq!(p.widen(&1001), Poll::Ready(Err(())));
182        assert!(p.wait());
183        // Errors are not cached
184        assert_eq!(p.widen(&1001), Poll::Pending);
185        assert!(!p.wait());
186        assert_eq!(p.widen(&1001), Poll::Ready(Err(())));
187        assert!(p.wait());
188    }
189
190    #[test]
191    fn deferred_success() {
192        let mut p = PolledThing::new();
193        assert_eq!(p.widen(&50), Poll::Pending);
194        assert!(!p.wait());
195        assert_eq!(p.widen(&50), Poll::Ready(Ok(50)));
196        assert!(p.wait());
197        // Success is cached.
198        assert_eq!(p.widen(&50), Poll::Ready(Ok(50)));
199        assert!(p.wait());
200    }
201}