use std::collections::HashSet;
use std::time::{Duration, Instant};
use crate::error::{Error, Result};
use crate::pid::{Pid, Signal};
use crate::process_iterator::ProcessIterator;
pub enum ChildrenMode {
Include,
Exclude,
}
impl Default for ChildrenMode {
fn default() -> Self {
ChildrenMode::Exclude
}
}
pub struct ProcessGroup {
target: Pid,
children_mode: ChildrenMode,
children: HashSet<Pid>,
last_update: Instant,
total_time: Duration,
cpu_usage: f64,
}
impl ProcessGroup {
pub fn new(pid: Pid, children_mode: ChildrenMode) -> Result<Self> {
let mut group = Self {
target: pid,
children: HashSet::new(),
children_mode,
cpu_usage: 0_f64,
last_update: Instant::now(),
total_time: Duration::from_secs(0),
};
group.update()?;
Ok(group)
}
pub fn update(&mut self) -> Result<()> {
if !self.target.alive() {
return Err(Error::DeadTarget);
}
let prev_time = self.total_time;
self.total_time = self.target.get_cputime();
if let ChildrenMode::Include = self.children_mode {
if let Ok(processes) = ProcessIterator::new() {
self.children.clear();
for process in processes {
if process != self.target && process.is_child_of(self.target) {
self.children.insert(process);
self.total_time += process.get_cputime();
}
}
}
}
let consumed = self.total_time - prev_time;
if !prev_time.is_zero() {
let elapsed = self.last_update.elapsed();
self.last_update = Instant::now();
let cpu_usage = consumed.as_secs_f64() / elapsed.as_secs_f64();
self.cpu_usage = 0.8 * self.cpu_usage + 0.2 * cpu_usage;
}
Ok(())
}
#[inline]
pub fn cpu_usage(&self) -> f64 {
self.cpu_usage
}
pub fn total_cpu_time(&self) -> Duration {
self.total_time
}
fn kill(&self, signal: &Signal) {
let _ = self.target.kill(signal);
if let ChildrenMode::Include = self.children_mode {
for child in &self.children {
let _ = child.kill(signal);
}
}
}
#[inline]
pub fn suspend(&self) {
self.kill(&Signal::SIGSTOP);
}
#[inline]
pub fn resume(&self) {
self.kill(&Signal::SIGCONT);
}
}