Skip to main content

ci_engine/
proc_group.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Shared registry for hard teardown of live check process groups.
3
4use std::{
5    collections::BTreeSet,
6    sync::{Arc, Mutex},
7};
8
9#[derive(Default, Debug)]
10struct Inner {
11    live: BTreeSet<i32>,
12    torn_down: bool,
13}
14
15/// Concurrency-safe set of process-group ids.
16#[derive(Clone, Default, Debug)]
17pub struct ProcGroupRegistry {
18    inner: Arc<Mutex<Inner>>,
19}
20
21impl ProcGroupRegistry {
22    /// Construct an empty registry.
23    #[must_use]
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Register a group unless teardown already happened.
29    #[must_use]
30    pub fn register_active(&self, process_group: i32) -> bool {
31        let mut inner = self.inner.lock().expect("process-group registry poisoned");
32        if inner.torn_down {
33            false
34        } else {
35            inner.live.insert(process_group);
36            true
37        }
38    }
39
40    /// Remove a reaped group.
41    pub fn unregister(&self, process_group: i32) {
42        self.inner
43            .lock()
44            .expect("process-group registry poisoned")
45            .live
46            .remove(&process_group);
47    }
48
49    /// Number of active groups.
50    #[must_use]
51    pub fn len(&self) -> usize {
52        self.inner
53            .lock()
54            .expect("process-group registry poisoned")
55            .live
56            .len()
57    }
58
59    /// Whether no group is active.
60    #[must_use]
61    pub fn is_empty(&self) -> bool {
62        self.len() == 0
63    }
64
65    /// Kill one Unix process group best-effort.
66    #[cfg(unix)]
67    pub fn kill_group(process_group: i32) {
68        // SAFETY: negative pid targets the group created by `process_group(0)`.
69        unsafe {
70            libc::kill(-process_group, libc::SIGKILL);
71        }
72    }
73
74    /// No process groups are created by this engine on non-Unix platforms.
75    #[cfg(not(unix))]
76    pub fn kill_group(_process_group: i32) {}
77
78    /// Kill all active groups and latch teardown.
79    #[cfg(unix)]
80    pub fn kill_all(&self) -> usize {
81        let mut inner = self.inner.lock().expect("process-group registry poisoned");
82        let count = inner.live.len();
83        for process_group in &inner.live {
84            // SAFETY: each id is registered only after spawning a group leader.
85            unsafe {
86                libc::kill(-process_group, libc::SIGKILL);
87            }
88        }
89        inner.live.clear();
90        inner.torn_down = true;
91        count
92    }
93
94    /// Latch teardown on non-Unix platforms.
95    #[cfg(not(unix))]
96    pub fn kill_all(&self) -> usize {
97        let mut inner = self.inner.lock().expect("process-group registry poisoned");
98        inner.live.clear();
99        inner.torn_down = true;
100        0
101    }
102}