blob: 68b6ba331d9579ed0a2849e991e9cb7e4bdb6742 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::mutable_state::{ordered_state_accessor, state_implementation};
use crate::signals::SignalInfo;
use crate::task::{Session, ThreadGroup};
use macro_rules_attribute::apply;
use starnix_sync::{LockBefore, Locked, OrderedRwLock, ProcessGroupState};
use starnix_uapi::pid_t;
use starnix_uapi::signals::{Signal, SIGCONT, SIGHUP};
use std::collections::BTreeMap;
use std::sync::{Arc, Weak};
#[derive(Debug)]
pub struct ProcessGroupMutableState {
/// The thread_groups in the process group.
///
/// The references to ThreadGroup is weak to prevent cycles as ThreadGroup have a Arc reference to their process group.
/// It is still expected that these weak references are always valid, as thread groups must unregister
/// themselves before they are deleted.
thread_groups: BTreeMap<pid_t, Weak<ThreadGroup>>,
/// Whether this process group is orphaned and already notified its members.
orphaned: bool,
}
#[derive(Debug)]
pub struct ProcessGroup {
/// The session of the process group.
pub session: Arc<Session>,
/// The leader of the process group.
pub leader: pid_t,
/// The mutable state of the ProcessGroup.
mutable_state: OrderedRwLock<ProcessGroupMutableState, ProcessGroupState>,
}
impl PartialEq for ProcessGroup {
fn eq(&self, other: &Self) -> bool {
self.leader == other.leader
}
}
impl Eq for ProcessGroup {}
impl std::hash::Hash for ProcessGroup {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.leader.hash(state);
}
}
/// A process group is a set of processes that are considered to be a unit for the purposes of job
/// control and signal delivery. Each process in a process group has the same process group
/// ID (PGID). The process with the same PID as the PGID is called the process group leader.
///
/// When a signal is sent to a process group, it is delivered to all processes in the group,
/// including the process group leader. This allows a single signal to be used to control all
/// processes in a group, such as stopping or resuming them all.
///
/// Process groups are also used for job control. The foreground and background process groups of a
/// terminal are used to determine which processes can read from and write to the terminal. The
/// foreground process group is the only process group that can read from and write to the terminal
/// at any given time.
///
/// When a process forks from its parent, the child process inherits the parent's PGID. A process
/// can also explicitly change its own PGID using the setpgid() system call.
///
/// Process groups are destroyed when the last process in the group exits.
impl ProcessGroup {
pub fn new(leader: pid_t, session: Option<Arc<Session>>) -> Arc<ProcessGroup> {
let session = session.unwrap_or_else(|| Session::new(leader));
let process_group = Arc::new(ProcessGroup {
session: session.clone(),
leader,
mutable_state: OrderedRwLock::new(ProcessGroupMutableState {
thread_groups: BTreeMap::new(),
orphaned: false,
}),
});
session.write().insert(&process_group);
process_group
}
ordered_state_accessor!(ProcessGroup, mutable_state, ProcessGroupState);
pub fn insert<L>(&self, locked: &mut Locked<'_, L>, thread_group: &Arc<ThreadGroup>)
where
L: LockBefore<ProcessGroupState>,
{
self.write(locked).thread_groups.insert(thread_group.leader, Arc::downgrade(thread_group));
}
/// Removes the thread group from the process group. Returns whether the process group is empty.
pub fn remove<L>(&self, locked: &mut Locked<'_, L>, thread_group: &ThreadGroup) -> bool
where
L: LockBefore<ProcessGroupState>,
{
self.write(locked).remove(thread_group)
}
pub fn send_signals<L>(&self, locked: &mut Locked<'_, L>, signals: &[Signal])
where
L: LockBefore<ProcessGroupState>,
{
let thread_groups = self.read(locked).thread_groups().collect::<Vec<_>>();
Self::send_signals_to_thread_groups(signals, thread_groups);
}
/// Check whether the process group became orphaned. If this is the case, send signals to its
/// members if at least one is stopped.
pub fn check_orphaned<L>(&self, locked: &mut Locked<'_, L>)
where
L: LockBefore<ProcessGroupState>,
{
let thread_groups = {
let state = self.read(locked);
if state.orphaned {
return;
}
state.thread_groups().collect::<Vec<_>>()
};
for tg in thread_groups {
let parent = tg.read().parent.clone();
match parent {
None => return,
Some(parent) => {
let parent_state = parent.read();
if parent_state.process_group.as_ref() != self
&& parent_state.process_group.session == self.session
{
return;
}
}
}
}
let thread_groups = {
let mut state = self.write(locked);
if state.orphaned {
return;
}
state.orphaned = true;
state.thread_groups().collect::<Vec<_>>()
};
if thread_groups.iter().any(|tg| tg.load_stopped().is_stopping_or_stopped()) {
Self::send_signals_to_thread_groups(&[SIGHUP, SIGCONT], thread_groups);
}
}
fn send_signals_to_thread_groups(signals: &[Signal], thread_groups: Vec<Arc<ThreadGroup>>) {
for signal in signals.iter() {
for thread_group in &thread_groups {
thread_group.write().send_signal(SignalInfo::default(*signal));
}
}
}
}
#[apply(state_implementation!)]
impl ProcessGroupMutableState<Base = ProcessGroup> {
pub fn thread_groups(&self) -> Box<dyn Iterator<Item = Arc<ThreadGroup>> + '_> {
Box::new(self.thread_groups.values().map(|t| {
t.upgrade()
.expect("Weak references to thread_groups in ProcessGroup must always be valid")
}))
}
/// Removes the thread group from the process group. Returns whether the process group is empty.
fn remove(&mut self, thread_group: &ThreadGroup) -> bool {
self.thread_groups.remove(&thread_group.leader);
self.thread_groups.is_empty()
}
}