#[cfg(any(test, feature = "fuzz"))]
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
pub(super) fn spawn(
name: &str,
#[cfg(any(test, feature = "fuzz"))] tracker: &Arc<Tracker>,
run: impl FnOnce() + Send + 'static,
) {
#[cfg(any(test, feature = "fuzz"))]
{
*tracker.active.lock().expect("worker count not poisoned") += 1;
}
let guard = WorkerGuard {
#[cfg(any(test, feature = "fuzz"))]
tracker: tracker.clone(),
};
let result = thread::Builder::new().name(name.into()).spawn(move || {
run();
drop(guard);
});
if let Err(error) = result {
tracing::error!("could not start protocol worker: {}", error);
std::process::abort();
}
}
struct WorkerGuard {
#[cfg(any(test, feature = "fuzz"))]
tracker: Arc<Tracker>,
}
impl Drop for WorkerGuard {
fn drop(&mut self) {
if thread::panicking() {
tracing::error!("protocol worker panicked, aborting");
std::process::abort();
}
#[cfg(any(test, feature = "fuzz"))]
{
*self.tracker.active.lock().unwrap() -= 1;
self.tracker.stopped.notify_all();
}
}
}
#[cfg(any(test, feature = "fuzz"))]
#[derive(Default)]
pub(super) struct Tracker {
active: Mutex<usize>,
stopped: Condvar,
}
#[cfg(any(test, feature = "fuzz"))]
impl Tracker {
pub(super) fn wait_stopped(&self) {
use std::time::{Duration, Instant};
let deadline = Instant::now() + Duration::from_secs(5);
let mut active = self.active.lock().unwrap();
while *active != 0 {
let (count, timeout) = self
.stopped
.wait_timeout(active, deadline.saturating_duration_since(Instant::now()))
.unwrap();
active = count;
if timeout.timed_out() && *active != 0 {
drop(active);
panic!("protocol workers did not exit");
}
}
}
}