starnix_core/task/
process_group.rs

1// Copyright 2022 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::mutable_state::{ordered_state_accessor, state_implementation};
6use crate::signals::SignalInfo;
7use crate::task::{PidTable, Session, ThreadGroup};
8use macro_rules_attribute::apply;
9use starnix_sync::{LockBefore, Locked, OrderedRwLock, ProcessGroupState};
10use starnix_uapi::pid_t;
11use starnix_uapi::signals::{SIGCONT, SIGHUP, Signal};
12use std::collections::BTreeMap;
13use std::sync::{Arc, Weak};
14
15#[derive(Debug)]
16pub struct ProcessGroupMutableState {
17    /// The thread_groups in the process group.
18    ///
19    /// The references to ThreadGroup is weak to prevent cycles as ThreadGroup have a Arc reference to their process group.
20    /// It is still expected that these weak references are always valid, as thread groups must unregister
21    /// themselves before they are deleted.
22    thread_groups: BTreeMap<pid_t, Weak<ThreadGroup>>,
23
24    /// Whether this process group is orphaned and already notified its members.
25    orphaned: bool,
26}
27
28#[derive(Debug)]
29pub struct ProcessGroup {
30    /// The session of the process group.
31    pub session: Arc<Session>,
32
33    /// The leader of the process group.
34    pub leader: pid_t,
35
36    /// The mutable state of the ProcessGroup.
37    mutable_state: OrderedRwLock<ProcessGroupMutableState, ProcessGroupState>,
38}
39
40impl PartialEq for ProcessGroup {
41    fn eq(&self, other: &Self) -> bool {
42        self.leader == other.leader
43    }
44}
45
46impl Eq for ProcessGroup {}
47
48impl std::hash::Hash for ProcessGroup {
49    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
50        self.leader.hash(state);
51    }
52}
53
54/// A process group is a set of processes that are considered to be a unit for the purposes of job
55/// control and signal delivery. Each process in a process group has the same process group
56/// ID (PGID). The process with the same PID as the PGID is called the process group leader.
57///
58/// When a signal is sent to a process group, it is delivered to all processes in the group,
59/// including the process group leader. This allows a single signal to be used to control all
60/// processes in a group, such as stopping or resuming them all.
61///
62/// Process groups are also used for job control. The foreground and background process groups of a
63/// terminal are used to determine which processes can read from and write to the terminal. The
64/// foreground process group is the only process group that can read from and write to the terminal
65/// at any given time.
66///
67/// When a process forks from its parent, the child process inherits the parent's PGID. A process
68/// can also explicitly change its own PGID using the setpgid() system call.
69///
70/// Process groups are destroyed when the last process in the group exits.
71impl ProcessGroup {
72    pub fn new(leader: pid_t, session: Option<Arc<Session>>) -> Arc<ProcessGroup> {
73        let session = session.unwrap_or_else(|| Session::new(leader));
74        let process_group = Arc::new(ProcessGroup {
75            session: session.clone(),
76            leader,
77            mutable_state: OrderedRwLock::new(ProcessGroupMutableState {
78                thread_groups: BTreeMap::new(),
79                orphaned: false,
80            }),
81        });
82        session.write().insert(&process_group);
83        process_group
84    }
85
86    ordered_state_accessor!(ProcessGroup, mutable_state, ProcessGroupState);
87
88    pub fn insert<L>(&self, locked: &mut Locked<L>, thread_group: &ThreadGroup)
89    where
90        L: LockBefore<ProcessGroupState>,
91    {
92        self.write(locked)
93            .thread_groups
94            .insert(thread_group.leader, thread_group.weak_self.clone());
95    }
96
97    /// Removes the thread group from the process group. Returns whether the process group is empty.
98    pub fn remove<L>(&self, locked: &mut Locked<L>, thread_group: &ThreadGroup) -> bool
99    where
100        L: LockBefore<ProcessGroupState>,
101    {
102        self.write(locked).remove(thread_group)
103    }
104
105    pub fn send_signals<L>(&self, locked: &mut Locked<L>, signals: &[Signal])
106    where
107        L: LockBefore<ProcessGroupState>,
108    {
109        let thread_groups = self.read(locked).thread_groups().collect::<Vec<_>>();
110        Self::send_signals_to_thread_groups(signals, thread_groups);
111    }
112
113    /// Check whether the process group became orphaned. If this is the case, send signals to its
114    /// members if at least one is stopped.
115    ///
116    /// Takes a read lock on the PidTable to ensure the object cannot be removed while this method
117    /// is running.
118    pub fn check_orphaned<L>(&self, locked: &mut Locked<L>, _pids: &PidTable)
119    where
120        L: LockBefore<ProcessGroupState>,
121    {
122        let thread_groups = {
123            let state = self.read(locked);
124            if state.orphaned {
125                return;
126            }
127            state.thread_groups().collect::<Vec<_>>()
128        };
129        for tg in thread_groups {
130            let Some(parent) = tg.read().parent.clone() else {
131                return;
132            };
133            let parent = parent.upgrade();
134            let parent_state = parent.read();
135            if parent_state.process_group.as_ref() != self
136                && parent_state.process_group.session == self.session
137            {
138                return;
139            }
140        }
141        let thread_groups = {
142            let mut state = self.write(locked);
143            if state.orphaned {
144                return;
145            }
146            state.orphaned = true;
147            state.thread_groups().collect::<Vec<_>>()
148        };
149        if thread_groups.iter().any(|tg| tg.load_stopped().is_stopping_or_stopped()) {
150            Self::send_signals_to_thread_groups(&[SIGHUP, SIGCONT], thread_groups);
151        }
152    }
153
154    fn send_signals_to_thread_groups(
155        signals: &[Signal],
156        thread_groups: impl IntoIterator<Item = impl AsRef<ThreadGroup>>,
157    ) {
158        for thread_group in thread_groups.into_iter() {
159            for signal in signals.iter() {
160                thread_group.as_ref().write().send_signal(SignalInfo::default(*signal));
161            }
162        }
163    }
164}
165
166#[apply(state_implementation!)]
167impl ProcessGroupMutableState<Base = ProcessGroup> {
168    pub fn thread_groups(&self) -> Box<dyn Iterator<Item = Arc<ThreadGroup>> + '_> {
169        Box::new(self.thread_groups.values().map(|t| {
170            t.upgrade()
171                .expect("Weak references to thread_groups in ProcessGroup must always be valid")
172        }))
173    }
174
175    /// Removes the thread group from the process group. Returns whether the process group is empty.
176    fn remove(&mut self, thread_group: &ThreadGroup) -> bool {
177        self.thread_groups.remove(&thread_group.leader);
178
179        self.thread_groups.is_empty()
180    }
181}