Skip to main content

starry_process/
process.rs

1use 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
24/// A process.
25pub struct Process {
26    pid: Pid,
27    is_zombie: AtomicBool,
28    pub(crate) tg: SpinNoIrq<ThreadGroup>,
29
30    // TODO: child subreaper9
31    children: SpinNoIrq<StrongMap<Pid, Arc<Process>>>,
32    parent: SpinNoIrq<Weak<Process>>,
33
34    group: SpinNoIrq<Arc<ProcessGroup>>,
35}
36
37impl Process {
38    /// The [`Process`] ID.
39    pub fn pid(&self) -> Pid {
40        self.pid
41    }
42
43    /// Returns `true` if the [`Process`] is the init process.
44    ///
45    /// This is a convenience method for checking if the [`Process`]
46    /// [`Arc::ptr_eq`]s with the init process, which is cheaper than
47    /// calling [`init_proc`] or testing if [`Process::parent`] is `None`.
48    pub fn is_init(self: &Arc<Self>) -> bool {
49        Arc::ptr_eq(self, INIT_PROC.get().unwrap())
50    }
51}
52
53/// Parent & children
54impl Process {
55    /// The parent [`Process`].
56    pub fn parent(&self) -> Option<Arc<Process>> {
57        self.parent.lock().upgrade()
58    }
59
60    /// The child [`Process`]es.
61    pub fn children(&self) -> Vec<Arc<Process>> {
62        self.children.lock().values().cloned().collect()
63    }
64}
65
66/// [`ProcessGroup`] & [`Session`]
67impl Process {
68    /// The [`ProcessGroup`] that the [`Process`] belongs to.
69    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    /// Creates a new [`Session`] and new [`ProcessGroup`] and moves the
84    /// [`Process`] to it.
85    ///
86    /// If the [`Process`] is already a session leader, this method does
87    /// nothing and returns `None`.
88    ///
89    /// Otherwise, it returns the new [`Session`] and [`ProcessGroup`].
90    ///
91    /// The caller has to ensure that the new [`ProcessGroup`] does not conflict
92    /// with any existing [`ProcessGroup`]. Thus, the [`Process`] must not
93    /// be a [`ProcessGroup`] leader.
94    ///
95    /// Checking [`Session`] conflicts is unnecessary.
96    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    /// Creates a new [`ProcessGroup`] and moves the [`Process`] to it.
109    ///
110    /// If the [`Process`] is already a group leader, this method does nothing
111    /// and returns `None`.
112    ///
113    /// Otherwise, it returns the new [`ProcessGroup`].
114    ///
115    /// The caller has to ensure that the new [`ProcessGroup`] does not conflict
116    /// with any existing [`ProcessGroup`].
117    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    /// Moves the [`Process`] to a specified [`ProcessGroup`].
129    ///
130    /// Returns `true` if the move succeeded. The move failed if the
131    /// [`ProcessGroup`] is not in the same [`Session`] as the [`Process`].
132    ///
133    /// If the [`Process`] is already in the specified [`ProcessGroup`], this
134    /// method does nothing and returns `true`.
135    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
149/// Threads
150impl Process {
151    /// Adds a thread to this [`Process`] with the given thread ID.
152    pub fn add_thread(self: &Arc<Self>, tid: Pid) {
153        self.tg.lock().threads.insert(tid);
154    }
155
156    /// Removes a thread from this [`Process`] and sets the exit code if the
157    /// group has not exited.
158    ///
159    /// Returns `true` if this was the last thread in the process.
160    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    /// Get all threads in this [`Process`].
170    pub fn threads(&self) -> Vec<Pid> {
171        self.tg.lock().threads.iter().cloned().collect()
172    }
173
174    /// Renames a thread in the thread group.
175    ///
176    /// Used by `execve`'s de_thread step when a non-leader thread successfully
177    /// `execve`s: the calling thread inherits the leader's TID so that
178    /// `gettid() == getpid()` holds in the new image. We swap `old_tid` for
179    /// `new_tid` atomically inside the thread-group lock so there is no
180    /// instant in which the caller is unrepresented in the group.
181    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    /// Returns `true` if the [`Process`] is group exited.
188    pub fn is_group_exited(&self) -> bool {
189        self.tg.lock().group_exited
190    }
191
192    /// Marks the [`Process`] as group exited.
193    pub fn group_exit(&self) {
194        self.tg.lock().group_exited = true;
195    }
196
197    /// The exit code of the [`Process`].
198    pub fn exit_code(&self) -> i32 {
199        self.tg.lock().exit_code
200    }
201}
202
203/// Status & exit
204impl Process {
205    /// Returns `true` if the [`Process`] is a zombie process.
206    pub fn is_zombie(&self) -> bool {
207        self.is_zombie.load(Ordering::Acquire)
208    }
209
210    /// Terminates the [`Process`], marking it as a zombie process.
211    ///
212    /// Child processes are inherited by the init process or by the nearest
213    /// subreaper process.
214    ///
215    /// This method panics if the [`Process`] is the init process.
216    pub fn exit(self: &Arc<Self>) {
217        // TODO: child subreaper
218        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    /// Frees a zombie [`Process`]. Removes it from the parent.
241    ///
242    /// This method panics if the [`Process`] is not a zombie.
243    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
273/// Builder
274impl 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    /// Creates a init [`Process`].
305    ///
306    /// This function can be called multiple times, but
307    /// [`ProcessBuilder::build`] on the the result must be called only once.
308    pub fn new_init(pid: Pid) -> Arc<Process> {
309        Self::new(pid, None)
310    }
311
312    /// Creates a child [`Process`].
313    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
320/// Gets the init process.
321///
322/// This function panics if the init process has not been initialized yet.
323pub fn init_proc() -> Arc<Process> {
324    INIT_PROC.get().unwrap().clone()
325}