1 //! An executor with task priorities.
2 
3 use std::future::Future;
4 use std::thread;
5 
6 use async_executor::{Executor, Task};
7 use futures_lite::{future, prelude::*};
8 
9 /// Task priority.
10 #[repr(usize)]
11 #[derive(Debug, Clone, Copy)]
12 enum Priority {
13     High = 0,
14     Medium = 1,
15     Low = 2,
16 }
17 
18 /// An executor with task priorities.
19 ///
20 /// Tasks with lower priorities only get polled when there are no tasks with higher priorities.
21 struct PriorityExecutor<'a> {
22     ex: [Executor<'a>; 3],
23 }
24 
25 impl<'a> PriorityExecutor<'a> {
26     /// Creates a new executor.
new() -> PriorityExecutor<'a>27     const fn new() -> PriorityExecutor<'a> {
28         PriorityExecutor {
29             ex: [Executor::new(), Executor::new(), Executor::new()],
30         }
31     }
32 
33     /// Spawns a task with the given priority.
spawn<T: Send + 'a>( &self, priority: Priority, future: impl Future<Output = T> + Send + 'a, ) -> Task<T>34     fn spawn<T: Send + 'a>(
35         &self,
36         priority: Priority,
37         future: impl Future<Output = T> + Send + 'a,
38     ) -> Task<T> {
39         self.ex[priority as usize].spawn(future)
40     }
41 
42     /// Runs the executor forever.
run(&self)43     async fn run(&self) {
44         loop {
45             for _ in 0..200 {
46                 let t0 = self.ex[0].tick();
47                 let t1 = self.ex[1].tick();
48                 let t2 = self.ex[2].tick();
49 
50                 // Wait until one of the ticks completes, trying them in order from highest
51                 // priority to lowest priority.
52                 t0.or(t1).or(t2).await;
53             }
54 
55             // Yield every now and then.
56             future::yield_now().await;
57         }
58     }
59 }
60 
main()61 fn main() {
62     static EX: PriorityExecutor<'_> = PriorityExecutor::new();
63 
64     // Spawn a thread running the executor forever.
65     thread::spawn(|| future::block_on(EX.run()));
66 
67     let mut tasks = Vec::new();
68 
69     for _ in 0..20 {
70         // Choose a random priority.
71         let choice = [Priority::High, Priority::Medium, Priority::Low];
72         let priority = choice[fastrand::usize(..choice.len())];
73 
74         // Spawn a task with this priority.
75         tasks.push(EX.spawn(priority, async move {
76             println!("{:?}", priority);
77             future::yield_now().await;
78             println!("{:?}", priority);
79         }));
80     }
81 
82     for task in tasks {
83         future::block_on(task);
84     }
85 }
86