#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::match_wild_err_arm)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::ignored_unit_patterns)]
#![allow(clippy::for_kv_map)]
#![allow(clippy::double_ended_iterator_last)]
#![allow(clippy::single_match_else)]
use std::{
collections::HashMap,
process::{Command, exit},
time::{Duration, Instant},
};
use fork::{Fork, fork, waitpid};
#[derive(Debug)]
struct ProcessInfo {
name: String,
started_at: Instant,
restarts: u32,
}
fn main() {
println!("🚀 Starting Process Supervisor\n");
let mut supervised: HashMap<Fork, ProcessInfo> = HashMap::new();
for i in 1..=3 {
match spawn_worker(i, &mut supervised) {
Ok(_) => println!("✅ Worker {} spawned", i),
Err(e) => eprintln!("❌ Failed to spawn worker {}: {}", i, e),
}
}
println!("\n📊 Supervisor managing {} processes\n", supervised.len());
loop {
if supervised.is_empty() {
println!("✅ All workers completed. Supervisor exiting.");
break;
}
let mut exited = Vec::new();
for (fork_result, info) in &supervised {
if let Some(pid) = fork_result.child_pid() {
println!("⏳ Checking worker '{}' (PID: {})", info.name, pid);
}
}
std::thread::sleep(Duration::from_millis(500));
for (fork_result, _info) in &supervised {
if let Some(pid) = fork_result.child_pid() {
match waitpid(pid) {
Ok(_) => {
exited.push(*fork_result);
}
Err(_) => {
}
}
}
}
for fork_result in exited {
if let Some(info) = supervised.remove(&fork_result) {
let pid = fork_result.child_pid().unwrap();
let uptime = info.started_at.elapsed();
println!(
"\n💀 Worker '{}' (PID: {}) exited after {:.2}s",
info.name,
pid,
uptime.as_secs_f64()
);
if info.restarts < 3 {
println!("🔄 Restarting worker '{}'...", info.name);
let worker_num: u32 = info
.name
.split('-')
.last()
.and_then(|s| s.parse().ok())
.unwrap_or(0);
match restart_worker(worker_num, &mut supervised, info.restarts + 1) {
Ok(_) => println!("✅ Worker '{}' restarted", info.name),
Err(e) => eprintln!("❌ Failed to restart: {}", e),
}
} else {
println!(
"⚠️ Worker '{}' reached max restarts, not restarting",
info.name
);
}
}
}
}
}
fn spawn_worker(id: u32, supervised: &mut HashMap<Fork, ProcessInfo>) -> std::io::Result<()> {
match fork()? {
result @ Fork::Parent(_) => {
supervised.insert(
result,
ProcessInfo {
name: format!("worker-{}", id),
started_at: Instant::now(),
restarts: 0,
},
);
Ok(())
}
Fork::Child => {
println!("👷 Worker {} starting work...", id);
Command::new("sleep")
.arg(format!("{}", id))
.status()
.expect("Failed to execute sleep");
println!("✅ Worker {} completed", id);
exit(0);
}
}
}
fn restart_worker(
id: u32,
supervised: &mut HashMap<Fork, ProcessInfo>,
restart_count: u32,
) -> std::io::Result<()> {
match fork()? {
result @ Fork::Parent(_) => {
supervised.insert(
result,
ProcessInfo {
name: format!("worker-{}", id),
started_at: Instant::now(),
restarts: restart_count,
},
);
Ok(())
}
Fork::Child => {
println!(
"👷 Worker {} (restart #{}) starting work...",
id, restart_count
);
Command::new("sleep")
.arg("1")
.status()
.expect("Failed to execute sleep");
println!("✅ Worker {} (restart #{}) completed", id, restart_count);
exit(0);
}
}
}