    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171

    //! Task management implementation
    //!
    //! Everything about task management, like starting and switching tasks is
    //! implemented here.
    //!
    //! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
    //! all the tasks in the operating system.
    //!
    //! Be careful when you see `__switch` ASM function in `switch.S`. Control flow around this function
    //! might not be what you expect.

    mod context;
    mod switch;

    #[allow(clippy::module_inception)]
    mod task;

    use crate::config::MAX_APP_NUM;
    use crate::loader::{get_num_app, init_app_cx};
    use crate::sync::UPSafeCell;
    use lazy_static::*;
    use switch::__switch;
    use task::{TaskControlBlock, TaskStatus};

    pub use context::TaskContext;

    /// The task manager, where all the tasks are managed.
    ///
    /// Functions implemented on `TaskManager` deals with all task state transitions
    /// and task context switching. For convenience, you can find wrappers around it
    /// in the module level.
    ///
    /// Most of `TaskManager` are hidden behind the field `inner`, to defer
    /// borrowing checks to runtime. You can see examples on how to use `inner` in
    /// existing functions on `TaskManager`.
    pub struct TaskManager {
        /// total number of tasks
        num_app: usize,
        /// use inner value to get mutable access
        inner: UPSafeCell<TaskManagerInner>,
    }

    /// Inner of Task Manager
    pub struct TaskManagerInner {
        /// task list
        tasks: [TaskControlBlock; MAX_APP_NUM],
        /// id of current `Running` task
        current_task: usize,
    }

    lazy_static! {
        /// Global variable: TASK_MANAGER
        pub static ref TASK_MANAGER: TaskManager = {
            let num_app = get_num_app();
            let mut tasks = [TaskControlBlock {
                task_cx: TaskContext::zero_init(),
                task_status: TaskStatus::UnInit,
            }; MAX_APP_NUM];
            for (i, task) in tasks.iter_mut().enumerate() {
                task.task_cx = TaskContext::goto_restore(init_app_cx(i));
                task.task_status = TaskStatus::Ready;
            }
            TaskManager {
                num_app,
                inner: unsafe {
                    UPSafeCell::new(TaskManagerInner {
                        tasks,
                        current_task: 0,
                    })
                },
            }
        };
    }

    impl TaskManager {
        /// Run the first task in task list.
        ///
        /// Generally, the first task in task list is an idle task (we call it zero process later).
        /// But in ch3, we load apps statically, so the first task is a real app.
        fn run_first_task(&self) -> ! {
            let mut inner = self.inner.exclusive_access();
            let task0 = &mut inner.tasks[0];
            task0.task_status = TaskStatus::Running;
            let next_task_cx_ptr = &task0.task_cx as *const TaskContext;
            drop(inner);
            let mut _unused = TaskContext::zero_init();
            // before this, we should drop local variables that must be dropped manually
            unsafe {
                __switch(&mut _unused as *mut TaskContext, next_task_cx_ptr);
            }
            panic!("unreachable in run_first_task!");
        }

        /// Change the status of current `Running` task into `Ready`.
        fn mark_current_suspended(&self) {
            let mut inner = self.inner.exclusive_access();
            let current = inner.current_task;
            inner.tasks[current].task_status = TaskStatus::Ready;
        }

        /// Change the status of current `Running` task into `Exited`.
        fn mark_current_exited(&self) {
            let mut inner = self.inner.exclusive_access();
            let current = inner.current_task;
            inner.tasks[current].task_status = TaskStatus::Exited;
        }

        /// Find next task to run and return task id.
        ///
        /// In this case, we only return the first `Ready` task in task list.
        fn find_next_task(&self) -> Option<usize> {
            let inner = self.inner.exclusive_access();
            let current = inner.current_task;
            (current + 1..current + self.num_app + 1)
                .map(|id| id % self.num_app)
                .find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
        }

        /// Switch current `Running` task to the task we have found,
        /// or there is no `Ready` task and we can exit with all applications completed
        fn run_next_task(&self) {
            if let Some(next) = self.find_next_task() {
                let mut inner = self.inner.exclusive_access();
                let current = inner.current_task;
                inner.tasks[next].task_status = TaskStatus::Running;
                inner.current_task = next;
                let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
                let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
                drop(inner);
                // before this, we should drop local variables that must be dropped manually
                unsafe {
                    __switch(current_task_cx_ptr, next_task_cx_ptr);
                }
                // go back to user mode
            } else {
                panic!("All applications completed!");
            }
        }
    }

    /// run first task
    pub fn run_first_task() {
        TASK_MANAGER.run_first_task();
    }

    /// rust next task
    fn run_next_task() {
        TASK_MANAGER.run_next_task();
    }

    /// suspend current task
    fn mark_current_suspended() {
        TASK_MANAGER.mark_current_suspended();
    }

    /// exit current task
    fn mark_current_exited() {
        TASK_MANAGER.mark_current_exited();
    }

    /// suspend current task, then run next task
    pub fn suspend_current_and_run_next() {
        mark_current_suspended();
        run_next_task();
    }

    /// exit current task,  then run next task
    pub fn exit_current_and_run_next() {
        mark_current_exited();
        run_next_task();
    }