use std::io;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use crate::runtime::{Manifold, Runtime};
use crate::sys::Process;
#[derive(Clone, Debug)]
pub struct LaunchCtx {
pub cpu: u16,
pub shutdown: Option<Arc<AtomicBool>>,
}
pub struct Launcher<F> {
pub cpus: Vec<u16>,
pub shutdown: Option<Arc<AtomicBool>>,
pub build: F,
}
impl<F> Launcher<F> {
pub fn new(cpus: Vec<u16>, build: F) -> Self {
Self {
cpus,
shutdown: None,
build,
}
}
pub fn with_shutdown(mut self, shutdown: Arc<AtomicBool>) -> Self {
self.shutdown = Some(shutdown);
self
}
pub fn run<R>(self) -> io::Result<()>
where
F: Fn(LaunchCtx) -> io::Result<Runtime<R>> + Send + Sync,
R: Manifold,
{
let Self {
cpus,
shutdown,
build,
} = self;
let (last, rest) = cpus
.split_last()
.expect("Launcher::run requires at least one cpu");
Process::ignore_sigpipe();
let build_ref = &build;
std::thread::scope(|s| -> io::Result<()> {
let mut handles = Vec::with_capacity(rest.len());
for &cpu in rest {
let ctx = LaunchCtx {
cpu,
shutdown: shutdown.clone(),
};
let handle = s.spawn(move || -> io::Result<()> {
setup_worker(ctx.cpu)?;
let bundle = build_ref(ctx)?;
bundle.run_forever()
});
handles.push(handle);
}
let leader_ctx = LaunchCtx {
cpu: *last,
shutdown: shutdown.clone(),
};
setup_worker(leader_ctx.cpu)?;
let leader_bundle = build_ref(leader_ctx)?;
let leader_result = leader_bundle.run_forever();
for h in handles {
h.join()
.map_err(|_| io::Error::other("launcher worker panicked"))??;
}
leader_result
})
}
}
fn setup_worker(_cpu: u16) -> io::Result<()> {
Process::lock_memory();
Ok(())
}