os/task/
mod.rs

1//! Task management implementation
2//!
3//! Everything about task management, like starting and switching tasks is
4//! implemented here.
5//!
6//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
7//! all the tasks in the operating system.
8//!
9//! Be careful when you see `__switch` ASM function in `switch.S`. Control flow around this function
10//! might not be what you expect.
11
12mod context;
13mod switch;
14
15#[allow(clippy::module_inception)]
16mod task;
17
18use crate::config::MAX_APP_NUM;
19use crate::loader::{get_num_app, init_app_cx};
20use crate::sbi::shutdown;
21use crate::sync::UPSafeCell;
22use lazy_static::*;
23use switch::__switch;
24use task::{TaskControlBlock, TaskStatus};
25
26pub use context::TaskContext;
27
28/// The task manager, where all the tasks are managed.
29///
30/// Functions implemented on `TaskManager` deals with all task state transitions
31/// and task context switching. For convenience, you can find wrappers around it
32/// in the module level.
33///
34/// Most of `TaskManager` are hidden behind the field `inner`, to defer
35/// borrowing checks to runtime. You can see examples on how to use `inner` in
36/// existing functions on `TaskManager`.
37pub struct TaskManager {
38    /// total number of tasks
39    num_app: usize,
40    /// use inner value to get mutable access
41    inner: UPSafeCell<TaskManagerInner>,
42}
43
44/// Inner of Task Manager
45pub struct TaskManagerInner {
46    /// task list
47    tasks: [TaskControlBlock; MAX_APP_NUM],
48    /// id of current `Running` task
49    current_task: usize,
50}
51
52lazy_static! {
53    /// Global variable: TASK_MANAGER
54    pub static ref TASK_MANAGER: TaskManager = {
55        let num_app = get_num_app();
56        let mut tasks = [TaskControlBlock {
57            task_cx: TaskContext::zero_init(),
58            task_status: TaskStatus::UnInit,
59        }; MAX_APP_NUM];
60        for (i, task) in tasks.iter_mut().enumerate() {
61            task.task_cx = TaskContext::goto_restore(init_app_cx(i));
62            task.task_status = TaskStatus::Ready;
63        }
64        TaskManager {
65            num_app,
66            inner: unsafe {
67                UPSafeCell::new(TaskManagerInner {
68                    tasks,
69                    current_task: 0,
70                })
71            },
72        }
73    };
74}
75
76impl TaskManager {
77    /// Run the first task in task list.
78    ///
79    /// Generally, the first task in task list is an idle task (we call it zero process later).
80    /// But in ch3, we load apps statically, so the first task is a real app.
81    fn run_first_task(&self) -> ! {
82        let mut inner = self.inner.exclusive_access();
83        let task0 = &mut inner.tasks[0];
84        task0.task_status = TaskStatus::Running;
85        let next_task_cx_ptr = &task0.task_cx as *const TaskContext;
86        drop(inner);
87        let mut _unused = TaskContext::zero_init();
88        // before this, we should drop local variables that must be dropped manually
89        unsafe {
90            __switch(&mut _unused as *mut TaskContext, next_task_cx_ptr);
91        }
92        panic!("unreachable in run_first_task!");
93    }
94
95    /// Change the status of current `Running` task into `Ready`.
96    fn mark_current_suspended(&self) {
97        let mut inner = self.inner.exclusive_access();
98        let current = inner.current_task;
99        inner.tasks[current].task_status = TaskStatus::Ready;
100    }
101
102    /// Change the status of current `Running` task into `Exited`.
103    fn mark_current_exited(&self) {
104        let mut inner = self.inner.exclusive_access();
105        let current = inner.current_task;
106        inner.tasks[current].task_status = TaskStatus::Exited;
107    }
108
109    /// Find next task to run and return task id.
110    ///
111    /// In this case, we only return the first `Ready` task in task list.
112    fn find_next_task(&self) -> Option<usize> {
113        let inner = self.inner.exclusive_access();
114        let current = inner.current_task;
115        (current + 1..current + self.num_app + 1)
116            .map(|id| id % self.num_app)
117            .find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
118    }
119
120    /// Switch current `Running` task to the task we have found,
121    /// or there is no `Ready` task and we can exit with all applications completed
122    fn run_next_task(&self) {
123        if let Some(next) = self.find_next_task() {
124            let mut inner = self.inner.exclusive_access();
125            let current = inner.current_task;
126            inner.tasks[next].task_status = TaskStatus::Running;
127            inner.current_task = next;
128            let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
129            let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
130            drop(inner);
131            // before this, we should drop local variables that must be dropped manually
132            unsafe {
133                __switch(current_task_cx_ptr, next_task_cx_ptr);
134            }
135            // go back to user mode
136        } else {
137            println!("All applications completed!");
138            shutdown(false);
139        }
140    }
141}
142
143/// run first task
144pub fn run_first_task() {
145    TASK_MANAGER.run_first_task();
146}
147
148/// rust next task
149fn run_next_task() {
150    TASK_MANAGER.run_next_task();
151}
152
153/// suspend current task
154fn mark_current_suspended() {
155    TASK_MANAGER.mark_current_suspended();
156}
157
158/// exit current task
159fn mark_current_exited() {
160    TASK_MANAGER.mark_current_exited();
161}
162
163/// suspend current task, then run next task
164pub fn suspend_current_and_run_next() {
165    mark_current_suspended();
166    run_next_task();
167}
168
169/// exit current task,  then run next task
170pub fn exit_current_and_run_next() {
171    mark_current_exited();
172    run_next_task();
173}