use std::collections::BTreeSet;
use std::io;
use super::ProcHandle;
use crate::model;
impl ProcHandle {
pub fn signal_masks(&self) -> io::Result<model::status::Status> {
let mut status = self.source.read_status()?;
status.sig_blk = self
.thread_blocked_masks()
.map(|masks| intersect_blocked_masks(&masks))
.or(status.sig_blk);
Ok(status)
}
fn thread_blocked_masks(&self) -> Option<Vec<BTreeSet<usize>>> {
let tids = self.source.list_tids().ok()?;
if let Ok(status) = self.source.read_status() {
if let Some(n) = status.threads {
if n > tids.len() {
eprintln!(
"warning: process had {} threads but only {} available; \
blocked masks may be incomplete",
n,
tids.len()
);
}
}
}
let tid_count = tids.len();
let mut masks = Vec::new();
for tid in tids {
let Ok(status) = self.source.read_tid_status(tid) else {
continue;
};
if let Some(sig_blk) = status.sig_blk {
masks.push(sig_blk);
}
}
if !masks.is_empty() && masks.len() < tid_count {
eprintln!(
"warning: read blocked mask for {} of {} threads; \
blocked-signal intersection may be incomplete",
masks.len(),
tid_count
);
}
if masks.is_empty() {
None
} else {
Some(masks)
}
}
}
fn intersect_blocked_masks(masks: &[BTreeSet<usize>]) -> BTreeSet<usize> {
let Some(first) = masks.first() else {
return BTreeSet::new();
};
masks[1..].iter().fold(first.clone(), |acc, m| {
acc.intersection(m).copied().collect()
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::status::parse_signal_mask;
#[test]
fn intersect_single_mask() {
let mask = parse_signal_mask("3").unwrap(); let result = intersect_blocked_masks(std::slice::from_ref(&mask));
assert_eq!(result, mask);
}
#[test]
fn intersect_two_masks() {
let a = parse_signal_mask("7").unwrap(); let b = parse_signal_mask("5").unwrap(); let result = intersect_blocked_masks(&[a, b]);
assert!(result.contains(&1));
assert!(!result.contains(&2));
assert!(result.contains(&3));
}
#[test]
fn intersect_empty_list() {
let result = intersect_blocked_masks(&[]);
assert!(result.is_empty());
}
}