starnix_core/task/
process_group.rs1use crate::mutable_state::{ordered_state_accessor, state_implementation};
6use crate::signals::SignalInfo;
7use crate::task::{PidTable, Session, SessionDisassociation, 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 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: 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
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: 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 pub fn remove<L>(
102 &self,
103 locked: &mut Locked<L>,
104 thread_group: &ThreadGroup,
105 ) -> (bool, SessionDisassociation)
106 where
107 L: LockBefore<ProcessGroupState>,
108 {
109 let is_session_leader = self.session.leader == thread_group.leader;
110 let is_empty = self.write(locked).remove(thread_group);
111 let disassociation = if is_session_leader {
112 SessionDisassociation::new(Some(self.session.clone()))
113 } else {
114 SessionDisassociation::new(None)
115 };
116 (is_empty, disassociation)
117 }
118
119 pub fn send_signals<L>(&self, locked: &mut Locked<L>, signals: &[Signal])
120 where
121 L: LockBefore<ProcessGroupState>,
122 {
123 let thread_groups = self.read(locked).thread_groups().collect::<Vec<_>>();
124 Self::send_signals_to_thread_groups(signals, thread_groups);
125 }
126
127 pub fn check_orphaned<L>(&self, locked: &mut Locked<L>, _pids: &PidTable)
133 where
134 L: LockBefore<ProcessGroupState>,
135 {
136 let thread_groups = {
137 let state = self.read(locked);
138 if state.orphaned {
139 return;
140 }
141 state.thread_groups().collect::<Vec<_>>()
142 };
143 for tg in thread_groups {
144 let Some(parent) = tg.read().parent.clone() else {
145 return;
146 };
147 let parent = parent.upgrade();
148 let parent_state = parent.read();
149 if parent_state.process_group.as_ref() != self
150 && parent_state.process_group.session == self.session
151 {
152 return;
153 }
154 }
155 let thread_groups = {
156 let mut state = self.write(locked);
157 if state.orphaned {
158 return;
159 }
160 state.orphaned = true;
161 state.thread_groups().collect::<Vec<_>>()
162 };
163 if thread_groups.iter().any(|tg| tg.load_stopped().is_stopping_or_stopped()) {
164 Self::send_signals_to_thread_groups(&[SIGHUP, SIGCONT], thread_groups);
165 }
166 }
167
168 fn send_signals_to_thread_groups(
169 signals: &[Signal],
170 thread_groups: impl IntoIterator<Item = impl AsRef<ThreadGroup>>,
171 ) {
172 for thread_group in thread_groups.into_iter() {
173 for &signal in signals {
174 thread_group.as_ref().write().send_signal(SignalInfo::kernel(signal));
175 }
176 }
177 }
178}
179
180#[apply(state_implementation!)]
181impl ProcessGroupMutableState<Base = ProcessGroup> {
182 pub fn thread_groups(&self) -> Box<dyn Iterator<Item = Arc<ThreadGroup>> + '_> {
183 Box::new(self.thread_groups.values().map(|t| {
184 t.upgrade()
185 .expect("Weak references to thread_groups in ProcessGroup must always be valid")
186 }))
187 }
188
189 fn remove(&mut self, thread_group: &ThreadGroup) -> bool {
191 self.thread_groups.remove(&thread_group.leader);
192
193 self.thread_groups.is_empty()
194 }
195}