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}