Skip to main content

minimal_executor/
local_pool_busy.rs

1use alloc::sync::{Arc, Weak};
2use futures::future::LocalFutureObj;
3use futures::{FutureExt};
4use core::task::{Poll};
5use crossbeam::queue::ArrayQueue;
6use futures::task::UnsafeFutureObj;
7use crate::poll_fn;
8use futures::future::FutureObj;
9use futures::task::Spawn;
10use futures::task::SpawnError;
11
12/// A single-threaded task pool for polling futures to completion.
13///
14/// This executor allows you to multiplex any number of tasks onto a single
15/// thread. It's appropriate to poll strictly I/O-bound futures that do very
16/// little work in between I/O actions.
17///
18/// To get a handle to the pool that implements
19/// [`Spawn`](futures_task::Spawn), use the
20/// [`spawner()`](LocalPool::spawner) method. Because the executor is
21/// single-threaded, it supports a special form of task spawning for non-`Send`
22/// futures, via [`spawn_local_obj`](futures_task::LocalSpawn::spawn_local_obj).
23#[derive(Debug)]
24pub struct LocalPool<'a, Ret = ()> {
25    pool: Arc<ArrayQueue<LocalFutureObj<'a, Ret>>>,
26}
27
28
29#[derive(Clone)]
30pub struct Spawner<'a, Ret> {
31    tx: Weak<ArrayQueue<LocalFutureObj<'a, Ret>>>,
32}
33
34
35impl<'a> Spawner<'a, ()> {
36    pub fn spawn<F>(&self, f: F) -> Result<(), SpawnError>
37        where F: UnsafeFutureObj<'a, ()> + Send {
38        let tx = self.tx.upgrade().ok_or(SpawnError::shutdown())?;
39        tx.push(LocalFutureObj::new(f)).expect("Queue full");
40        Ok(())
41    }
42}
43
44
45impl Spawn for Spawner<'static, ()> {
46    fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
47        let tx = self.tx.upgrade().ok_or(SpawnError::shutdown())?;
48        tx.push(future.into()).expect("Queue full");
49        Ok(())
50    }
51}
52
53
54impl<'a, Ret> LocalPool<'a, Ret> {
55    /// Create a new, empty pool of tasks.
56    pub fn new(cap: usize) -> Self {
57        Self {
58            pool: Arc::new(ArrayQueue::new(cap)),
59        }
60    }
61
62    pub fn spawner(&self) -> Spawner<'a, Ret> {
63        Spawner {
64            tx: Arc::downgrade(&self.pool),
65        }
66    }
67    pub fn spawn<F>(&mut self, f: F)
68        where F: UnsafeFutureObj<'a, Ret> {
69        self.pool.push(LocalFutureObj::new(f)).expect("Queue full");
70    }
71    /// Run all tasks in the pool to completion.
72    ///
73    /// ```rust
74    ///
75    /// use minimal_executor::LocalPool;
76    ///
77    /// let mut pool: LocalPool<'_, ()> = LocalPool::new();
78    ///
79    /// // ... spawn some initial tasks using `spawn.spawn()` or `spawn.spawn_local()`
80    ///
81    /// // run *all* tasks in the pool to completion, including any newly-spawned ones.
82    /// pool.run();
83    /// ```
84    ///
85    /// The function will block the calling thread until *all* tasks in the pool
86    /// are complete, including any spawned while running existing tasks.
87    pub fn run(&mut self) -> alloc::vec::Vec<Ret> {
88        let mut results = alloc::vec::Vec::new();
89        loop {
90            let ret = self.poll_once();
91
92            // no queued tasks; we may be done
93            match ret {
94                Poll::Pending => {}
95                Poll::Ready(None) => break,
96                Poll::Ready(Some(r)) => { results.push(r); }
97            }
98        }
99        results
100    }
101
102    /// Runs all tasks and returns after completing one future or until no more progress
103    /// can be made. Returns `true` if one future was completed, `false` otherwise.
104    ///
105    /// ```rust
106    ///
107    /// use futures::task::LocalSpawnExt;
108    /// use futures::future::{ready, pending};
109    /// use minimal_executor::LocalPool;
110    ///
111    /// let mut pool: LocalPool<'_, ()> = LocalPool::new();
112    /// pool.spawn(Box::pin(ready(())));
113    /// pool.spawn(Box::pin(ready(())));
114    /// pool.spawn(Box::pin(pending()));
115    ///
116    /// // Run the two ready tasks and return true for them.
117    /// pool.try_run_one(); // returns true after completing one of the ready futures
118    /// pool.try_run_one(); // returns true after completing the other ready future
119    ///
120    /// // the remaining task can not be completed
121    /// assert!(pool.try_run_one().is_pending()); // returns false
122    /// ```
123    ///
124    /// This function will not block the calling thread and will return the moment
125    /// that there are no tasks left for which progress can be made or after exactly one
126    /// task was completed; Remaining incomplete tasks in the pool can continue with
127    /// further use of one of the pool's run or poll methods.
128    /// Though only one task will be completed, progress may be made on multiple tasks.
129    pub fn try_run_one(&mut self) -> Poll<Ret> {
130        let ret = self.poll_though();
131        match ret {
132            Poll::Ready(Some(ret)) => {
133                Poll::Ready(ret)
134            }
135            Poll::Ready(None) => {
136                Poll::Pending
137            }
138            Poll::Pending => {
139                Poll::Pending
140            }
141        }
142    }
143
144    pub fn poll_though(&mut self) -> Poll<Option<Ret>> {
145        let len = self.pool.len();
146        if len == 0 {
147            return Poll::Ready(None);
148        }
149        poll_fn(|cx| {
150            for _ in 0..len {
151                if let Some(mut future) = self.pool.pop() {
152                    match future.poll_unpin(cx) {
153                        Poll::Pending => {
154                            self.pool.push(future).expect("Queue full");
155                        }
156                        Poll::Ready(ret) => {
157                            return Poll::Ready(Some(ret));
158                        }
159                    }
160                }
161            }
162            Poll::Pending
163        })
164    }
165    pub fn poll_once(&mut self) -> Poll<Option<Ret>> {
166        if let Some(mut future) = self.pool.pop() {
167            match poll_fn(|cx| future.poll_unpin(cx)) {
168                Poll::Pending => {
169                    self.pool.push(future).expect("Queue full");
170                    Poll::Pending
171                }
172                Poll::Ready(ret) => {
173                    Poll::Ready(Some(ret))
174                }
175            }
176        } else {
177            Poll::Ready(None)
178        }
179    }
180}
181