Skip to main content

ahrightos_process/
pcb.rs

1use crate::{ProcessContext, ProcessId};
2use alloc::vec::Vec;
3use core::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ProcessState {
7    Ready,
8    Running,
9    Blocked,
10    Zombie,
11}
12
13impl ProcessState {
14    pub fn can_schedule(self) -> bool {
15        self == Self::Ready
16    }
17
18    pub fn can_transition_to(self, new_state: Self) -> bool {
19        match (self, new_state) {
20            (Self::Ready, Self::Running)
21            | (Self::Running, Self::Ready)
22            | (Self::Running, Self::Blocked)
23            | (Self::Running, Self::Zombie)
24            | (Self::Blocked, Self::Ready) => true,
25            (old, new) if old == new => true,
26            _ => false,
27        }
28    }
29}
30
31impl fmt::Display for ProcessState {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        let state = match self {
34            Self::Ready => "Ready",
35            Self::Running => "Running",
36            Self::Blocked => "Blocked",
37            Self::Zombie => "Zombie",
38        };
39        write!(f, "{}", state)
40    }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct ProcessStateError {
45    pub from: ProcessState,
46    pub to: ProcessState,
47}
48
49impl fmt::Display for ProcessStateError {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "invalid process transition {} -> {}", self.from, self.to)
52    }
53}
54
55#[derive(Clone)]
56pub struct ProcessControlBlock {
57    pid: ProcessId,
58    name: &'static str,
59    state: ProcessState,
60    exit_code: Option<i32>,
61    parent_pid: Option<ProcessId>,
62    children: Vec<ProcessId>,
63    context: ProcessContext,
64    time_slice: usize,
65    priority: usize,
66}
67
68impl ProcessControlBlock {
69    pub fn new(pid: ProcessId, name: &'static str, parent_pid: Option<ProcessId>) -> Self {
70        Self {
71            pid,
72            name,
73            state: ProcessState::Ready,
74            exit_code: None,
75            parent_pid,
76            children: Vec::new(),
77            context: ProcessContext::new(),
78            time_slice: 5,
79            priority: 1,
80        }
81    }
82
83    pub fn add_child(&mut self, pid: ProcessId) {
84        if !self.children.contains(&pid) {
85            self.children.push(pid);
86        }
87    }
88
89    pub fn remove_child(&mut self, pid: ProcessId) {
90        self.children.retain(|&p| p != pid);
91    }
92
93    pub fn children(&self) -> &[ProcessId] {
94        &self.children
95    }
96
97    pub fn parent_pid(&self) -> Option<ProcessId> {
98        self.parent_pid
99    }
100
101    pub fn state(&self) -> ProcessState {
102        self.state
103    }
104
105    pub fn set_state(&mut self, state: ProcessState) -> Result<(), ProcessStateError> {
106        if !self.state.can_transition_to(state) {
107            return Err(ProcessStateError {
108                from: self.state,
109                to: state,
110            });
111        }
112        self.state = state;
113        Ok(())
114    }
115
116    #[cfg(test)]
117    pub(crate) fn set_state_unchecked(&mut self, state: ProcessState) {
118        self.state = state;
119    }
120
121    pub fn is_runnable(&self) -> bool {
122        self.state == ProcessState::Ready
123    }
124
125    pub fn set_exit_code(&mut self, code: i32) {
126        self.exit_code = Some(code);
127        self.state = ProcessState::Zombie;
128    }
129
130    pub fn exit_code(&self) -> Option<i32> {
131        self.exit_code
132    }
133
134    pub fn context_mut(&mut self) -> &mut ProcessContext {
135        &mut self.context
136    }
137
138    pub fn context(&self) -> &ProcessContext {
139        &self.context
140    }
141
142    pub fn pid(&self) -> ProcessId {
143        self.pid
144    }
145
146    pub fn name(&self) -> &'static str {
147        self.name
148    }
149
150    pub fn tick(&mut self) -> usize {
151        if self.time_slice > 0 {
152            self.time_slice -= 1;
153        }
154        self.time_slice
155    }
156
157    pub fn reset_time_slice(&mut self) {
158        self.time_slice = 5;
159    }
160
161    pub fn time_slice(&self) -> usize {
162        self.time_slice
163    }
164
165    pub fn set_priority(&mut self, priority: usize) {
166        self.priority = priority;
167    }
168
169    pub fn priority(&self) -> usize {
170        self.priority
171    }
172
173    pub fn is_init(&self) -> bool {
174        self.pid.is_init()
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn pcb_tracks_children_and_zombie_exit_code() {
184        let parent = ProcessId::from_usize(1);
185        let child = ProcessId::from_usize(2);
186        let mut pcb = ProcessControlBlock::new(parent, "init", None);
187        pcb.add_child(child);
188        pcb.add_child(child);
189        assert_eq!(pcb.children(), &[child]);
190        pcb.remove_child(child);
191        assert!(pcb.children().is_empty());
192        pcb.set_exit_code(42);
193        assert_eq!(pcb.state(), ProcessState::Zombie);
194        assert_eq!(pcb.exit_code(), Some(42));
195    }
196
197    #[test]
198    fn invalid_state_transition_is_rejected() {
199        let mut pcb = ProcessControlBlock::new(ProcessId::from_usize(3), "worker", None);
200        let err = pcb.set_state(ProcessState::Blocked).unwrap_err();
201        assert_eq!(err.from, ProcessState::Ready);
202        assert_eq!(err.to, ProcessState::Blocked);
203        assert_eq!(pcb.state(), ProcessState::Ready);
204    }
205}