use std::collections::HashMap;
use std::sync::Mutex;
use tokio::process::Child;
pub struct BgEntry {
pub command: String,
pub child: Child,
}
pub struct BgRegistry {
inner: Mutex<HashMap<u32, BgEntry>>,
}
impl BgRegistry {
pub fn new() -> Self {
Self {
inner: Mutex::new(HashMap::new()),
}
}
pub fn insert(&self, pid: u32, command: String, child: Child) -> u32 {
self.inner
.lock()
.unwrap()
.insert(pid, BgEntry { command, child });
pid
}
pub fn list(&self) -> Vec<(u32, String)> {
self.inner
.lock()
.unwrap()
.iter()
.map(|(pid, e)| (*pid, e.command.clone()))
.collect()
}
pub fn len(&self) -> usize {
self.inner.lock().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.inner.lock().unwrap().is_empty()
}
}
impl Default for BgRegistry {
fn default() -> Self {
Self::new()
}
}
impl Drop for BgRegistry {
fn drop(&mut self) {
let mut guard = self.inner.lock().unwrap();
for (pid, entry) in guard.iter_mut() {
if let Err(e) = entry.child.start_kill() {
tracing::warn!("BgRegistry drop: failed to kill PID {pid}: {e}");
} else {
tracing::debug!("BgRegistry drop: sent SIGTERM to PID {pid}");
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_starts_empty() {
let reg = BgRegistry::new();
assert_eq!(reg.len(), 0);
assert!(reg.list().is_empty());
}
}