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
//! The `process` module provides system calls to interact with processes.

use alloc::vec::Vec;

use crate::{
    executor::ControlFlow,
    mem::UserPtr,
    sync::{wait_for_event, Event},
    syscall::SystemCall,
    task::Status,
};

impl SystemCall<'_> {
    /// Terminates the current thread with the given exit code.
    pub fn sys_exit(&self, exit_code: usize) -> (isize, ControlFlow) {
        (0, ControlFlow::Exit(exit_code))
    }

    /// Yields the CPU to another thread.
    pub fn sys_sched_yield(&self) -> (isize, ControlFlow) {
        (0, ControlFlow::Yield)
    }

    /// Forks the current process and create a new child process.
    pub fn sys_fork(&self) -> (isize, ControlFlow) {
        let process = self.thread.process().fork();
        (process.pid() as isize, ControlFlow::Continue)
    }

    /// Waits for a child process with the given process to terminate, and return the PID and exit.
    pub async fn sys_waitpid(
        &self,
        pid: isize,
        mut wait_status: UserPtr<usize>,
    ) -> (isize, ControlFlow) {
        loop {
            let process = self.thread.process();
            let mut process_state = process.state().lock();
            let child_list = process_state.child_list_mut();

            if let Some((pid, exit_code)) = match pid {
                -1 | 0 => child_list.iter().find_map(|child_process| {
                    let child_process_state = child_process.state().lock();
                    if child_process_state.status() == Status::Zombie {
                        Some((child_process.pid(), child_process_state.exit_code()))
                    } else {
                        None
                    }
                }),
                pid => child_list.iter().find_map(|child_process| {
                    let child_process_state = child_process.state().lock();
                    if child_process.pid() == pid as usize
                        && child_process_state.status() == Status::Zombie
                    {
                        Some((child_process.pid(), child_process_state.exit_code()))
                    } else {
                        None
                    }
                }),
            } {
                child_list.retain(|child_process| child_process.pid() != pid);
                *wait_status = exit_code;
                return (pid as isize, ControlFlow::Continue);
            } else {
                let event_bus = process.event_bus();
                drop(process_state);
                wait_for_event(event_bus.clone(), Event::CHILD_PROCESS_QUIT).await;
                event_bus.lock().clear(Event::CHILD_PROCESS_QUIT);
            }
        }
    }

    /// Replaces the current process with a new process loaded from the executable file with a given
    /// name.
    pub fn sys_exec(&self, path: UserPtr<u8>) -> (isize, ControlFlow) {
        self.thread.process().exec(&path.as_string(), Vec::new());
        (0, ControlFlow::Continue)
    }
}