starry_process/
process.rs1use alloc::{
2 collections::btree_set::BTreeSet,
3 sync::{Arc, Weak},
4 vec::Vec,
5};
6use core::{
7 fmt,
8 sync::atomic::{AtomicBool, Ordering},
9};
10
11use ax_kspin::SpinNoIrq;
12use ax_lazyinit::LazyInit;
13use weak_map::StrongMap;
14
15use crate::{Pid, ProcessGroup, Session};
16
17#[derive(Default)]
18pub(crate) struct ThreadGroup {
19 pub(crate) threads: BTreeSet<Pid>,
20 pub(crate) exit_code: i32,
21 pub(crate) group_exited: bool,
22}
23
24pub struct Process {
26 pid: Pid,
27 is_zombie: AtomicBool,
28 pub(crate) tg: SpinNoIrq<ThreadGroup>,
29
30 children: SpinNoIrq<StrongMap<Pid, Arc<Process>>>,
32 parent: SpinNoIrq<Weak<Process>>,
33
34 group: SpinNoIrq<Arc<ProcessGroup>>,
35}
36
37impl Process {
38 pub fn pid(&self) -> Pid {
40 self.pid
41 }
42
43 pub fn is_init(self: &Arc<Self>) -> bool {
49 Arc::ptr_eq(self, INIT_PROC.get().unwrap())
50 }
51}
52
53impl Process {
55 pub fn parent(&self) -> Option<Arc<Process>> {
57 self.parent.lock().upgrade()
58 }
59
60 pub fn children(&self) -> Vec<Arc<Process>> {
62 self.children.lock().values().cloned().collect()
63 }
64}
65
66impl Process {
68 pub fn group(&self) -> Arc<ProcessGroup> {
70 self.group.lock().clone()
71 }
72
73 fn set_group(self: &Arc<Self>, group: &Arc<ProcessGroup>) {
74 let mut self_group = self.group.lock();
75
76 self_group.processes.lock().remove(&self.pid);
77
78 group.processes.lock().insert(self.pid, self);
79
80 *self_group = group.clone();
81 }
82
83 pub fn create_session(self: &Arc<Self>) -> Option<(Arc<Session>, Arc<ProcessGroup>)> {
97 if self.group.lock().session.sid() == self.pid {
98 return None;
99 }
100
101 let new_session = Session::new(self.pid);
102 let new_group = ProcessGroup::new(self.pid, &new_session);
103 self.set_group(&new_group);
104
105 Some((new_session, new_group))
106 }
107
108 pub fn create_group(self: &Arc<Self>) -> Option<Arc<ProcessGroup>> {
118 if self.group.lock().pgid() == self.pid {
119 return None;
120 }
121
122 let new_group = ProcessGroup::new(self.pid, &self.group.lock().session);
123 self.set_group(&new_group);
124
125 Some(new_group)
126 }
127
128 pub fn move_to_group(self: &Arc<Self>, group: &Arc<ProcessGroup>) -> bool {
136 if Arc::ptr_eq(&self.group.lock(), group) {
137 return true;
138 }
139
140 if !Arc::ptr_eq(&self.group.lock().session, &group.session) {
141 return false;
142 }
143
144 self.set_group(group);
145 true
146 }
147}
148
149impl Process {
151 pub fn add_thread(self: &Arc<Self>, tid: Pid) {
153 self.tg.lock().threads.insert(tid);
154 }
155
156 pub fn exit_thread(self: &Arc<Self>, tid: Pid, exit_code: i32) -> bool {
161 let mut tg = self.tg.lock();
162 if !tg.group_exited {
163 tg.exit_code = exit_code;
164 }
165 tg.threads.remove(&tid);
166 tg.threads.is_empty()
167 }
168
169 pub fn threads(&self) -> Vec<Pid> {
171 self.tg.lock().threads.iter().cloned().collect()
172 }
173
174 pub fn rename_thread(self: &Arc<Self>, old_tid: Pid, new_tid: Pid) {
182 let mut tg = self.tg.lock();
183 tg.threads.remove(&old_tid);
184 tg.threads.insert(new_tid);
185 }
186
187 pub fn is_group_exited(&self) -> bool {
189 self.tg.lock().group_exited
190 }
191
192 pub fn group_exit(&self) {
194 self.tg.lock().group_exited = true;
195 }
196
197 pub fn exit_code(&self) -> i32 {
199 self.tg.lock().exit_code
200 }
201}
202
203impl Process {
205 pub fn is_zombie(&self) -> bool {
207 self.is_zombie.load(Ordering::Acquire)
208 }
209
210 pub fn exit(self: &Arc<Self>) {
217 let reaper_proc = INIT_PROC.get().unwrap();
219
220 if Arc::ptr_eq(self, reaper_proc) {
221 return;
222 }
223
224 let reaper_parent = Arc::downgrade(reaper_proc);
225 let children = {
226 let mut children = self.children.lock();
227 core::mem::take(&mut *children)
228 };
229
230 let mut reaper_children = reaper_proc.children.lock();
231 for (pid, child) in children {
232 *child.parent.lock() = reaper_parent.clone();
233 reaper_children.insert(pid, child);
234 }
235 drop(reaper_children);
236
237 self.is_zombie.store(true, Ordering::Release);
238 }
239
240 pub fn free(&self) {
244 assert!(self.is_zombie(), "only zombie process can be freed");
245
246 if let Some(parent) = self.parent() {
247 parent.children.lock().remove(&self.pid);
248 }
249 }
250}
251
252impl fmt::Debug for Process {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 let mut builder = f.debug_struct("Process");
255 builder.field("pid", &self.pid);
256
257 let tg = self.tg.lock();
258 if tg.group_exited {
259 builder.field("group_exited", &tg.group_exited);
260 }
261 if self.is_zombie() {
262 builder.field("exit_code", &tg.exit_code);
263 }
264
265 if let Some(parent) = self.parent() {
266 builder.field("parent", &parent.pid());
267 }
268 builder.field("group", &self.group());
269 builder.finish()
270 }
271}
272
273impl Process {
275 fn new(pid: Pid, parent: Option<Arc<Process>>) -> Arc<Process> {
276 let group = parent.as_ref().map_or_else(
277 || {
278 let session = Session::new(pid);
279 ProcessGroup::new(pid, &session)
280 },
281 |p| p.group(),
282 );
283
284 let process = Arc::new(Process {
285 pid,
286 is_zombie: AtomicBool::new(false),
287 tg: SpinNoIrq::new(ThreadGroup::default()),
288 children: SpinNoIrq::new(StrongMap::new()),
289 parent: SpinNoIrq::new(parent.as_ref().map(Arc::downgrade).unwrap_or_default()),
290 group: SpinNoIrq::new(group.clone()),
291 });
292
293 group.processes.lock().insert(pid, &process);
294
295 if let Some(parent) = parent {
296 parent.children.lock().insert(pid, process.clone());
297 } else {
298 INIT_PROC.init_once(process.clone());
299 }
300
301 process
302 }
303
304 pub fn new_init(pid: Pid) -> Arc<Process> {
309 Self::new(pid, None)
310 }
311
312 pub fn fork(self: &Arc<Process>, pid: Pid) -> Arc<Process> {
314 Self::new(pid, Some(self.clone()))
315 }
316}
317
318static INIT_PROC: LazyInit<Arc<Process>> = LazyInit::new();
319
320pub fn init_proc() -> Arc<Process> {
324 INIT_PROC.get().unwrap().clone()
325}