Skip to main content

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#[allow(clippy::module_inception)]
15mod task;
16
17use crate::loader::{get_app_data, get_num_app};
18use crate::sbi::shutdown;
19use crate::sync::UPSafeCell;
20use crate::trap::TrapContext;
21use alloc::vec::Vec;
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/// The task manager inner in 'UPSafeCell'
45struct TaskManagerInner {
46    /// task list
47    tasks: Vec<TaskControlBlock>,
48    /// id of current `Running` task
49    current_task: usize,
50}
51
52lazy_static! {
53    /// a `TaskManager` global instance through lazy_static!
54    pub static ref TASK_MANAGER: TaskManager = {
55        println!("init TASK_MANAGER");
56        let num_app = get_num_app();
57        println!("num_app = {}", num_app);
58        let mut tasks: Vec<TaskControlBlock> = Vec::new();
59        for i in 0..num_app {
60            tasks.push(TaskControlBlock::new(get_app_data(i), i));
61        }
62        TaskManager {
63            num_app,
64            inner: unsafe {
65                UPSafeCell::new(TaskManagerInner {
66                    tasks,
67                    current_task: 0,
68                })
69            },
70        }
71    };
72}
73
74impl TaskManager {
75    /// Run the first task in task list.
76    ///
77    /// Generally, the first task in task list is an idle task (we call it zero process later).
78    /// But in ch4, we load apps statically, so the first task is a real app.
79    fn run_first_task(&self) -> ! {
80        let mut inner = self.inner.exclusive_access();
81        let next_task = &mut inner.tasks[0];
82        next_task.task_status = TaskStatus::Running;
83        let next_task_cx_ptr = &next_task.task_cx as *const TaskContext;
84        drop(inner);
85        let mut _unused = TaskContext::zero_init();
86        // before this, we should drop local variables that must be dropped manually
87        unsafe {
88            __switch(&mut _unused as *mut _, next_task_cx_ptr);
89        }
90        panic!("unreachable in run_first_task!");
91    }
92
93    /// Change the status of current `Running` task into `Ready`.
94    fn mark_current_suspended(&self) {
95        let mut inner = self.inner.exclusive_access();
96        let cur = inner.current_task;
97        inner.tasks[cur].task_status = TaskStatus::Ready;
98    }
99
100    /// Change the status of current `Running` task into `Exited`.
101    fn mark_current_exited(&self) {
102        let mut inner = self.inner.exclusive_access();
103        let cur = inner.current_task;
104        inner.tasks[cur].task_status = TaskStatus::Exited;
105    }
106
107    /// Find next task to run and return task id.
108    ///
109    /// In this case, we only return the first `Ready` task in task list.
110    fn find_next_task(&self) -> Option<usize> {
111        let inner = self.inner.exclusive_access();
112        let current = inner.current_task;
113        (current + 1..current + self.num_app + 1)
114            .map(|id| id % self.num_app)
115            .find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
116    }
117
118    /// Get the current 'Running' task's token.
119    fn get_current_token(&self) -> usize {
120        let inner = self.inner.exclusive_access();
121        inner.tasks[inner.current_task].get_user_token()
122    }
123
124    /// Get the current 'Running' task's trap contexts.
125    fn get_current_trap_cx(&self) -> &'static mut TrapContext {
126        let inner = self.inner.exclusive_access();
127        inner.tasks[inner.current_task].get_trap_cx()
128    }
129
130    /// Change the current 'Running' task's program break
131    pub fn change_current_program_brk(&self, size: i32) -> Option<usize> {
132        let mut inner = self.inner.exclusive_access();
133        let cur = inner.current_task;
134        inner.tasks[cur].change_program_brk(size)
135    }
136
137    /// Switch current `Running` task to the task we have found,
138    /// or there is no `Ready` task and we can exit with all applications completed
139    fn run_next_task(&self) {
140        if let Some(next) = self.find_next_task() {
141            let mut inner = self.inner.exclusive_access();
142            let current = inner.current_task;
143            inner.tasks[next].task_status = TaskStatus::Running;
144            inner.current_task = next;
145            let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
146            let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
147            drop(inner);
148            // before this, we should drop local variables that must be dropped manually
149            unsafe {
150                __switch(current_task_cx_ptr, next_task_cx_ptr);
151            }
152            // go back to user mode
153        } else {
154            println!("All applications completed!");
155            shutdown(false);
156        }
157    }
158}
159
160/// Run the first task in task list.
161pub fn run_first_task() {
162    TASK_MANAGER.run_first_task();
163}
164
165/// Switch current `Running` task to the task we have found,
166/// or there is no `Ready` task and we can exit with all applications completed
167fn run_next_task() {
168    TASK_MANAGER.run_next_task();
169}
170
171/// Change the status of current `Running` task into `Ready`.
172fn mark_current_suspended() {
173    TASK_MANAGER.mark_current_suspended();
174}
175
176/// Change the status of current `Running` task into `Exited`.
177fn mark_current_exited() {
178    TASK_MANAGER.mark_current_exited();
179}
180
181/// Suspend the current 'Running' task and run the next task in task list.
182pub fn suspend_current_and_run_next() {
183    mark_current_suspended();
184    run_next_task();
185}
186
187/// Exit the current 'Running' task and run the next task in task list.
188pub fn exit_current_and_run_next() {
189    mark_current_exited();
190    run_next_task();
191}
192
193/// Get the current 'Running' task's token.
194pub fn current_user_token() -> usize {
195    TASK_MANAGER.get_current_token()
196}
197
198/// Get the current 'Running' task's trap contexts.
199pub fn current_trap_cx() -> &'static mut TrapContext {
200    TASK_MANAGER.get_current_trap_cx()
201}
202
203/// Change the current 'Running' task's program break
204pub fn change_program_brk(size: i32) -> Option<usize> {
205    TASK_MANAGER.change_current_program_brk(size)
206}