Skip to main content

edge_executor/
lib.rs

1#![no_std]
2
3use core::future::{poll_fn, Future};
4use core::marker::PhantomData;
5use core::task::{Context, Poll};
6
7use alloc::rc::Rc;
8
9use async_task::Runnable;
10
11pub use async_task::{FallibleTask, Task};
12
13use atomic_waker::AtomicWaker;
14use futures_lite::FutureExt;
15
16#[cfg(not(feature = "portable-atomic"))]
17use alloc::sync::Arc;
18#[cfg(feature = "portable-atomic")]
19use portable_atomic_util::Arc;
20
21use once_cell::sync::OnceCell;
22
23#[cfg(feature = "std")]
24pub use futures_lite::future::block_on;
25
26pub use queue::*;
27
28extern crate alloc;
29
30mod queue;
31
32/// An async executor.
33///
34/// # Examples
35///
36/// A multi-threaded executor:
37///
38/// ```ignore
39/// use async_channel::unbounded;
40/// use easy_parallel::Parallel;
41///
42/// use edge_executor::{Executor, block_on};
43///
44/// let ex: Executor = Default::default();
45/// let (signal, shutdown) = unbounded::<()>();
46///
47/// Parallel::new()
48///     // Run four executor threads.
49///     .each(0..4, |_| block_on(ex.run(shutdown.recv())))
50///     // Run the main future on the current thread.
51///     .finish(|| block_on(async {
52///         println!("Hello world!");
53///         drop(signal);
54///     }));
55/// ```
56pub struct Executor<'a, Q = BoundQueue> {
57    state: OnceCell<Arc<State<Q>>>,
58    queue_ctor: fn() -> Q,
59    _marker: PhantomData<core::cell::UnsafeCell<&'a ()>>,
60}
61
62impl<'a, Q: ExecutorQueue> Executor<'a, Q> {
63    /// Creates a new executor.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use edge_executor::Executor;
69    ///
70    /// let ex: Executor = Default::default();
71    /// ```
72    pub const fn new() -> Self {
73        Self::new_with(Q::new)
74    }
75
76    /// Creates a new executor with the provided queue constructor.
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use edge_executor::{Executor, UnboundQueue};
82    ///
83    /// let ex = Executor::new_with(|| { let queue: UnboundQueue = UnboundQueue::with_capacity(100); queue });
84    /// ```
85    pub const fn new_with(queue_ctor: fn() -> Q) -> Self {
86        Self {
87            state: OnceCell::new(),
88            queue_ctor,
89            _marker: PhantomData,
90        }
91    }
92
93    /// Spawns a task onto the executor.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use edge_executor::Executor;
99    ///
100    /// let ex: Executor = Default::default();
101    ///
102    /// let task = ex.spawn(async {
103    ///     println!("Hello world");
104    /// });
105    /// ```
106    ///
107    /// Note that if the executor's queue size is equal to the number of currently
108    /// spawned and running tasks, spawning this additional task might cause the executor to panic
109    /// later, when the task is scheduled for polling.
110    pub fn spawn<F>(&self, fut: F) -> Task<F::Output>
111    where
112        F: Future + Send + 'a,
113        F::Output: Send + 'a,
114    {
115        unsafe { self.spawn_unchecked(fut) }
116    }
117
118    /// Attempts to run a task if at least one is scheduled.
119    ///
120    /// Running a scheduled task means simply polling its future once.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use edge_executor::Executor;
126    ///
127    /// let ex: Executor = Default::default();
128    /// assert!(!ex.try_tick()); // no tasks to run
129    ///
130    /// let task = ex.spawn(async {
131    ///     println!("Hello world");
132    /// });
133    /// assert!(ex.try_tick()); // a task was found
134    /// ```    
135    pub fn try_tick(&self) -> bool {
136        if let Some(runnable) = self.try_runnable() {
137            runnable.run();
138
139            true
140        } else {
141            false
142        }
143    }
144
145    /// Runs a single task asynchronously.
146    ///
147    /// Running a task means simply polling its future once.
148    ///
149    /// If no tasks are scheduled when this method is called, it will wait until one is scheduled.
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// use edge_executor::{Executor, block_on};
155    ///
156    /// let ex: Executor = Default::default();
157    ///
158    /// let task = ex.spawn(async {
159    ///     println!("Hello world");
160    /// });
161    /// block_on(ex.tick()); // runs the task
162    /// ```
163    pub async fn tick(&self) {
164        self.runnable().await.run();
165    }
166
167    /// Runs the executor asynchronously until the given future completes.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use edge_executor::{Executor, block_on};
173    ///
174    /// let ex: Executor = Default::default();
175    ///
176    /// let task = ex.spawn(async { 1 + 2 });
177    /// let res = block_on(ex.run(async { task.await * 2 }));
178    ///
179    /// assert_eq!(res, 6);
180    /// ```
181    pub async fn run<F>(&self, fut: F) -> F::Output
182    where
183        F: Future,
184    {
185        unsafe { self.run_unchecked(fut).await }
186    }
187
188    /// Waits for the next runnable task to run.
189    async fn runnable(&self) -> Runnable {
190        poll_fn(|ctx| self.poll_runnable(ctx)).await
191    }
192
193    /// Polls the first task scheduled for execution by the executor.
194    fn poll_runnable(&self, ctx: &Context<'_>) -> Poll<Runnable> {
195        self.state().waker.register(ctx.waker());
196
197        if let Some(runnable) = self.try_runnable() {
198            Poll::Ready(runnable)
199        } else {
200            Poll::Pending
201        }
202    }
203
204    /// Pops the first task scheduled for execution by the executor.
205    ///
206    /// Returns
207    /// - `None` - if no task was scheduled for execution
208    /// - `Some(Runnnable)` - the first task scheduled for execution. Calling `Runnable::run` will
209    ///   execute the task. In other words, it will poll its future.
210    fn try_runnable(&self) -> Option<Runnable> {
211        self.state().queue.pop()
212    }
213
214    unsafe fn spawn_unchecked<F>(&self, fut: F) -> Task<F::Output>
215    where
216        F: Future,
217    {
218        let schedule = {
219            let state = self.state().clone();
220
221            move |runnable| {
222                state.queue.push(runnable);
223
224                if let Some(waker) = state.waker.take() {
225                    waker.wake();
226                }
227            }
228        };
229
230        let (runnable, task) = unsafe { async_task::spawn_unchecked(fut, schedule) };
231
232        runnable.schedule();
233
234        task
235    }
236
237    async unsafe fn run_unchecked<F>(&self, fut: F) -> F::Output
238    where
239        F: Future,
240    {
241        let run_forever = async {
242            loop {
243                self.tick().await;
244            }
245        };
246
247        run_forever.or(fut).await
248    }
249
250    /// Returns a reference to the inner state.
251    fn state(&self) -> &Arc<State<Q>> {
252        self.state
253            .get_or_init(|| Arc::new(State::new((self.queue_ctor)())))
254    }
255}
256
257impl<Q: ExecutorQueue> Default for Executor<'_, Q> {
258    fn default() -> Self {
259        Self::new()
260    }
261}
262
263unsafe impl<Q: Send> Send for Executor<'_, Q> {}
264unsafe impl<Q: Sync> Sync for Executor<'_, Q> {}
265
266/// A thread-local executor.
267///
268/// The executor can only be run on the thread that created it.
269///
270/// # Examples
271///
272/// ```
273/// use edge_executor::{LocalExecutor, block_on};
274///
275/// let local_ex: LocalExecutor = Default::default();
276///
277/// block_on(local_ex.run(async {
278///     println!("Hello world!");
279/// }));
280/// ```
281pub struct LocalExecutor<'a, Q = BoundQueue> {
282    executor: Executor<'a, Q>,
283    _marker: PhantomData<Rc<()>>,
284}
285
286impl<'a, Q: ExecutorQueue> LocalExecutor<'a, Q> {
287    /// Creates a single-threaded executor.
288    ///
289    /// # Examples
290    ///
291    /// ```
292    /// use edge_executor::LocalExecutor;
293    ///
294    /// let local_ex: LocalExecutor = Default::default();
295    /// ```
296    pub const fn new() -> Self {
297        Self::new_with(Q::new)
298    }
299
300    /// Creates a single-threaded executor with the provided queue constructor.
301    ///
302    /// # Examples
303    ///
304    /// ```
305    /// use edge_executor::{LocalExecutor, UnboundQueue};
306    ///
307    /// let local_ex = LocalExecutor::new_with(|| { let queue: UnboundQueue = UnboundQueue::with_capacity(100); queue });
308    /// ```
309    pub const fn new_with(queue_ctor: fn() -> Q) -> Self {
310        Self {
311            executor: Executor::<Q>::new_with(queue_ctor),
312            _marker: PhantomData,
313        }
314    }
315
316    /// Spawns a task onto the executor.
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// use edge_executor::LocalExecutor;
322    ///
323    /// let local_ex: LocalExecutor = Default::default();
324    ///
325    /// let task = local_ex.spawn(async {
326    ///     println!("Hello world");
327    /// });
328    /// ```
329    ///
330    /// Note that if the executor's queue size is equal to the number of currently
331    /// spawned and running tasks, spawning this additional task might cause the executor to panic
332    /// later, when the task is scheduled for polling.
333    pub fn spawn<F>(&self, fut: F) -> Task<F::Output>
334    where
335        F: Future + 'a,
336        F::Output: 'a,
337    {
338        unsafe { self.executor.spawn_unchecked(fut) }
339    }
340
341    /// Attempts to run a task if at least one is scheduled.
342    ///
343    /// Running a scheduled task means simply polling its future once.
344    ///
345    /// # Examples
346    ///
347    /// ```
348    /// use edge_executor::LocalExecutor;
349    ///
350    /// let local_ex: LocalExecutor = Default::default();
351    /// assert!(!local_ex.try_tick()); // no tasks to run
352    ///
353    /// let task = local_ex.spawn(async {
354    ///     println!("Hello world");
355    /// });
356    /// assert!(local_ex.try_tick()); // a task was found
357    /// ```    
358    pub fn try_tick(&self) -> bool {
359        self.executor.try_tick()
360    }
361
362    /// Runs a single task asynchronously.
363    ///
364    /// Running a task means simply polling its future once.
365    ///
366    /// If no tasks are scheduled when this method is called, it will wait until one is scheduled.
367    ///
368    /// # Examples
369    ///
370    /// ```
371    /// use edge_executor::{LocalExecutor, block_on};
372    ///
373    /// let local_ex: LocalExecutor = Default::default();
374    ///
375    /// let task = local_ex.spawn(async {
376    ///     println!("Hello world");
377    /// });
378    /// block_on(local_ex.tick()); // runs the task
379    /// ```
380    pub async fn tick(&self) {
381        self.executor.tick().await
382    }
383
384    /// Runs the executor asynchronously until the given future completes.
385    ///
386    /// # Examples
387    ///
388    /// ```
389    /// use edge_executor::{LocalExecutor, block_on};
390    ///
391    /// let local_ex: LocalExecutor = Default::default();
392    ///
393    /// let task = local_ex.spawn(async { 1 + 2 });
394    /// let res = block_on(local_ex.run(async { task.await * 2 }));
395    ///
396    /// assert_eq!(res, 6);
397    /// ```
398    pub async fn run<F>(&self, fut: F) -> F::Output
399    where
400        F: Future,
401    {
402        unsafe { self.executor.run_unchecked(fut) }.await
403    }
404}
405
406impl<'a, Q: ExecutorQueue> Default for LocalExecutor<'a, Q> {
407    fn default() -> Self {
408        Self::new()
409    }
410}
411
412struct State<Q> {
413    queue: Q,
414    waker: AtomicWaker,
415}
416
417impl<Q> State<Q> {
418    const fn new(queue: Q) -> Self {
419        Self {
420            queue,
421            waker: AtomicWaker::new(),
422        }
423    }
424}