|
1 | 1 | use super::{Task, TaskId};
|
2 |
| -use alloc::{ |
3 |
| - collections::{BTreeMap, VecDeque}, |
4 |
| - sync::Arc, |
5 |
| - task::Wake, |
6 |
| -}; |
| 2 | +use alloc::{collections::BTreeMap, sync::Arc, task::Wake}; |
7 | 3 | use core::task::{Context, Poll, Waker};
|
8 | 4 | use crossbeam_queue::ArrayQueue;
|
9 | 5 |
|
10 | 6 | pub struct Executor {
|
11 |
| - task_queue: VecDeque<Task>, |
12 |
| - waiting_tasks: BTreeMap<TaskId, Task>, |
13 |
| - wake_queue: Arc<ArrayQueue<TaskId>>, |
| 7 | + tasks: BTreeMap<TaskId, Task>, |
| 8 | + task_queue: Arc<ArrayQueue<TaskId>>, |
14 | 9 | waker_cache: BTreeMap<TaskId, Waker>,
|
15 | 10 | }
|
16 | 11 |
|
17 | 12 | impl Executor {
|
18 | 13 | pub fn new() -> Self {
|
19 | 14 | Executor {
|
20 |
| - task_queue: VecDeque::new(), |
21 |
| - waiting_tasks: BTreeMap::new(), |
22 |
| - wake_queue: Arc::new(ArrayQueue::new(100)), |
| 15 | + tasks: BTreeMap::new(), |
| 16 | + task_queue: Arc::new(ArrayQueue::new(100)), |
23 | 17 | waker_cache: BTreeMap::new(),
|
24 | 18 | }
|
25 | 19 | }
|
26 | 20 |
|
27 | 21 | pub fn spawn(&mut self, task: Task) {
|
28 |
| - self.task_queue.push_back(task) |
| 22 | + let task_id = task.id; |
| 23 | + if self.tasks.insert(task.id, task).is_some() { |
| 24 | + panic!("task with same ID already in tasks"); |
| 25 | + } |
| 26 | + self.task_queue.push(task_id).expect("queue full"); |
29 | 27 | }
|
30 | 28 |
|
31 | 29 | pub fn run(&mut self) -> ! {
|
32 | 30 | loop {
|
33 |
| - self.wake_tasks(); |
34 | 31 | self.run_ready_tasks();
|
35 | 32 | self.sleep_if_idle();
|
36 | 33 | }
|
37 | 34 | }
|
38 | 35 |
|
39 | 36 | fn run_ready_tasks(&mut self) {
|
40 |
| - while let Some(mut task) = self.task_queue.pop_front() { |
41 |
| - let task_id = task.id; |
42 |
| - if !self.waker_cache.contains_key(&task_id) { |
43 |
| - self.waker_cache.insert(task_id, self.create_waker(task_id)); |
44 |
| - } |
45 |
| - let waker = self.waker_cache.get(&task_id).expect("should exist"); |
| 37 | + // destructure `self` to avoid borrow checker errors |
| 38 | + let Self { |
| 39 | + tasks, |
| 40 | + task_queue, |
| 41 | + waker_cache, |
| 42 | + } = self; |
| 43 | + |
| 44 | + while let Ok(task_id) = task_queue.pop() { |
| 45 | + let task = match tasks.get_mut(&task_id) { |
| 46 | + Some(task) => task, |
| 47 | + None => continue, // task no longer exists |
| 48 | + }; |
| 49 | + let waker = waker_cache |
| 50 | + .entry(task_id) |
| 51 | + .or_insert_with(|| TaskWaker::new(task_id, task_queue.clone())); |
46 | 52 | let mut context = Context::from_waker(waker);
|
47 | 53 | match task.poll(&mut context) {
|
48 | 54 | Poll::Ready(()) => {
|
49 |
| - // task done -> remove cached waker |
50 |
| - self.waker_cache.remove(&task_id); |
| 55 | + // task done -> remove it and its cached waker |
| 56 | + tasks.remove(&task_id); |
| 57 | + waker_cache.remove(&task_id); |
51 | 58 | }
|
52 |
| - Poll::Pending => { |
53 |
| - if self.waiting_tasks.insert(task_id, task).is_some() { |
54 |
| - panic!("task with same ID already in waiting_tasks"); |
55 |
| - } |
56 |
| - } |
57 |
| - } |
58 |
| - } |
59 |
| - } |
60 |
| - |
61 |
| - fn wake_tasks(&mut self) { |
62 |
| - while let Ok(task_id) = self.wake_queue.pop() { |
63 |
| - if let Some(task) = self.waiting_tasks.remove(&task_id) { |
64 |
| - self.task_queue.push_back(task); |
| 59 | + Poll::Pending => {} |
65 | 60 | }
|
66 | 61 | }
|
67 | 62 | }
|
68 | 63 |
|
69 | 64 | fn sleep_if_idle(&self) {
|
70 | 65 | use x86_64::instructions::interrupts::{self, enable_interrupts_and_hlt};
|
71 | 66 |
|
72 |
| - // fast path |
73 |
| - if !self.wake_queue.is_empty() { |
74 |
| - return; |
75 |
| - } |
76 |
| - |
77 | 67 | interrupts::disable();
|
78 |
| - if self.wake_queue.is_empty() { |
| 68 | + if self.task_queue.is_empty() { |
79 | 69 | enable_interrupts_and_hlt();
|
80 | 70 | } else {
|
81 | 71 | interrupts::enable();
|
82 | 72 | }
|
83 | 73 | }
|
84 |
| - |
85 |
| - fn create_waker(&self, task_id: TaskId) -> Waker { |
86 |
| - Waker::from(Arc::new(TaskWaker { |
87 |
| - task_id, |
88 |
| - wake_queue: self.wake_queue.clone(), |
89 |
| - })) |
90 |
| - } |
91 | 74 | }
|
92 | 75 |
|
93 | 76 | struct TaskWaker {
|
94 | 77 | task_id: TaskId,
|
95 |
| - wake_queue: Arc<ArrayQueue<TaskId>>, |
| 78 | + task_queue: Arc<ArrayQueue<TaskId>>, |
96 | 79 | }
|
97 | 80 |
|
98 | 81 | impl TaskWaker {
|
| 82 | + fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker { |
| 83 | + Waker::from(Arc::new(TaskWaker { |
| 84 | + task_id, |
| 85 | + task_queue, |
| 86 | + })) |
| 87 | + } |
| 88 | + |
99 | 89 | fn wake_task(&self) {
|
100 |
| - self.wake_queue.push(self.task_id).expect("wake_queue full"); |
| 90 | + self.task_queue.push(self.task_id).expect("task_queue full"); |
101 | 91 | }
|
102 | 92 | }
|
103 | 93 |
|
|
0 commit comments