fluxbench_core/worker/
main_loop.rs1use super::flusher::Flusher;
10use super::panic_report;
11use super::stream::{SharedStream, StreamSink};
12use super::transport::{self, IpcReader};
13use crate::measure::{cycles_overhead, pin_to_cpu, timer_overhead_ns};
14use crate::shutdown::{install_sigterm_handler, shutdown_requested};
15use crate::{Bencher, BenchmarkDef, MeasureConfig, run_benchmark_loop};
16use fluxbench_ipc::{
17 BenchmarkConfig, FailureKind, PIN_CPU_ENV, SupervisorCommand, WorkerCapabilities, WorkerMessage,
18};
19
20fn pin_from_env() {
24 let Ok(value) = std::env::var(PIN_CPU_ENV) else {
25 return;
26 };
27 match value.parse::<usize>() {
28 Ok(cpu) => {
29 if let Err(e) = pin_to_cpu(cpu) {
30 eprintln!(
31 "fluxbench: warning: pin to CPU {cpu} failed: {e}. Timings run unpinned."
32 );
33 }
34 }
35 Err(_) => eprintln!(
36 "fluxbench: warning: invalid {PIN_CPU_ENV}={value:?} (expected a CPU index). Timings run unpinned."
37 ),
38 }
39}
40
41pub struct WorkerMain {
43 reader: IpcReader,
44 stream: SharedStream,
45}
46
47impl WorkerMain {
48 pub fn new() -> Self {
50 let (reader, writer) = transport::open();
51 Self {
52 reader,
53 stream: SharedStream::new(writer),
54 }
55 }
56
57 pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
59 install_sigterm_handler();
60 panic_report::install();
61
62 self.stream
63 .send(&WorkerMessage::Hello(WorkerCapabilities::default()))?;
64
65 let _flusher = Flusher::start(self.stream.clone());
67
68 pin_from_env();
70
71 let _ = timer_overhead_ns();
73 let _ = cycles_overhead();
74
75 loop {
76 if shutdown_requested() {
77 break;
78 }
79
80 let command: SupervisorCommand = self.reader.read()?;
81
82 match command {
83 SupervisorCommand::Run { bench_id, config } => {
84 self.run_benchmark(&bench_id, &config)?;
85 if shutdown_requested() {
86 break;
87 }
88 }
89 SupervisorCommand::Abort | SupervisorCommand::Shutdown => break,
90 SupervisorCommand::Ping => {}
91 }
92 }
93
94 Ok(())
95 }
96
97 fn run_benchmark(
99 &mut self,
100 bench_id: &str,
101 config: &BenchmarkConfig,
102 ) -> Result<(), Box<dyn std::error::Error>> {
103 let bench = inventory::iter::<BenchmarkDef>
104 .into_iter()
105 .find(|b| b.id == bench_id);
106
107 let Some(bench) = bench else {
108 self.stream.send(&WorkerMessage::Failure {
109 kind: FailureKind::Unknown,
110 message: format!(
111 "benchmark {bench_id:?} not found in the worker binary. Rebuild the bench binary and run again."
112 ),
113 backtrace: None,
114 })?;
115 return Ok(());
116 };
117
118 self.stream.begin(bench_id);
119 panic_report::clear();
120
121 let sink = StreamSink(self.stream.clone());
122 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
123 let bencher = Bencher::with_config(MeasureConfig::from(config)).with_sample_sink(sink);
124 run_benchmark_loop(bencher, |b| (bench.runner_fn)(b))
125 }));
126
127 let last = match result {
128 Ok(Ok(bench_result)) => {
129 if bench_result.truncated {
130 let reason = if shutdown_requested() {
131 "stop requested"
132 } else {
133 "deadline reached"
134 };
135 eprintln!(
136 "fluxbench: warning: {bench_id}: {reason}, {} of {} samples kept",
137 bench_result.samples.len(),
138 config.sample_count
139 );
140 }
141 WorkerMessage::Complete {
143 total_iterations: bench_result.iterations,
144 total_duration_nanos: bench_result.total_time_ns,
145 throughput: bench_result.throughput,
146 }
147 }
148 Ok(Err(error)) => WorkerMessage::Failure {
149 kind: FailureKind::Unknown,
150 message: error.to_string(),
151 backtrace: None,
152 },
153 Err(payload) => match panic_report::take() {
154 Some(report) => WorkerMessage::Failure {
155 kind: FailureKind::Panic,
156 message: report.message_with_location(),
157 backtrace: Some(report.backtrace),
158 },
159 None => WorkerMessage::Failure {
160 kind: FailureKind::Panic,
161 message: panic_report::payload_message(payload.as_ref()),
162 backtrace: None,
163 },
164 },
165 };
166
167 self.stream.end(&last)?;
168 Ok(())
169 }
170}
171
172impl Default for WorkerMain {
173 fn default() -> Self {
174 Self::new()
175 }
176}