use super::flusher::Flusher;
use super::panic_report;
use super::stream::{SharedStream, StreamSink};
use super::transport::{self, IpcReader};
use crate::measure::{cycles_overhead, pin_to_cpu, timer_overhead_ns};
use crate::shutdown::{install_sigterm_handler, shutdown_requested};
use crate::{Bencher, BenchmarkDef, MeasureConfig, run_benchmark_loop};
use fluxbench_ipc::{
BenchmarkConfig, FailureKind, PIN_CPU_ENV, SupervisorCommand, WorkerCapabilities, WorkerMessage,
};
fn pin_from_env() {
let Ok(value) = std::env::var(PIN_CPU_ENV) else {
return;
};
match value.parse::<usize>() {
Ok(cpu) => {
if let Err(e) = pin_to_cpu(cpu) {
eprintln!(
"fluxbench: warning: pin to CPU {cpu} failed: {e}. Timings run unpinned."
);
}
}
Err(_) => eprintln!(
"fluxbench: warning: invalid {PIN_CPU_ENV}={value:?} (expected a CPU index). Timings run unpinned."
),
}
}
pub struct WorkerMain {
reader: IpcReader,
stream: SharedStream,
}
impl WorkerMain {
pub fn new() -> Self {
let (reader, writer) = transport::open();
Self {
reader,
stream: SharedStream::new(writer),
}
}
pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
install_sigterm_handler();
panic_report::install();
self.stream
.send(&WorkerMessage::Hello(WorkerCapabilities::default()))?;
let _flusher = Flusher::start(self.stream.clone());
pin_from_env();
let _ = timer_overhead_ns();
let _ = cycles_overhead();
loop {
if shutdown_requested() {
break;
}
let command: SupervisorCommand = self.reader.read()?;
match command {
SupervisorCommand::Run { bench_id, config } => {
self.run_benchmark(&bench_id, &config)?;
if shutdown_requested() {
break;
}
}
SupervisorCommand::Abort | SupervisorCommand::Shutdown => break,
SupervisorCommand::Ping => {}
}
}
Ok(())
}
fn run_benchmark(
&mut self,
bench_id: &str,
config: &BenchmarkConfig,
) -> Result<(), Box<dyn std::error::Error>> {
let bench = inventory::iter::<BenchmarkDef>
.into_iter()
.find(|b| b.id == bench_id);
let Some(bench) = bench else {
self.stream.send(&WorkerMessage::Failure {
kind: FailureKind::Unknown,
message: format!(
"benchmark {bench_id:?} not found in the worker binary. Rebuild the bench binary and run again."
),
backtrace: None,
})?;
return Ok(());
};
self.stream.begin(bench_id);
panic_report::clear();
let sink = StreamSink(self.stream.clone());
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let bencher = Bencher::with_config(MeasureConfig::from(config)).with_sample_sink(sink);
run_benchmark_loop(bencher, |b| (bench.runner_fn)(b))
}));
let last = match result {
Ok(Ok(bench_result)) => {
if bench_result.truncated {
let reason = if shutdown_requested() {
"stop requested"
} else {
"deadline reached"
};
eprintln!(
"fluxbench: warning: {bench_id}: {reason}, {} of {} samples kept",
bench_result.samples.len(),
config.sample_count
);
}
WorkerMessage::Complete {
total_iterations: bench_result.iterations,
total_duration_nanos: bench_result.total_time_ns,
throughput: bench_result.throughput,
}
}
Ok(Err(error)) => WorkerMessage::Failure {
kind: FailureKind::Unknown,
message: error.to_string(),
backtrace: None,
},
Err(payload) => match panic_report::take() {
Some(report) => WorkerMessage::Failure {
kind: FailureKind::Panic,
message: report.message_with_location(),
backtrace: Some(report.backtrace),
},
None => WorkerMessage::Failure {
kind: FailureKind::Panic,
message: panic_report::payload_message(payload.as_ref()),
backtrace: None,
},
},
};
self.stream.end(&last)?;
Ok(())
}
}
impl Default for WorkerMain {
fn default() -> Self {
Self::new()
}
}