use alloc::{
sync::{Arc, Weak},
vec::Vec,
};
use core::fmt;
use weak_map::WeakMap;
use super::{Process, Session};
use crate::{
StarryResult,
sync::SpinLock,
task::{Pgid, PgidNumber, PidIdentity, PidRoleLease, TgidNumber},
};
pub struct ProcessGroup {
pgid: PgidNumber,
identity: Weak<PidIdentity>,
_role: PidRoleLease<Pgid>,
pub(crate) session: Arc<Session>,
pub(crate) processes: SpinLock<WeakMap<TgidNumber, Weak<Process>>>,
}
impl ProcessGroup {
pub(crate) fn get_or_create(
identity: Arc<PidIdentity>,
session: &Arc<Session>,
) -> StarryResult<Arc<Self>> {
let pgid = PgidNumber::from(identity.root_number());
let mut groups = session.process_groups.lock_irqsave();
if let Some(existing) = groups.get(&pgid) {
return Ok(existing);
}
let role = identity.acquire_role::<Pgid>()?;
let group = Arc::new(Self {
pgid,
identity: Arc::downgrade(&identity),
_role: role,
session: session.clone(),
processes: SpinLock::new(WeakMap::new()),
});
identity.bind_process_group(&group);
groups.insert(pgid, &group);
Ok(group)
}
}
impl ProcessGroup {
pub const fn pgid(&self) -> PgidNumber {
self.pgid
}
pub(crate) const fn pgid_number(&self) -> PgidNumber {
self.pgid
}
pub(crate) fn identity(&self) -> Arc<PidIdentity> {
self.identity
.upgrade()
.expect("process group outlived its PID identity")
}
pub fn session(&self) -> Arc<Session> {
self.session.clone()
}
pub fn processes(&self) -> Vec<Arc<Process>> {
self.processes.lock_irqsave().values().collect()
}
}
impl fmt::Debug for ProcessGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ProcessGroup({}, session={})",
self.pgid,
self.session.sid()
)
}
}
#[cfg(all(test, not(axtest)))]
mod tests {
extern crate std;
use std::{sync::Barrier, thread};
use super::*;
#[test]
fn duplicate_live_group_identity_reuses_the_session_group() {
let namespace = crate::task::new_test_pid_namespace();
let (session_identity, _session_tgid) = crate::task::new_test_process_identity(&namespace);
let session = Session::new(session_identity).unwrap();
let (group_identity, _group_tgid) = crate::task::new_test_process_identity(&namespace);
let start = Arc::new(Barrier::new(2));
let first_session = session.clone();
let first_start = start.clone();
let first_identity = group_identity.clone();
let first = thread::spawn(move || {
first_start.wait();
ProcessGroup::get_or_create(first_identity, &first_session).unwrap()
});
let second = thread::spawn(move || {
start.wait();
ProcessGroup::get_or_create(group_identity, &session).unwrap()
});
let first = first.join().unwrap();
let second = second.join().unwrap();
let session = first.session();
assert!(Arc::ptr_eq(&first, &second));
assert_eq!(session.process_groups().len(), 1);
}
}