    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

    //! 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 whole operating system.
    //!
    //! A single global instance of [`Processor`] called `PROCESSOR` monitors running
    //! task(s) for each core.
    //!
    //! A single global instance of [`PidAllocator`] called `PID_ALLOCATOR` allocates
    //! pid for user apps.
    //!
    //! 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 manager;
    mod pid;
    mod processor;
    mod switch;
    #[allow(clippy::module_inception)]

    mod task;

    use crate::loader::get_app_data_by_name;
    use alloc::sync::Arc;
    use lazy_static::*;
    pub use manager::{fetch_task,TaskManager};
    use switch::__switch;
    use task::{TaskControlBlock, TaskStatus};

    pub use context::TaskContext;
    pub use manager::add_task;
    pub use pid::{pid_alloc, KernelStack, PidHandle,PidAllocator};
    pub use processor::{
        current_task, current_trap_cx, current_user_token, run_tasks, schedule, take_current_task,Processor
    };
    /// Suspend the current 'Running' task and run the next task in task list.
    pub fn suspend_current_and_run_next() {
        // There must be an application running.
        let task = take_current_task().unwrap();

        // ---- access current TCB exclusively
        let mut task_inner = task.inner_exclusive_access();
        let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
        // Change status to Ready
        task_inner.task_status = TaskStatus::Ready;
        drop(task_inner);
        // ---- release current PCB

        // push back to ready queue.
        add_task(task);
        // jump to scheduling cycle
        schedule(task_cx_ptr);
    }
    /// Exit the current 'Running' task and run the next task in task list.
    pub fn exit_current_and_run_next(exit_code: i32) {
        // take from Processor
        let task = take_current_task().unwrap();
        // **** access current TCB exclusively
        let mut inner = task.inner_exclusive_access();
        // Change status to Zombie
        inner.task_status = TaskStatus::Zombie;
        // Record exit code
        inner.exit_code = exit_code;
        // do not move to its parent but under initproc

        // ++++++ access initproc TCB exclusively
        {
            let mut initproc_inner = INITPROC.inner_exclusive_access();
            for child in inner.children.iter() {
                child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
                initproc_inner.children.push(child.clone());
            }
        }
        // ++++++ release parent PCB

        inner.children.clear();
        // deallocate user space
        inner.memory_set.recycle_data_pages();
        drop(inner);
        // **** release current PCB
        // drop task manually to maintain rc correctly
        drop(task);
        // we do not have to save task context
        let mut _unused = TaskContext::zero_init();
        schedule(&mut _unused as *mut _);
    }

    lazy_static! {
        ///Globle process that init user shell
        pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new(TaskControlBlock::new(
            get_app_data_by_name("initproc").unwrap()
        ));
    }
    ///Add init process to the manager
    pub fn add_initproc() {
        add_task(INITPROC.clone());
    }