in reverie-ptrace/src/trace/mod.rs [1129:1196]
fn group_exit(thread_count: usize) -> Result<(), Box<dyn std::error::Error + 'static>> {
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use std::time::Duration;
let (parent_pid, tracee) = trace(
move || {
let counter = Arc::new(AtomicUsize::new(0));
// Create a handful of threads that sleep forever.
let _threads = (0..thread_count)
.map(|_i| {
let counter = counter.clone();
thread::spawn(move || {
counter.fetch_add(1, Ordering::Relaxed);
thread::sleep(Duration::from_secs(60));
})
})
.collect::<Vec<_>>();
// Wait for each of the threads to actually get initialized.
while counter.load(Ordering::Relaxed) != thread_count {
thread::yield_now();
}
// All threads should be alive at this point. SYS_exit_group
// should force all threads to exit.
let _ = unsafe { libc::syscall(libc::SYS_exit_group, 42) };
unreachable!()
},
Options::PTRACE_O_EXITKILL
| Options::PTRACE_O_TRACEEXIT
| ptrace::Options::PTRACE_O_TRACECLONE,
)?;
tracee.resume(None)?;
let mut exited = Vec::new();
// Keep consuming events until everything has exited.
while let Some(wait) = wait_group(parent_pid)? {
match wait {
Wait::Stopped(tracee, _event) => {
tracee.resume(None)?;
}
Wait::Exited(pid, exit_status) => {
exited.push((pid, exit_status));
}
}
}
// The parent should have exited last.
assert_eq!(exited.pop(), Some((parent_pid, ExitStatus::Exited(42))));
// The only things left should be the threads that were spawned.
assert_eq!(exited.len(), thread_count);
// All others should have exited with the same exit status.
for (_pid, exit_status) in exited {
assert_eq!(exit_status, ExitStatus::Exited(42));
}
Ok(())
}