#[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) {
let mut active = self.active.lock().unwrap();
while *active != 0 {
active = self.stopped.wait(active).unwrap();
}
}
}