use std::process::Child;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{LazyLock, Mutex};
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum JobStatus {
Running,
Done(i32), }
pub struct Job {
pub id: u32,
pub pid: u32,
pub cmdline: String,
pub child: Child,
pub status: JobStatus,
}
static NEXT_JOB_ID: AtomicU32 = AtomicU32::new(1);
static JOBS: LazyLock<Mutex<Vec<Job>>> = LazyLock::new(|| Mutex::new(Vec::new()));
pub fn add_job(child: Child, cmdline: String) -> u32 {
let id = NEXT_JOB_ID.fetch_add(1, Ordering::SeqCst);
let pid = child.id();
if let Ok(mut jobs) = JOBS.lock() {
jobs.push(Job {
id,
pid,
cmdline,
child,
status: JobStatus::Running,
});
}
#[cfg(unix)]
unsafe {
libc::kill(pid as i32, libc::SIGCONT);
}
id
}
fn reap() -> usize {
let mut running = 0;
if let Ok(mut jobs) = JOBS.lock() {
for job in jobs.iter_mut() {
if job.status == JobStatus::Running {
match job.child.try_wait() {
Ok(Some(status)) => {
job.status = JobStatus::Done(status.code().unwrap_or(-1));
}
Ok(None) => running += 1,
Err(_) => {
running += 1;
}
}
}
}
}
running
}
pub fn running_count() -> usize {
reap()
}
pub fn list_jobs() -> Vec<(u32, u32, String, JobStatus)> {
reap();
if let Ok(mut jobs) = JOBS.lock() {
let snapshot: Vec<_> = jobs
.iter()
.map(|j| (j.id, j.pid, j.cmdline.clone(), j.status))
.collect();
jobs.retain(|j| j.status == JobStatus::Running); snapshot
} else {
Vec::new()
}
}
pub fn kill_job(job_id: u32) -> bool {
if let Ok(mut jobs) = JOBS.lock() {
if let Some(job) = jobs.iter_mut().find(|j| j.id == job_id) {
return job.child.kill().is_ok();
}
}
false
}