use std::sync::{Arc, Mutex};
use crate::execution_context::{self, ExecutionContext};
use crate::observer::{LogLevel, PhaseProgressUpdate, RunObserver};
#[derive(Clone, Debug, PartialEq)]
pub enum PhaseRecord {
Completed {
name: String,
labels: String,
duration_secs: f64,
},
Failed {
name: String,
labels: String,
error: String,
},
}
#[derive(Clone, Debug)]
pub struct ExecutionOutcome {
pub exec_id: u64,
pub phases: Vec<PhaseRecord>,
pub logs: Vec<String>,
}
pub struct HeadlessObserver {
phases: Mutex<Vec<PhaseRecord>>,
logs: Mutex<Vec<String>>,
}
impl HeadlessObserver {
pub fn new() -> Self {
Self {
phases: Mutex::new(Vec::new()),
logs: Mutex::new(Vec::new()),
}
}
fn take(&self) -> (Vec<PhaseRecord>, Vec<String>) {
let p = std::mem::take(&mut *self.phases.lock().unwrap_or_else(|e| e.into_inner()));
let l = std::mem::take(&mut *self.logs.lock().unwrap_or_else(|e| e.into_inner()));
(p, l)
}
}
impl Default for HeadlessObserver {
fn default() -> Self {
Self::new()
}
}
impl RunObserver for HeadlessObserver {
fn phase_starting(
&self,
_scene_node_id: crate::scene_tree::SceneNodeId,
_name: &str,
_labels: &str,
_ops: usize,
_cycles: u64,
_conc: usize,
) {
}
fn phase_completed(
&self,
_scene_node_id: crate::scene_tree::SceneNodeId,
name: &str,
labels: &str,
duration_secs: f64,
) {
self.phases
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(PhaseRecord::Completed {
name: name.to_string(),
labels: labels.to_string(),
duration_secs,
});
}
fn phase_failed(
&self,
_scene_node_id: crate::scene_tree::SceneNodeId,
name: &str,
labels: &str,
error: &str,
) {
self.phases
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(PhaseRecord::Failed {
name: name.to_string(),
labels: labels.to_string(),
error: error.to_string(),
});
}
fn phase_progress(&self, _update: &PhaseProgressUpdate) {}
fn run_finished(&self) {}
fn log(&self, _level: LogLevel, message: &str) {
self.logs
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(message.to_string());
}
}
pub async fn run_workload_headless(args: &[String]) -> (ExecutionOutcome, Result<(), String>) {
let obs = Arc::new(HeadlessObserver::new());
let ctx = ExecutionContext::with_observer(obs.clone() as Arc<dyn RunObserver>);
let exec_id = ctx.exec_id;
let result = execution_context::scope(
ctx,
crate::runner::run_with_observer(args, obs.clone() as Arc<dyn RunObserver>),
)
.await;
let (phases, logs) = obs.take();
(
ExecutionOutcome {
exec_id,
phases,
logs,
},
result,
)
}