starnix_core/task/
process_group.rs1use crate::mutable_state::{state_accessor, state_implementation};
6use crate::signals::SignalInfo;
7use crate::task::{PidTable, Session, SessionDisassociation, ThreadGroup};
8use macro_rules_attribute::apply;
9use starnix_sync::{LockDepRwLock, 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 thread_groups: BTreeMap<pid_t, Weak<ThreadGroup>>,
23
24 orphaned: bool,
26}
27
28#[derive(Debug)]
29pub struct ProcessGroup {
30 pub session: Arc<Session>,
32
33 pub leader: pid_t,
35
36 mutable_state: LockDepRwLock<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
54impl 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: LockDepRwLock::new(ProcessGroupMutableState {
78 thread_groups: BTreeMap::new(),
79 orphaned: false,
80 }),
81 });
82 session.write().insert(&process_group);
83 process_group
84 }
85
86 state_accessor!(ProcessGroup, mutable_state);
87
88 pub fn insert(&self, thread_group: &ThreadGroup) {
89 self.write().thread_groups.insert(thread_group.leader, thread_group.weak_self.clone());
90 }
91
92 pub fn remove(&self, thread_group: &ThreadGroup) -> (bool, SessionDisassociation) {
97 let is_session_leader = self.session.leader == thread_group.leader;
98 let is_empty = self.write().remove(thread_group);
99 let disassociation = if is_session_leader {
100 SessionDisassociation::new(Some(self.session.clone()))
101 } else {
102 SessionDisassociation::new(None)
103 };
104 (is_empty, disassociation)
105 }
106
107 pub fn send_signals(&self, signals: &[Signal]) {
108 let thread_groups = self.read().thread_groups().collect::<Vec<_>>();
109 Self::send_signals_to_thread_groups(signals, thread_groups);
110 }
111
112 pub fn check_orphaned(&self, _pids: &PidTable) {
118 let thread_groups = {
119 let state = self.read();
120 if state.orphaned {
121 return;
122 }
123 state.thread_groups().collect::<Vec<_>>()
124 };
125 for tg in thread_groups {
126 let Some(parent) = tg.read().parent.clone() else {
127 return;
128 };
129 let parent = parent.upgrade();
130 let parent_state = parent.read();
131 if parent_state.process_group.as_ref() != self
132 && parent_state.process_group.session == self.session
133 {
134 return;
135 }
136 }
137 let thread_groups = {
138 let mut state = self.write();
139 if state.orphaned {
140 return;
141 }
142 state.orphaned = true;
143 state.thread_groups().collect::<Vec<_>>()
144 };
145 if thread_groups.iter().any(|tg| tg.load_stopped().is_stopping_or_stopped()) {
146 Self::send_signals_to_thread_groups(&[SIGHUP, SIGCONT], thread_groups);
147 }
148 }
149
150 fn send_signals_to_thread_groups(
151 signals: &[Signal],
152 thread_groups: impl IntoIterator<Item = impl AsRef<ThreadGroup>>,
153 ) {
154 for thread_group in thread_groups.into_iter() {
155 for &signal in signals {
156 thread_group.as_ref().write().send_signal(SignalInfo::kernel(signal));
157 }
158 }
159 }
160}
161
162#[apply(state_implementation!)]
163impl ProcessGroupMutableState<Base = ProcessGroup> {
164 pub fn thread_groups(&self) -> Box<dyn Iterator<Item = Arc<ThreadGroup>> + '_> {
165 Box::new(self.thread_groups.values().map(|t| {
166 t.upgrade()
167 .expect("Weak references to thread_groups in ProcessGroup must always be valid")
168 }))
169 }
170
171 fn remove(&mut self, thread_group: &ThreadGroup) -> bool {
173 self.thread_groups.remove(&thread_group.leader);
174
175 self.thread_groups.is_empty()
176 }
177}