Skip to main content

starnix_core/task/
pid_table.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::device::terminal::{Terminal, TerminalSide};
6use crate::ptrace::StopState;
7use crate::signals::SignalInfo;
8use crate::task::idr::{Idr, IdrGuard};
9use crate::task::memory_attribution::MemoryAttributionLifecycleEvent;
10use crate::task::{Task, ThreadGroup, ThreadGroupLinkAdapter, ThreadGroupLinkNode};
11use fuchsia_rcu::subtle::{RcuPtr, RcuPtrRef};
12use fuchsia_rcu::{RcuDroppable, RcuOptionBox, RcuReadScope, RcuWeak, rcu_drop};
13use fuchsia_rcu_collections::rcu_intrusive_list::RcuIntrusiveList;
14use starnix_sync::PidTableLock;
15use starnix_uapi::errors::Errno;
16use starnix_uapi::signals::Signal;
17use starnix_uapi::{errno, error, pid_t};
18use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
19use std::sync::{Arc, Weak};
20
21#[derive(Debug, RcuDroppable)]
22enum ProcessEntry {
23    ThreadGroup(Weak<ThreadGroup>),
24    Zombie(Weak<Task>),
25}
26
27impl ProcessEntry {
28    fn thread_group(&self) -> Option<&Weak<ThreadGroup>> {
29        match self {
30            Self::ThreadGroup(group) => Some(group),
31            _ => None,
32        }
33    }
34}
35
36/// Entities identified by a pid.
37#[derive(Debug, RcuDroppable)]
38pub struct PidEntry {
39    pub id: pid_t,
40    task: RcuWeak<Task>,
41    process: RcuOptionBox<ProcessEntry>,
42    pgid_thread_groups: RcuIntrusiveList<ThreadGroupLinkNode, ThreadGroupLinkAdapter>,
43    sid_thread_groups: RcuIntrusiveList<ThreadGroupLinkNode, ThreadGroupLinkAdapter>,
44}
45
46impl PidEntry {
47    pub fn get_task(&self) -> Result<Arc<Task>, Errno> {
48        self.task.upgrade().ok_or_else(|| errno!(ESRCH))
49    }
50
51    /// Returns a representative [`Task`] for this process, including when the thread-group
52    /// leader has exited or the process is an unreaped zombie.
53    pub fn get_process_task(&self) -> Option<Arc<Task>> {
54        if let Ok(task) = self.get_task() {
55            return Some(task);
56        }
57        let process = self.process.read()?;
58        match &*process {
59            ProcessEntry::ThreadGroup(thread_group) => {
60                if let Some(task) = thread_group.upgrade().and_then(|tg| tg.read().first_task()) {
61                    return Some(task);
62                }
63                match &*self.process.read()? {
64                    ProcessEntry::Zombie(task) => task.upgrade(),
65                    ProcessEntry::ThreadGroup(_) => None,
66                }
67            }
68            ProcessEntry::Zombie(task) => task.upgrade(),
69        }
70    }
71
72    pub fn get_process(&self) -> Option<ProcessEntryRef> {
73        let process = self.process.read()?;
74        match &*process {
75            ProcessEntry::ThreadGroup(thread_group) => {
76                // Because `self.process` is read lock-free under RCU, the process may concurrently
77                // transition to a zombie in `ThreadGroup::remove()` and drop its last strong
78                // `Arc<ThreadGroup>` reference before `upgrade()` is called here.
79                Some(match thread_group.upgrade() {
80                    Some(thread_group) => ProcessEntryRef::Process(thread_group),
81                    None => ProcessEntryRef::Zombie,
82                })
83            }
84            ProcessEntry::Zombie(_) => Some(ProcessEntryRef::Zombie),
85        }
86    }
87
88    pub fn get_thread_group(&self) -> Result<Arc<ThreadGroup>, Errno> {
89        match self.get_process() {
90            Some(ProcessEntryRef::Process(tg)) => Ok(tg),
91            _ => error!(ESRCH),
92        }
93    }
94
95    /// Returns whether any member thread groups belong to this process group (PGID).
96    pub fn is_process_group(&self, scope: &RcuReadScope) -> bool {
97        !self.pgid_thread_groups.is_empty(scope)
98    }
99
100    /// Returns whether any member thread groups belong to this session (SID).
101    pub fn is_session(&self, scope: &RcuReadScope) -> bool {
102        !self.sid_thread_groups.is_empty(scope)
103    }
104
105    /// Returns an iterator over the member thread groups in this process group (PGID).
106    pub fn pgid_thread_groups<'a>(
107        &'a self,
108        scope: &'a RcuReadScope,
109    ) -> impl Iterator<Item = Arc<ThreadGroup>> + 'a {
110        self.pgid_thread_groups.iter(scope).filter_map(|n| n.thread_group.upgrade())
111    }
112
113    /// Returns an iterator over the member thread groups in this session (SID).
114    pub fn sid_thread_groups<'a>(
115        &'a self,
116        scope: &'a RcuReadScope,
117    ) -> impl Iterator<Item = Arc<ThreadGroup>> + 'a {
118        self.sid_thread_groups.iter(scope).filter_map(|n| n.thread_group.upgrade())
119    }
120
121    /// Returns whether this process group (PGID) is non-empty and belongs to `session` (SID).
122    pub fn is_process_group_in_session(&self, session: &PidEntry, scope: &RcuReadScope) -> bool {
123        self.pgid_thread_groups(scope).next().is_some_and(|member_tg| {
124            session.sid_thread_groups(scope).any(|tg| Arc::ptr_eq(&tg, &member_tg))
125        })
126    }
127
128    /// Clears the controlling terminal on all member thread groups in this session (SID).
129    ///
130    /// If `matching` is `Some((terminal, maybe_side))`, only clears `controlling_terminal`
131    /// if it refers to `terminal` (and matches `side` if specified).
132    pub fn clear_controlling_terminal(&self, matching: Option<(&Terminal, Option<TerminalSide>)>) {
133        let scope = RcuReadScope::new();
134        for tg in self.sid_thread_groups(&scope) {
135            let mut state = tg.write();
136            if *state.session != *self {
137                continue;
138            }
139            let should_clear = state.controlling_terminal.as_ref().is_some_and(|ct| {
140                let matches = match matching {
141                    Some((terminal, Some(side))) => ct.matches(terminal, side),
142                    Some((terminal, None)) => std::ptr::eq(terminal, Arc::as_ptr(&ct.terminal)),
143                    None => true,
144                };
145                matches && ct.terminal.read().controlling_session().map(Arc::as_ref) != Some(self)
146            });
147            if should_clear {
148                state.controlling_terminal = None;
149            }
150        }
151    }
152
153    /// Returns whether the process group represented by this PidEntry is orphaned.
154    ///
155    /// An orphaned process group is one where every member's parent is either in the
156    /// same process group or outside the group's session.
157    ///
158    /// If `ignored_tg` is supplied, that thread group is ignored during traversal.
159    pub fn will_become_orphaned_pgrp(
160        &self,
161        scope: &RcuReadScope,
162        ignored_tg: Option<&ThreadGroup>,
163    ) -> bool {
164        for tg in self.pgid_thread_groups(scope) {
165            if ignored_tg.is_some_and(|ignored| std::ptr::eq(tg.as_ref(), ignored)) {
166                continue;
167            }
168
169            let (parent_tg, my_sid) = {
170                let tg_state = tg.read();
171                if !tg_state.is_running() {
172                    continue;
173                }
174                (tg_state.parent.as_ref().map(|p| p.upgrade()), tg_state.session.id)
175            };
176
177            let Some(parent_tg) = parent_tg else {
178                continue;
179            };
180
181            // Skip the parent if it matches ignored_tg.
182            if ignored_tg.is_some_and(|ignored| std::ptr::eq(parent_tg.as_ref(), ignored)) {
183                continue;
184            }
185
186            // Init (PID 1) does not act as a job-controlling parent for reparented tasks.
187            if parent_tg.leader.id == 1 {
188                continue;
189            }
190
191            let (parent_pgid, parent_sid) = {
192                let parent_state = parent_tg.read();
193                if !parent_state.is_running() {
194                    continue;
195                }
196                (parent_tg.pgid().id, parent_state.session.id)
197            };
198
199            // If a member has a parent in the same session but outside this process group,
200            // that parent handles job control. The group is NOT orphaned.
201            if parent_pgid != self.id && parent_sid == my_sid {
202                return false;
203            }
204        }
205
206        true
207    }
208
209    /// Returns whether any member in this process group is stopped by job control.
210    pub fn has_stopped_jobs(&self, scope: &RcuReadScope) -> bool {
211        for tg in self.pgid_thread_groups(scope) {
212            let stop_state = tg.load_stopped();
213            if matches!(stop_state, StopState::GroupStopping | StopState::GroupStopped)
214                && tg.read().is_running()
215            {
216                return true;
217            }
218        }
219        false
220    }
221
222    /// Dispatches signals to all member thread groups in this process group.
223    ///
224    /// Signals are delivered in order: each signal in `signals` is delivered to all
225    /// group members before the subsequent signal is dispatched.
226    pub fn send_signals_to_pgid(&self, signals: &[Signal]) {
227        let scope = RcuReadScope::new();
228        for &signal in signals {
229            for tg in self.pgid_thread_groups(&scope) {
230                let state = tg.write();
231                if !state.is_exited() {
232                    state.send_signal(SignalInfo::kernel(signal));
233                }
234            }
235        }
236    }
237
238    /// Attaches `thread_group` to `list` using `node_slot` to store the allocated link node.
239    ///
240    /// Allocates a fresh [`ThreadGroupLinkNode`] for this list attachment so moving a process
241    /// across process groups or sessions never mutates a node still visible to in-flight RCU
242    /// readers.
243    fn attach_thread_group_node(
244        _guard: &PidTableGuard<'_>,
245        list: &RcuIntrusiveList<ThreadGroupLinkNode, ThreadGroupLinkAdapter>,
246        node_slot: &RcuPtr<ThreadGroupLinkNode>,
247        thread_group: &Arc<ThreadGroup>,
248    ) {
249        let scope = RcuReadScope::new();
250        debug_assert!(node_slot.read(&scope).is_null());
251        let node_ptr =
252            Box::into_raw(Box::new(ThreadGroupLinkNode::new(Arc::downgrade(thread_group))));
253        node_slot.assign(node_ptr);
254        // SAFETY: Single-writer exclusion is guaranteed by `_guard`, and `node_ptr` is newly
255        // allocated for this list attachment and retired via `rcu_drop` upon detach.
256        unsafe {
257            list.push_back(&scope, RcuPtrRef::new(&scope, node_ptr));
258        }
259    }
260
261    /// Detaches the node stored in `node_slot` from `list`, returning whether `list` is now empty.
262    ///
263    /// Unlinks the node (leaving `link.next` intact for concurrent RCU readers) and schedules
264    /// deferred reclamation via [`rcu_drop`].
265    fn detach_thread_group_node(
266        _guard: &PidTableGuard<'_>,
267        list: &RcuIntrusiveList<ThreadGroupLinkNode, ThreadGroupLinkAdapter>,
268        node_slot: &RcuPtr<ThreadGroupLinkNode>,
269    ) -> bool {
270        let scope = RcuReadScope::new();
271        let node_ptr = node_slot.read(&scope);
272        if !node_ptr.is_null() {
273            node_slot.assign(std::ptr::null_mut());
274            // SAFETY: Single-writer exclusion is guaranteed by `_guard`. `remove` leaves
275            // `node.link.next` intact for concurrent RCU readers, and `node_ptr` is never reused.
276            unsafe {
277                list.remove(&scope, node_ptr);
278            }
279            // SAFETY: `node_ptr` was allocated via `Box::into_raw` in `attach_thread_group_node`
280            // and is detached at most once under `PidTableLock`.
281            rcu_drop(unsafe { Box::from_raw(node_ptr.as_mut_ptr()) });
282        }
283        list.is_empty(&scope)
284    }
285    /// Attaches a member thread group to this process group (PGID).
286    fn attach_pgid(&self, guard: &PidTableGuard<'_>, thread_group: &Arc<ThreadGroup>) {
287        Self::attach_thread_group_node(
288            guard,
289            &self.pgid_thread_groups,
290            &thread_group.pgrp_node,
291            thread_group,
292        );
293    }
294
295    /// Detaches a member thread group from this process group (PGID).
296    fn detach_pgid(&self, guard: &PidTableGuard<'_>, thread_group: &ThreadGroup) -> bool {
297        Self::detach_thread_group_node(guard, &self.pgid_thread_groups, &thread_group.pgrp_node)
298    }
299
300    /// Attaches a member thread group to this session (SID).
301    fn attach_sid(&self, guard: &PidTableGuard<'_>, thread_group: &Arc<ThreadGroup>) {
302        Self::attach_thread_group_node(
303            guard,
304            &self.sid_thread_groups,
305            &thread_group.session_node,
306            thread_group,
307        );
308    }
309
310    /// Detaches a member thread group from this session (SID).
311    fn detach_sid(&self, guard: &PidTableGuard<'_>, thread_group: &ThreadGroup) -> bool {
312        Self::detach_thread_group_node(guard, &self.sid_thread_groups, &thread_group.session_node)
313    }
314
315    #[cfg(test)]
316    pub fn new_for_test(id: pid_t) -> Pid {
317        Arc::new(Self::new(id))
318    }
319
320    fn new(id: pid_t) -> Self {
321        Self {
322            id,
323            task: Default::default(),
324            process: Default::default(),
325            pgid_thread_groups: Default::default(),
326            sid_thread_groups: Default::default(),
327        }
328    }
329
330    fn is_empty(&self, scope: &RcuReadScope) -> bool {
331        self.task.strong_count(scope) == 0
332            && self.process.is_none(scope)
333            && self.pgid_thread_groups.is_empty(scope)
334            && self.sid_thread_groups.is_empty(scope)
335    }
336}
337
338impl std::fmt::Display for PidEntry {
339    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340        write!(f, "{}", self.id)
341    }
342}
343
344impl PartialEq for PidEntry {
345    fn eq(&self, other: &Self) -> bool {
346        std::ptr::eq(self, other)
347    }
348}
349
350impl Eq for PidEntry {}
351
352impl PartialOrd for PidEntry {
353    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
354        Some(self.cmp(other))
355    }
356}
357
358impl Ord for PidEntry {
359    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
360        (self as *const Self).cmp(&(other as *const Self))
361    }
362}
363
364impl std::hash::Hash for PidEntry {
365    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
366        (self as *const Self).hash(state);
367    }
368}
369
370pub enum ProcessEntryRef {
371    Process(Arc<ThreadGroup>),
372    Zombie,
373}
374
375pub type Pid = Arc<PidEntry>;
376
377#[derive(Copy, Clone, Debug, Eq, PartialEq)]
378pub enum TaskEntryScope {
379    Task,
380    ThreadGroup,
381}
382
383/// Identifies the target process or thread for a `/proc/<pid>` or `/proc/<pid>/task/<tid>` node.
384#[derive(Clone, Debug)]
385pub struct TaskContainer {
386    pub pid: Pid,
387    pub tid: Pid,
388    pub scope: TaskEntryScope,
389}
390
391impl TaskContainer {
392    pub fn from_task(task: &Task) -> Self {
393        Self { pid: task.pid.clone(), tid: task.tid.clone(), scope: TaskEntryScope::Task }
394    }
395
396    pub fn from_pid(pid: Pid) -> Self {
397        Self { tid: pid.clone(), pid, scope: TaskEntryScope::ThreadGroup }
398    }
399
400    pub fn from_thread_group(pid: Pid, tid: Pid) -> Self {
401        Self { pid, tid, scope: TaskEntryScope::ThreadGroup }
402    }
403
404    pub fn get_task(&self) -> Result<Arc<Task>, Errno> {
405        if let Ok(task) = self.tid.get_task() {
406            return Ok(task);
407        }
408        if self.scope == TaskEntryScope::ThreadGroup && self.tid == self.pid {
409            return self.pid.get_process_task().ok_or_else(|| errno!(ESRCH));
410        }
411        error!(ESRCH)
412    }
413}
414
415/// The number of reserved PIDs in Linux. When wrapping around, PID allocation restarts at this value.
416pub const RESERVED_PIDS: u32 = 300;
417
418/// The default maximal PID considered in Linux.
419pub const PID_MAX_DEFAULT: pid_t = 1 << 15;
420
421/// The maximal PID considered in Linux.
422pub const PID_MAX_LIMIT: pid_t = 1 << 22;
423
424/// The default number of PIDs per CPU used to scale pid_max.
425pub const PIDS_PER_CPU_DEFAULT: pid_t = 1024;
426
427/// Returns the actual PID limit given a requested limit, scaled by the system's CPU count.
428fn actual_pid_limit(limit: pid_t) -> pid_t {
429    actual_pid_limit_with_cpus(limit, zx::system_get_num_cpus())
430}
431
432/// Returns the actual PID limit given a requested limit and the number of CPUs.
433fn actual_pid_limit_with_cpus(limit: pid_t, num_cpus: u32) -> pid_t {
434    let cpu_limit = (num_cpus as pid_t).saturating_mul(PIDS_PER_CPU_DEFAULT);
435    limit.max(cpu_limit).min(PID_MAX_LIMIT)
436}
437
438pub struct PidTable {
439    /// The most-recently allocated pid in this table.
440    last_pid: AtomicI32,
441
442    /// The number of live tasks in this table.
443    num_tasks: AtomicUsize,
444
445    /// The tasks in this table, organized by pid_t using an IDR radix tree.
446    idr: Idr<PidEntry, PidTableLock>,
447
448    /// Used to notify thread group changes.
449    thread_group_notifier: RcuOptionBox<std::sync::mpsc::Sender<MemoryAttributionLifecycleEvent>>,
450}
451
452impl Default for PidTable {
453    fn default() -> Self {
454        let idr = Idr::new_cyclic(Some(RESERVED_PIDS));
455        idr.set_max(actual_pid_limit(PID_MAX_DEFAULT) as u32);
456        idr.lock().reserve_id(0);
457        Self {
458            last_pid: AtomicI32::new(0),
459            num_tasks: AtomicUsize::new(0),
460            idr,
461            thread_group_notifier: Default::default(),
462        }
463    }
464}
465
466impl std::fmt::Debug for PidTable {
467    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468        f.debug_struct("PidTable")
469            .field("last_pid", &self.last_pid.load(Ordering::Relaxed))
470            .finish()
471    }
472}
473
474/// An RAII guard representing exclusive writer access to a [`PidTable`].
475///
476/// Holding this guard serializes mutations (allocations, reservations, removals)
477/// to the PID table while allowing concurrent lock-free reads.
478pub struct PidTableGuard<'a> {
479    table: &'a PidTable,
480    idr: IdrGuard<'a, PidEntry, PidTableLock>,
481}
482
483impl<'a> std::ops::Deref for PidTableGuard<'a> {
484    type Target = PidTable;
485
486    fn deref(&self) -> &Self::Target {
487        self.table
488    }
489}
490
491impl<'a> PidTableGuard<'a> {
492    /// Allocates a new PID, returning an error if the PID table is full.
493    pub fn allocate_pid(&mut self) -> Result<Pid, Errno> {
494        self.idr
495            .alloc(|id| Arc::new(PidEntry::new(id as pid_t)))
496            .map(|(_, pid)| pid)
497            .ok_or_else(|| errno!(EAGAIN))
498    }
499
500    pub fn add_task(&mut self, task: Arc<Task>) {
501        let scope = RcuReadScope::new();
502        let entry =
503            self.idr.lookup(task.tid.id as u32, &scope).expect("task.tid should be in pid table");
504        assert_eq!(entry.task.strong_count(&scope), 0);
505        if task.is_leader() {
506            assert!(entry.process.is_none(&scope));
507            self.table.last_pid.store(task.tid.id, Ordering::Relaxed);
508            // Publish process before task so lock-free RCU readers that look up a task
509            // first (e.g. /proc/<pid> lookups) always observe its process entry populated
510            // for a leader. Readers that check process first and fall back to task (such as
511            // new_pidfd) rely on task.is_leader() to handle concurrent initialization/reaping.
512            entry
513                .process
514                .update(Some(ProcessEntry::ThreadGroup(Arc::downgrade(task.thread_group()))));
515        }
516        entry.task.update(Arc::downgrade(&task));
517        self.table.num_tasks.fetch_add(1, Ordering::Relaxed);
518
519        if task.is_leader() {
520            // Notify thread group changes.
521            if let Some(notifier) = self.table.thread_group_notifier.as_ref(&scope) {
522                let mut tg_state = task.thread_group.write();
523                let _ = notifier.send(MemoryAttributionLifecycleEvent::creation(task.tid.id));
524                tg_state.notifier = Some(notifier.clone());
525            }
526        }
527    }
528
529    fn remove_item<F>(&mut self, pid: &Pid, do_remove: F)
530    where
531        F: FnOnce(&PidEntry),
532    {
533        let scope = RcuReadScope::new();
534        debug_assert_eq!(self.idr.lookup(pid.id as u32, &scope).as_ref(), Some(pid));
535        do_remove(pid);
536        if pid.is_empty(&scope) {
537            self.idr.remove(pid.id as u32);
538        }
539    }
540
541    pub fn remove_task(&mut self, tid: &Pid) {
542        self.table.num_tasks.fetch_sub(1, Ordering::Relaxed);
543        self.remove_item(tid, |entry| {
544            let scope = RcuReadScope::new();
545            assert!(entry.task.strong_count(&scope) > 0);
546            entry.task.update(Weak::new());
547        });
548    }
549
550    /// Replace process with the specified `pid` with a zombie.
551    pub fn kill_process(&mut self, pid: &Pid, zombie_task: Weak<Task>) {
552        let scope = RcuReadScope::new();
553        debug_assert_eq!(self.idr.lookup(pid.id as u32, &scope).as_ref(), Some(pid));
554        assert!(matches!(pid.process.read().as_deref(), Some(ProcessEntry::ThreadGroup(_))));
555
556        pid.process.update(Some(ProcessEntry::Zombie(zombie_task)));
557    }
558
559    pub fn remove_zombie(&mut self, pid: &Pid) {
560        let scope = RcuReadScope::new();
561
562        self.remove_item(pid, |entry| {
563            assert!(matches!(entry.process.read().as_deref(), Some(ProcessEntry::Zombie(_))));
564            entry.process.update(None);
565        });
566
567        // Notify thread group changes.
568        if let Some(notifier) = self.table.thread_group_notifier.as_ref(&scope) {
569            let _ = notifier.send(MemoryAttributionLifecycleEvent::destruction(pid.id));
570        }
571    }
572
573    pub fn attach_pgid(&self, pid: &PidEntry, thread_group: &Arc<ThreadGroup>) {
574        pid.attach_pgid(self, thread_group);
575    }
576
577    pub fn detach_pgid(&mut self, pid: &Pid, thread_group: &ThreadGroup) -> bool {
578        let scope = RcuReadScope::new();
579        let is_empty = pid.detach_pgid(self, thread_group);
580        if pid.is_empty(&scope) && self.idr.lookup(pid.id as u32, &scope).as_ref() == Some(pid) {
581            self.idr.remove(pid.id as u32);
582        }
583        is_empty
584    }
585
586    pub fn attach_sid(&self, pid: &PidEntry, thread_group: &Arc<ThreadGroup>) {
587        pid.attach_sid(self, thread_group);
588    }
589
590    pub fn detach_sid(&mut self, pid: &Pid, thread_group: &ThreadGroup) -> bool {
591        let scope = RcuReadScope::new();
592        let is_empty = pid.detach_sid(self, thread_group);
593        if pid.is_empty(&scope) && self.idr.lookup(pid.id as u32, &scope).as_ref() == Some(pid) {
594            self.idr.remove(pid.id as u32);
595        }
596        is_empty
597    }
598}
599
600impl PidTable {
601    /// Acquires the lock for mutations, returning a [`PidTableGuard`].
602    pub fn lock(&self) -> PidTableGuard<'_> {
603        PidTableGuard { table: self, idr: self.idr.lock() }
604    }
605
606    pub fn get(&self, pid: pid_t) -> Result<Pid, Errno> {
607        if pid <= 0 {
608            return error!(ESRCH);
609        }
610        let scope = RcuReadScope::new();
611        self.idr.lookup(pid as u32, &scope).ok_or_else(|| errno!(ESRCH))
612    }
613
614    pub fn set_thread_group_notifier(
615        &self,
616        notifier: std::sync::mpsc::Sender<MemoryAttributionLifecycleEvent>,
617    ) {
618        self.thread_group_notifier.update(Some(notifier));
619    }
620
621    pub fn get_thread_groups<'a>(
622        &'a self,
623        scope: &'a RcuReadScope,
624    ) -> impl Iterator<Item = Arc<ThreadGroup>> + 'a {
625        self.idr.iter(&scope).flat_map(move |(_, entry)| {
626            entry
627                .process
628                .as_ref(&scope)
629                .and_then(ProcessEntry::thread_group)
630                .and_then(|g| g.upgrade())
631        })
632    }
633
634    /// Returns the number of live tasks in this table.
635    pub fn num_tasks(&self) -> usize {
636        self.num_tasks.load(Ordering::Relaxed)
637    }
638
639    /// Returns the process ids for all processes, including zombies.
640    pub fn process_ids(&self) -> Vec<pid_t> {
641        let scope = RcuReadScope::new();
642        self.idr
643            .iter(&scope)
644            .flat_map(|(_, entry)| entry.process.is_some(&scope).then_some(entry.id))
645            .collect()
646    }
647
648    /// Returns an iterator over the [`Pid`]s for all the currently running tasks.
649    pub fn running_task_ids<'a>(
650        &'a self,
651        scope: &'a RcuReadScope,
652    ) -> impl Iterator<Item = &'a Pid> {
653        self.idr
654            .iter(scope)
655            .map(|(_, entry)| entry)
656            .filter(|entry| entry.task.strong_count(scope) > 0)
657    }
658
659    pub fn last_pid(&self) -> pid_t {
660        self.last_pid.load(Ordering::Relaxed)
661    }
662
663    /// Returns the maximal PID value allowed for allocation.
664    pub fn max(&self) -> pid_t {
665        self.idr.max() as pid_t
666    }
667
668    /// Sets the maximal PID value allowed for allocation.
669    pub fn set_max(&self, max: pid_t) {
670        self.idr.set_max(actual_pid_limit(max) as u32);
671    }
672}
673
674#[cfg(test)]
675mod tests {
676    use super::*;
677    use crate::task::ExitStatus;
678    use crate::testing::spawn_kernel_and_run;
679    use starnix_types::ownership::Releasable;
680    use starnix_uapi::signals::{SIGCHLD, SIGCONT};
681    use starnix_uapi::user_address::UserRef;
682    use starnix_uapi::{CLONE_CHILD_SETTID, CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
683    use std::collections::HashSet;
684    use std::sync::atomic::AtomicBool;
685
686    #[test]
687    fn test_pid_table_allocation() {
688        let table = PidTable::default();
689        let pid1 = table.lock().allocate_pid().unwrap();
690        assert_eq!(pid1.id, 1);
691
692        let pid2 = table.lock().allocate_pid().unwrap();
693        assert_eq!(pid2.id, 2);
694
695        assert_eq!(table.get(1).unwrap().id, 1);
696        assert_eq!(table.get(2).unwrap().id, 2);
697        assert!(table.get(0).is_err());
698        assert!(table.get(-1).is_err());
699        assert!(table.get(3).is_err());
700    }
701
702    #[test]
703    fn test_pid_table_lock_guard() {
704        let table = PidTable::default();
705        let pid1 = {
706            let mut guard = table.lock();
707            let pid = guard.allocate_pid().unwrap();
708            assert_eq!(pid.id, 1);
709            pid
710        };
711        assert_eq!(table.get(1).unwrap(), pid1);
712    }
713
714    #[test]
715    fn test_pid_table_empty_state() {
716        let table = PidTable::default();
717        assert_eq!(table.last_pid(), 0);
718        assert_eq!(table.num_tasks(), 0);
719        assert_eq!(table.process_ids().len(), 0);
720        let scope = RcuReadScope::new();
721        assert_eq!(table.running_task_ids(&scope).count(), 0);
722    }
723
724    #[test]
725    fn test_pid_table_max_and_wrap() {
726        let table = PidTable::default();
727        assert_eq!(table.max(), actual_pid_limit(PID_MAX_DEFAULT));
728
729        // Allocate up to the max value (1..=max).
730        let max = table.max();
731        for expected in 1..=max {
732            let pid = table.lock().allocate_pid().unwrap();
733            assert_eq!(pid.id, expected);
734        }
735
736        // The table is full up to max. Allocation fails.
737        assert_eq!(table.lock().allocate_pid().unwrap_err(), errno!(EAGAIN));
738
739        // Free PID 2 (below RESERVED_PIDS) and PID 302 (at or above RESERVED_PIDS).
740        table.idr.lock().remove(2);
741        table.idr.lock().remove(302);
742
743        // Next allocation wraps to RESERVED_PIDS (300) and allocates slot 302.
744        // Slot 2 is skipped because wrapping only allocates IDs >= RESERVED_PIDS.
745        let pid = table.lock().allocate_pid().unwrap();
746        assert_eq!(pid.id, 302);
747
748        // Now all slots >= RESERVED_PIDS are occupied, so allocation fails even though slot 2 is free.
749        assert_eq!(table.lock().allocate_pid().unwrap_err(), errno!(EAGAIN));
750
751        // Free PID 300. Allocation should reuse it.
752        table.idr.lock().remove(300);
753        let pid = table.lock().allocate_pid().unwrap();
754        assert_eq!(pid.id, 300);
755
756        // Increase max and verify new PIDs can be allocated.
757        let new_max = max + 10;
758        table.set_max(new_max);
759        assert_eq!(table.max(), new_max);
760        let pid = table.lock().allocate_pid().unwrap();
761        assert_eq!(pid.id, max + 1);
762    }
763
764    #[test]
765    fn test_actual_pid_limit() {
766        // With 1 CPU, limit scales to at least 1024, but PID_MAX_DEFAULT is 32768.
767        assert_eq!(actual_pid_limit_with_cpus(PID_MAX_DEFAULT, 1), PID_MAX_DEFAULT);
768        assert_eq!(actual_pid_limit_with_cpus(500, 1), 1024);
769
770        // With 64 CPUs, cpu_limit is 65536 > PID_MAX_DEFAULT.
771        assert_eq!(actual_pid_limit_with_cpus(PID_MAX_DEFAULT, 64), 65536);
772        assert_eq!(actual_pid_limit_with_cpus(100_000, 64), 100_000);
773
774        // Capped at PID_MAX_LIMIT.
775        assert_eq!(actual_pid_limit_with_cpus(PID_MAX_LIMIT + 1000, 1), PID_MAX_LIMIT);
776        assert_eq!(actual_pid_limit_with_cpus(PID_MAX_DEFAULT, 10_000), PID_MAX_LIMIT);
777    }
778
779    #[test]
780    fn test_get_process_after_thread_group_release() {
781        // `get_process()` reads `process` lock-free under RCU, so it can observe a
782        // `ThreadGroup` entry that `kill_process()` is concurrently replacing with `Zombie`
783        // and upgrade the `Weak` only after `CurrentTask::exit()` has dropped the last
784        // strong reference. `Weak::new()` never upgrades, modelling that released state.
785        let pid = PidEntry::new_for_test(1);
786        pid.process.update(Some(ProcessEntry::ThreadGroup(Weak::new())));
787
788        assert!(matches!(pid.get_process(), Some(ProcessEntryRef::Zombie)));
789    }
790
791    #[::fuchsia::test]
792    async fn test_pid_table_last_pid_on_thread_group() {
793        spawn_kernel_and_run(async |current_task| {
794            let kernel = current_task.kernel();
795            let initial_last_pid = kernel.pids.last_pid();
796
797            // Allocating a PID directly must not update last_pid.
798            let _allocated = kernel.pids.lock().allocate_pid().unwrap();
799            assert_eq!(kernel.pids.last_pid(), initial_last_pid);
800
801            // Cloning a thread in the same thread group must not update last_pid.
802            let _thread = current_task.clone_task_for_test(
803                (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
804                Some(SIGCHLD),
805            );
806            assert_eq!(kernel.pids.last_pid(), initial_last_pid);
807
808            // Cloning a new process (thread group leader) must update last_pid.
809            let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
810            assert_eq!(kernel.pids.last_pid(), child.get_pid());
811        })
812        .await;
813    }
814
815    #[::fuchsia::test]
816    async fn test_pid_entry_intrusive_lists() {
817        spawn_kernel_and_run(async |current_task| {
818            let kernel = current_task.kernel();
819            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
820            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
821            let scope = RcuReadScope::new();
822
823            assert!(!pid_entry.is_process_group(&scope));
824            assert!(!pid_entry.is_session(&scope));
825
826            // Detach from initial group/session and attach to pid_entry.
827            let guard = kernel.pids.lock();
828            current_pid.detach_pgid(&guard, current_task.thread_group());
829            current_pid.detach_sid(&guard, current_task.thread_group());
830            pid_entry.attach_pgid(&guard, current_task.thread_group());
831            pid_entry.attach_sid(&guard, current_task.thread_group());
832
833            // Verify PGID traversal.
834            let pgid_members: Vec<_> =
835                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
836            assert_eq!(pgid_members, vec![current_task.get_pid()]);
837
838            // Verify SID traversal.
839            let sid_members: Vec<_> =
840                pid_entry.sid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
841            assert_eq!(sid_members, vec![current_task.get_pid()]);
842
843            // Detach and restore to current_pid.
844            let pgid_empty = pid_entry.detach_pgid(&guard, current_task.thread_group());
845            assert!(pgid_empty);
846            let sid_empty = pid_entry.detach_sid(&guard, current_task.thread_group());
847            assert!(sid_empty);
848            current_pid.attach_pgid(&guard, current_task.thread_group());
849            current_pid.attach_sid(&guard, current_task.thread_group());
850        })
851        .await;
852    }
853
854    #[::fuchsia::test]
855    async fn test_pid_entry_empty_traversal() {
856        spawn_kernel_and_run(async |current_task| {
857            let kernel = current_task.kernel();
858            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
859            let scope = RcuReadScope::new();
860
861            // Verify empty traversals report no membership and return empty iterators.
862            assert!(!pid_entry.is_process_group(&scope));
863            assert!(!pid_entry.is_session(&scope));
864            assert!(pid_entry.pgid_thread_groups(&scope).next().is_none());
865            assert!(pid_entry.sid_thread_groups(&scope).next().is_none());
866        })
867        .await;
868    }
869
870    #[::fuchsia::test]
871    async fn test_pid_entry_multiple_tasks_ordered_and_reverse_detach() {
872        spawn_kernel_and_run(async |current_task| {
873            let kernel = current_task.kernel();
874            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
875            let scope = RcuReadScope::new();
876
877            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
878            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
879            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
880
881            let guard = kernel.pids.lock();
882            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
883            current_pid.detach_pgid(&guard, child1.thread_group());
884            current_pid.detach_pgid(&guard, child2.thread_group());
885            current_pid.detach_pgid(&guard, child3.thread_group());
886
887            pid_entry.attach_pgid(&guard, child1.thread_group());
888            pid_entry.attach_pgid(&guard, child2.thread_group());
889            pid_entry.attach_pgid(&guard, child3.thread_group());
890
891            let initial: Vec<_> =
892                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
893            assert_eq!(initial, vec![child1.get_pid(), child2.get_pid(), child3.get_pid()]);
894
895            // Detach tail first (child3).
896            let is_empty = pid_entry.detach_pgid(&guard, child3.thread_group());
897            assert!(!is_empty);
898            let state1: Vec<_> =
899                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
900            assert_eq!(state1, vec![child1.get_pid(), child2.get_pid()]);
901
902            // Detach next tail (child2).
903            let is_empty = pid_entry.detach_pgid(&guard, child2.thread_group());
904            assert!(!is_empty);
905            let state2: Vec<_> =
906                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
907            assert_eq!(state2, vec![child1.get_pid()]);
908
909            // Detach final remaining member (child1).
910            let is_empty = pid_entry.detach_pgid(&guard, child1.thread_group());
911            assert!(is_empty);
912            assert!(pid_entry.pgid_thread_groups(&scope).next().is_none());
913
914            current_pid.attach_pgid(&guard, child1.thread_group());
915            current_pid.attach_pgid(&guard, child2.thread_group());
916            current_pid.attach_pgid(&guard, child3.thread_group());
917        })
918        .await;
919    }
920
921    #[::fuchsia::test]
922    async fn test_pid_entry_head_detach() {
923        spawn_kernel_and_run(async |current_task| {
924            let kernel = current_task.kernel();
925            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
926            let scope = RcuReadScope::new();
927
928            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
929            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
930            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
931
932            let guard = kernel.pids.lock();
933            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
934            current_pid.detach_pgid(&guard, child1.thread_group());
935            current_pid.detach_pgid(&guard, child2.thread_group());
936            current_pid.detach_pgid(&guard, child3.thread_group());
937
938            pid_entry.attach_pgid(&guard, child1.thread_group());
939            pid_entry.attach_pgid(&guard, child2.thread_group());
940            pid_entry.attach_pgid(&guard, child3.thread_group());
941
942            // Detach head (child1).
943            let is_empty = pid_entry.detach_pgid(&guard, child1.thread_group());
944            assert!(!is_empty);
945            let state1: Vec<_> =
946                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
947            assert_eq!(state1, vec![child2.get_pid(), child3.get_pid()]);
948
949            // Detach new head (child2).
950            let is_empty = pid_entry.detach_pgid(&guard, child2.thread_group());
951            assert!(!is_empty);
952            let state2: Vec<_> =
953                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
954            assert_eq!(state2, vec![child3.get_pid()]);
955
956            // Detach final head (child3).
957            let is_empty = pid_entry.detach_pgid(&guard, child3.thread_group());
958            assert!(is_empty);
959            assert!(pid_entry.pgid_thread_groups(&scope).next().is_none());
960
961            current_pid.attach_pgid(&guard, child1.thread_group());
962            current_pid.attach_pgid(&guard, child2.thread_group());
963            current_pid.attach_pgid(&guard, child3.thread_group());
964        })
965        .await;
966    }
967
968    #[::fuchsia::test]
969    async fn test_pid_entry_middle_detach() {
970        spawn_kernel_and_run(async |current_task| {
971            let kernel = current_task.kernel();
972            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
973            let scope = RcuReadScope::new();
974
975            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
976            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
977            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
978
979            let guard = kernel.pids.lock();
980            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
981            current_pid.detach_pgid(&guard, child1.thread_group());
982            current_pid.detach_pgid(&guard, child2.thread_group());
983            current_pid.detach_pgid(&guard, child3.thread_group());
984
985            pid_entry.attach_pgid(&guard, child1.thread_group());
986            pid_entry.attach_pgid(&guard, child2.thread_group());
987            pid_entry.attach_pgid(&guard, child3.thread_group());
988
989            // Detach middle element (child2).
990            let is_empty = pid_entry.detach_pgid(&guard, child2.thread_group());
991            assert!(!is_empty);
992            let state1: Vec<_> =
993                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
994            assert_eq!(state1, vec![child1.get_pid(), child3.get_pid()]);
995
996            // Detach child3.
997            let is_empty = pid_entry.detach_pgid(&guard, child3.thread_group());
998            assert!(!is_empty);
999            let state2: Vec<_> =
1000                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
1001            assert_eq!(state2, vec![child1.get_pid()]);
1002
1003            // Detach child1.
1004            let is_empty = pid_entry.detach_pgid(&guard, child1.thread_group());
1005            assert!(is_empty);
1006            assert!(pid_entry.pgid_thread_groups(&scope).next().is_none());
1007
1008            current_pid.attach_pgid(&guard, child1.thread_group());
1009            current_pid.attach_pgid(&guard, child2.thread_group());
1010            current_pid.attach_pgid(&guard, child3.thread_group());
1011        })
1012        .await;
1013    }
1014
1015    #[::fuchsia::test]
1016    async fn test_pid_table_reclaims_pid_when_last_pgid_and_sid_task_detaches() {
1017        spawn_kernel_and_run(async |current_task| {
1018            let kernel = current_task.kernel();
1019
1020            // Create a session/process-group leader.
1021            let leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
1022            leader.thread_group().setsid().expect("setsid leader");
1023            let leader_pid_id = leader.get_pid();
1024
1025            // Create a member process in the leader's session and process group.
1026            let member = leader.clone_task_for_test(0, Some(SIGCHLD));
1027
1028            // Kill and drop the leader, then reap its zombie from current_task.
1029            leader.thread_group().kill(ExitStatus::Exit(0), None);
1030            drop(leader);
1031            {
1032                let mut pids = kernel.pids.lock();
1033                let zombie = current_task
1034                    .thread_group()
1035                    .write()
1036                    .zombie_children
1037                    .pop()
1038                    .expect("leader zombie");
1039                zombie.release(&mut pids);
1040            }
1041
1042            // Leader's PID must still exist in PidTable while member references it via pgid/sid.
1043            assert!(kernel.pids.get(leader_pid_id).is_ok());
1044
1045            // Move member into its own session and process group.
1046            member.thread_group().setsid().expect("setsid member");
1047
1048            // Now leader's PidEntry is completely empty and must be reclaimed from PidTable.
1049            assert_eq!(kernel.pids.get(leader_pid_id), error!(ESRCH));
1050        })
1051        .await;
1052    }
1053
1054    #[::fuchsia::test]
1055    async fn test_pid_entry_reattach_after_detach() {
1056        spawn_kernel_and_run(async |current_task| {
1057            let kernel = current_task.kernel();
1058            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
1059            let scope = RcuReadScope::new();
1060
1061            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1062            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1063            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1064
1065            let guard = kernel.pids.lock();
1066            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1067            current_pid.detach_pgid(&guard, child1.thread_group());
1068            current_pid.detach_pgid(&guard, child2.thread_group());
1069            current_pid.detach_pgid(&guard, child3.thread_group());
1070
1071            pid_entry.attach_pgid(&guard, child1.thread_group());
1072            pid_entry.attach_pgid(&guard, child2.thread_group());
1073            pid_entry.attach_pgid(&guard, child3.thread_group());
1074
1075            // Detach middle member child2.
1076            let is_empty = pid_entry.detach_pgid(&guard, child2.thread_group());
1077            assert!(!is_empty);
1078            let after_detach: Vec<_> =
1079                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
1080            assert_eq!(after_detach, vec![child1.get_pid(), child3.get_pid()]);
1081
1082            // Re-attach child2 to the same pid_entry.
1083            pid_entry.attach_pgid(&guard, child2.thread_group());
1084
1085            // Guard against cycle or infinite loop with take(10).
1086            let reattached: Vec<_> =
1087                pid_entry.pgid_thread_groups(&scope).take(10).map(|tg| tg.leader.id).collect();
1088            assert_eq!(reattached, vec![child1.get_pid(), child3.get_pid(), child2.get_pid()]);
1089
1090            pid_entry.detach_pgid(&guard, child1.thread_group());
1091            pid_entry.detach_pgid(&guard, child3.thread_group());
1092            pid_entry.detach_pgid(&guard, child2.thread_group());
1093            current_pid.attach_pgid(&guard, child1.thread_group());
1094            current_pid.attach_pgid(&guard, child2.thread_group());
1095            current_pid.attach_pgid(&guard, child3.thread_group());
1096        })
1097        .await;
1098    }
1099
1100    #[::fuchsia::test]
1101    async fn test_pid_entry_move_between_groups() {
1102        spawn_kernel_and_run(async |current_task| {
1103            let kernel = current_task.kernel();
1104            let (pid_entry1, pid_entry2) = {
1105                let mut guard = kernel.pids.lock();
1106                (guard.allocate_pid().unwrap(), guard.allocate_pid().unwrap())
1107            };
1108
1109            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1110            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1111            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1112
1113            let scope = RcuReadScope::new();
1114
1115            let guard = kernel.pids.lock();
1116            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1117            current_pid.detach_pgid(&guard, child1.thread_group());
1118            current_pid.detach_pgid(&guard, child2.thread_group());
1119            current_pid.detach_pgid(&guard, child3.thread_group());
1120
1121            pid_entry1.attach_pgid(&guard, child1.thread_group());
1122            pid_entry1.attach_pgid(&guard, child2.thread_group());
1123            pid_entry1.attach_pgid(&guard, child3.thread_group());
1124
1125            // Move child2 from group 1 to group 2.
1126            pid_entry1.detach_pgid(&guard, child2.thread_group());
1127            pid_entry2.attach_pgid(&guard, child2.thread_group());
1128
1129            let group1_members: Vec<_> =
1130                pid_entry1.pgid_thread_groups(&scope).take(10).map(|tg| tg.leader.id).collect();
1131            assert_eq!(group1_members, vec![child1.get_pid(), child3.get_pid()]);
1132
1133            let group2_members: Vec<_> =
1134                pid_entry2.pgid_thread_groups(&scope).take(10).map(|tg| tg.leader.id).collect();
1135            assert_eq!(group2_members, vec![child2.get_pid()]);
1136
1137            pid_entry1.detach_pgid(&guard, child1.thread_group());
1138            pid_entry1.detach_pgid(&guard, child3.thread_group());
1139            pid_entry2.detach_pgid(&guard, child2.thread_group());
1140            current_pid.attach_pgid(&guard, child1.thread_group());
1141            current_pid.attach_pgid(&guard, child2.thread_group());
1142            current_pid.attach_pgid(&guard, child3.thread_group());
1143        })
1144        .await;
1145    }
1146
1147    #[::fuchsia::test]
1148    async fn test_pid_entry_singleton_reattach_and_detach() {
1149        spawn_kernel_and_run(async |current_task| {
1150            let kernel = current_task.kernel();
1151            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
1152            let scope = RcuReadScope::new();
1153
1154            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1155
1156            let guard = kernel.pids.lock();
1157            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1158            current_pid.detach_pgid(&guard, child1.thread_group());
1159
1160            // Attach singleton.
1161            pid_entry.attach_pgid(&guard, child1.thread_group());
1162            assert_eq!(
1163                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect::<Vec<_>>(),
1164                vec![child1.get_pid()]
1165            );
1166
1167            // Detach singleton.
1168            let is_empty = pid_entry.detach_pgid(&guard, child1.thread_group());
1169            assert!(is_empty);
1170
1171            // Re-attach singleton.
1172            pid_entry.attach_pgid(&guard, child1.thread_group());
1173            assert_eq!(
1174                pid_entry.pgid_thread_groups(&scope).map(|tg| tg.leader.id).collect::<Vec<_>>(),
1175                vec![child1.get_pid()]
1176            );
1177
1178            // Detach singleton again and restore to current_pid.
1179            let is_empty = pid_entry.detach_pgid(&guard, child1.thread_group());
1180            assert!(is_empty);
1181            current_pid.attach_pgid(&guard, child1.thread_group());
1182        })
1183        .await;
1184    }
1185
1186    #[::fuchsia::test]
1187    async fn test_pid_entry_sid_tasks_adversarial() {
1188        spawn_kernel_and_run(async |current_task| {
1189            let kernel = current_task.kernel();
1190            let (pid_entry1, pid_entry2) = {
1191                let mut guard = kernel.pids.lock();
1192                (guard.allocate_pid().unwrap(), guard.allocate_pid().unwrap())
1193            };
1194            let scope = RcuReadScope::new();
1195
1196            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1197            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1198            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1199
1200            let guard = kernel.pids.lock();
1201            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1202            current_pid.detach_sid(&guard, child1.thread_group());
1203            current_pid.detach_sid(&guard, child2.thread_group());
1204            current_pid.detach_sid(&guard, child3.thread_group());
1205
1206            pid_entry1.attach_sid(&guard, child1.thread_group());
1207            pid_entry1.attach_sid(&guard, child2.thread_group());
1208            pid_entry1.attach_sid(&guard, child3.thread_group());
1209
1210            // Verify ordered traversal.
1211            let initial: Vec<_> =
1212                pid_entry1.sid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
1213            assert_eq!(initial, vec![child1.get_pid(), child2.get_pid(), child3.get_pid()]);
1214
1215            // Detach middle element (child2).
1216            let is_empty = pid_entry1.detach_sid(&guard, child2.thread_group());
1217            assert!(!is_empty);
1218            let state1: Vec<_> =
1219                pid_entry1.sid_thread_groups(&scope).map(|tg| tg.leader.id).collect();
1220            assert_eq!(state1, vec![child1.get_pid(), child3.get_pid()]);
1221
1222            // Re-attach child2 to pid_entry2 (move between sessions).
1223            pid_entry2.attach_sid(&guard, child2.thread_group());
1224            let session1_members: Vec<_> =
1225                pid_entry1.sid_thread_groups(&scope).take(10).map(|tg| tg.leader.id).collect();
1226            let session2_members: Vec<_> =
1227                pid_entry2.sid_thread_groups(&scope).take(10).map(|tg| tg.leader.id).collect();
1228            assert_eq!(session1_members, vec![child1.get_pid(), child3.get_pid()]);
1229            assert_eq!(session2_members, vec![child2.get_pid()]);
1230
1231            // Detach remaining elements and restore to current_pid.
1232            assert!(!pid_entry1.detach_sid(&guard, child3.thread_group()));
1233            assert!(pid_entry1.detach_sid(&guard, child1.thread_group()));
1234            assert!(pid_entry1.sid_thread_groups(&scope).next().is_none());
1235            assert!(pid_entry2.detach_sid(&guard, child2.thread_group()));
1236
1237            current_pid.attach_sid(&guard, child1.thread_group());
1238            current_pid.attach_sid(&guard, child2.thread_group());
1239            current_pid.attach_sid(&guard, child3.thread_group());
1240        })
1241        .await;
1242    }
1243
1244    #[::fuchsia::test]
1245    async fn test_pid_entry_signal_delivery_under_pid_table_lock() {
1246        spawn_kernel_and_run(async |current_task| {
1247            let kernel = current_task.kernel();
1248            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
1249
1250            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1251            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1252
1253            {
1254                let guard = kernel.pids.lock();
1255                let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1256                current_pid.detach_pgid(&guard, child1.thread_group());
1257                current_pid.detach_pgid(&guard, child2.thread_group());
1258
1259                pid_entry.attach_pgid(&guard, child1.thread_group());
1260                pid_entry.attach_pgid(&guard, child2.thread_group());
1261
1262                // Send signals while holding PidTableLock to verify that lock acquisition
1263                // order PidTableLock => ThreadGroupMutableStateLock succeeds without deadlock.
1264                pid_entry.send_signals_to_pgid(&[SIGCONT]);
1265            }
1266
1267            // Send signals without holding PidTableLock.
1268            pid_entry.send_signals_to_pgid(&[SIGCONT]);
1269
1270            let guard = kernel.pids.lock();
1271            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1272            pid_entry.detach_pgid(&guard, child1.thread_group());
1273            pid_entry.detach_pgid(&guard, child2.thread_group());
1274            current_pid.attach_pgid(&guard, child1.thread_group());
1275            current_pid.attach_pgid(&guard, child2.thread_group());
1276        })
1277        .await;
1278    }
1279
1280    #[::fuchsia::test]
1281    async fn test_pid_entry_concurrent_rcu_traversal_under_mutation() {
1282        spawn_kernel_and_run(async |current_task| {
1283            let kernel = current_task.kernel();
1284            let pid_entry = kernel.pids.lock().allocate_pid().unwrap();
1285
1286            let child1 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1287            let child2 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1288            let child3 = current_task.clone_task_for_test(0, Some(SIGCHLD));
1289
1290            {
1291                let guard = kernel.pids.lock();
1292                let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1293                current_pid.detach_pgid(&guard, child1.thread_group());
1294                current_pid.detach_pgid(&guard, child2.thread_group());
1295                current_pid.detach_pgid(&guard, child3.thread_group());
1296                current_pid.detach_sid(&guard, child1.thread_group());
1297                current_pid.detach_sid(&guard, child2.thread_group());
1298                current_pid.detach_sid(&guard, child3.thread_group());
1299            }
1300
1301            let valid_pids: HashSet<i32> =
1302                [child1.get_pid(), child2.get_pid(), child3.get_pid()].into_iter().collect();
1303
1304            let running = Arc::new(AtomicBool::new(true));
1305
1306            // Spawn concurrent reader thread 1: iterates pgid_thread_groups.
1307            let r1_pid = pid_entry.clone();
1308            let r1_running = Arc::clone(&running);
1309            let r1_pids = valid_pids.clone();
1310            let reader1 = std::thread::spawn(move || {
1311                while r1_running.load(Ordering::Relaxed) {
1312                    let scope = RcuReadScope::new();
1313                    let mut count = 0;
1314                    for tg in r1_pid.pgid_thread_groups(&scope) {
1315                        count += 1;
1316                        assert!(r1_pids.contains(&tg.leader.id));
1317                        assert!(
1318                            count <= 3,
1319                            "Infinite loop or cycle detected in pgid_thread_groups"
1320                        );
1321                    }
1322                }
1323            });
1324
1325            // Spawn concurrent reader thread 2: iterates sid_thread_groups.
1326            let r2_pid = pid_entry.clone();
1327            let r2_running = Arc::clone(&running);
1328            let r2_pids = valid_pids.clone();
1329            let reader2 = std::thread::spawn(move || {
1330                while r2_running.load(Ordering::Relaxed) {
1331                    let scope = RcuReadScope::new();
1332                    let tgs: Vec<_> = r2_pid.sid_thread_groups(&scope).take(10).collect();
1333                    assert!(tgs.len() <= 3, "Infinite loop or cycle detected in sid_thread_groups");
1334                    for tg in tgs {
1335                        assert!(r2_pids.contains(&tg.leader.id));
1336                    }
1337                }
1338            });
1339
1340            // Spawn concurrent signal delivery thread: delivers signals to process group.
1341            let r3_pid = pid_entry.clone();
1342            let r3_running = Arc::clone(&running);
1343            let signal_thread = std::thread::spawn(move || {
1344                while r3_running.load(Ordering::Relaxed) {
1345                    r3_pid.send_signals_to_pgid(&[SIGCONT]);
1346                }
1347            });
1348
1349            // Writer thread mutates pgid_thread_groups and sid_thread_groups under PidTableLock.
1350            for _ in 0..150 {
1351                {
1352                    let guard = kernel.pids.lock();
1353                    pid_entry.attach_pgid(&guard, child1.thread_group());
1354                    pid_entry.attach_pgid(&guard, child2.thread_group());
1355                    pid_entry.attach_sid(&guard, child2.thread_group());
1356                    pid_entry.attach_sid(&guard, child3.thread_group());
1357                }
1358
1359                {
1360                    let guard = kernel.pids.lock();
1361                    pid_entry.detach_pgid(&guard, child2.thread_group());
1362                    pid_entry.attach_pgid(&guard, child3.thread_group());
1363                    pid_entry.detach_sid(&guard, child2.thread_group());
1364                }
1365
1366                {
1367                    let guard = kernel.pids.lock();
1368                    pid_entry.detach_pgid(&guard, child1.thread_group());
1369                    pid_entry.detach_pgid(&guard, child3.thread_group());
1370                    pid_entry.detach_sid(&guard, child3.thread_group());
1371                }
1372            }
1373
1374            running.store(false, Ordering::Relaxed);
1375            reader1.join().expect("Reader 1 join failed");
1376            reader2.join().expect("Reader 2 join failed");
1377            signal_thread.join().expect("Signal thread join failed");
1378
1379            // Final cleanup check and restore to current_pid.
1380            let scope = RcuReadScope::new();
1381            assert!(pid_entry.pgid_thread_groups(&scope).next().is_none());
1382            assert!(pid_entry.sid_thread_groups(&scope).next().is_none());
1383
1384            let guard = kernel.pids.lock();
1385            let current_pid = kernel.pids.get(current_task.get_pid()).unwrap();
1386            current_pid.attach_pgid(&guard, child1.thread_group());
1387            current_pid.attach_pgid(&guard, child2.thread_group());
1388            current_pid.attach_pgid(&guard, child3.thread_group());
1389            current_pid.attach_sid(&guard, child1.thread_group());
1390            current_pid.attach_sid(&guard, child2.thread_group());
1391            current_pid.attach_sid(&guard, child3.thread_group());
1392        })
1393        .await;
1394    }
1395
1396    #[::fuchsia::test]
1397    async fn test_pid_table_num_tasks_tracks_live_tasks() {
1398        spawn_kernel_and_run(async |current_task| {
1399            let kernel = current_task.kernel();
1400            let scope = RcuReadScope::new();
1401            let initial_tasks = kernel.pids.num_tasks();
1402            let initial_processes = kernel.pids.process_ids().len();
1403            assert!(initial_tasks >= 1);
1404            assert_eq!(initial_tasks, kernel.pids.running_task_ids(&scope).count());
1405
1406            // Allocating a PID directly must not change num_tasks().
1407            let _allocated = kernel.pids.lock().allocate_pid().unwrap();
1408            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1409
1410            // Creating and dropping a thread in the same thread group must increment and
1411            // decrement num_tasks().
1412            let thread = current_task.clone_task_for_test(
1413                (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64,
1414                Some(SIGCHLD),
1415            );
1416            assert_eq!(kernel.pids.num_tasks(), initial_tasks + 1);
1417            assert_eq!(kernel.pids.num_tasks(), kernel.pids.running_task_ids(&scope).count());
1418            std::mem::drop(thread);
1419            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1420            assert_eq!(kernel.pids.num_tasks(), kernel.pids.running_task_ids(&scope).count());
1421
1422            // Creating a new process (thread group leader) must increment num_tasks().
1423            let child = current_task.clone_task_for_test(0, Some(SIGCHLD));
1424            assert_eq!(kernel.pids.num_tasks(), initial_tasks + 1);
1425            assert_eq!(kernel.pids.num_tasks(), kernel.pids.running_task_ids(&scope).count());
1426
1427            // Releasing the child process calls remove_task() and kill_process(), transitioning it
1428            // to a zombie and decrementing num_tasks(), while process_ids() still includes the
1429            // zombie.
1430            std::mem::drop(child);
1431            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1432            assert_eq!(kernel.pids.num_tasks(), kernel.pids.running_task_ids(&scope).count());
1433            assert_eq!(kernel.pids.process_ids().len(), initial_processes + 1);
1434
1435            // Reaping the zombie via remove_zombie removes it from process_ids() without
1436            // decrementing num_tasks() a second time.
1437            let zombies: Vec<_> =
1438                current_task.thread_group().write().zombie_children.drain(..).collect();
1439            assert_eq!(zombies.len(), 1);
1440            {
1441                let mut pids = kernel.pids.lock();
1442                for zombie in zombies {
1443                    zombie.release(&mut pids);
1444                }
1445            }
1446            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1447            assert_eq!(kernel.pids.process_ids().len(), initial_processes);
1448        })
1449        .await;
1450    }
1451
1452    #[::fuchsia::test]
1453    async fn test_pid_table_num_tasks_failed_clone_rollback() {
1454        spawn_kernel_and_run(async |current_task| {
1455            let kernel = current_task.kernel();
1456            let scope = RcuReadScope::new();
1457            let initial_tasks = kernel.pids.num_tasks();
1458            let initial_processes = kernel.pids.process_ids().len();
1459            assert!(initial_tasks >= 1);
1460            assert_eq!(initial_tasks, kernel.pids.running_task_ids(&scope).count());
1461
1462            // Passing CLONE_CHILD_SETTID with a null user_child_tid fails with EFAULT after
1463            // PidTableGuard::add_task has already incremented num_tasks, exercising the
1464            // release_on_error! rollback path for both process and thread clones.
1465            let Err(err) = current_task.clone_task(
1466                CLONE_CHILD_SETTID as u64,
1467                Some(SIGCHLD),
1468                UserRef::default(),
1469                UserRef::default(),
1470                UserRef::default(),
1471            ) else {
1472                panic!("clone_task with null user_child_tid should fail");
1473            };
1474            assert_eq!(err, errno!(EFAULT));
1475            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1476            assert_eq!(kernel.pids.num_tasks(), kernel.pids.running_task_ids(&scope).count());
1477
1478            let Err(err) = current_task.clone_task(
1479                (CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_CHILD_SETTID) as u64,
1480                Some(SIGCHLD),
1481                UserRef::default(),
1482                UserRef::default(),
1483                UserRef::default(),
1484            ) else {
1485                panic!("thread clone_task with null user_child_tid should fail");
1486            };
1487            assert_eq!(err, errno!(EFAULT));
1488            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1489            assert_eq!(kernel.pids.num_tasks(), kernel.pids.running_task_ids(&scope).count());
1490
1491            let zombies: Vec<_> =
1492                current_task.thread_group().write().zombie_children.drain(..).collect();
1493            {
1494                let mut pids = kernel.pids.lock();
1495                for zombie in zombies {
1496                    zombie.release(&mut pids);
1497                }
1498            }
1499            assert_eq!(kernel.pids.num_tasks(), initial_tasks);
1500            assert_eq!(kernel.pids.process_ids().len(), initial_processes);
1501        })
1502        .await;
1503    }
1504}