use std::sync::Arc;
use nmbrs_metrics::labels::Labels;
use nmbrs_runtime::activity::{Activity, ActivityConfig};
use nmbrs_runtime::adapter::{DriverAdapter, ExecutionError, OpDispenser, OpResult};
use nmbrs_runtime::opseq::{OpSequence, SequencerType};
use polydat::compile::assembly::{PolydatAssembler, WireRef};
use polydat::library::identity::Identity;
struct PanickingAdapter;
impl DriverAdapter for PanickingAdapter {
fn name(&self) -> &str {
"panicker"
}
fn map_op<'a>(
&'a self,
_template: &'a nmbrs_workload::model::ParsedOp,
_parent: Arc<dyn polydat::Kernel>,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<Box<dyn OpDispenser>, String>> + Send + 'a>,
> {
Box::pin(async move { Ok(Box::new(PanickingDispenser) as Box<dyn OpDispenser>) })
}
}
struct PanickingDispenser;
impl OpDispenser for PanickingDispenser {
fn execute<'a>(
&'a self,
cycle: u64,
_ctx: &'a nmbrs_runtime::adapter::ExecCtx<'a>,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<OpResult, ExecutionError>> + Send + 'a>,
> {
Box::pin(async move {
if cycle % 50 == 25 {
panic!("synthetic op panic at cycle {cycle}");
}
Ok(OpResult {
body: None,
skipped: false,
})
})
}
}
fn test_kernel() -> polydat::kernel::PolydatKernel {
let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
asm.add_node(
"id",
Box::new(Identity::new(polydat::ast::PortType::U64)),
vec![WireRef::input("cycle")],
);
asm.add_output("id", WireRef::node("id"));
asm.compile().unwrap()
}
#[tokio::test]
async fn panicking_op_does_not_drop_fibers() {
let ops = nmbrs_workload::parse::parse_ops("ops:\n step:\n stmt: \"x\"\n").unwrap();
let adapter: Arc<dyn DriverAdapter> = Arc::new(PanickingAdapter);
let config = ActivityConfig {
name: "panic_survive".into(),
cycles: 400,
concurrency: 4,
error_spec: ".*:warn,counter".into(),
..Default::default()
};
let seq = OpSequence::from_ops(ops, SequencerType::Bucket);
let activity = Activity::new(config, &Labels::of("session", "test"), seq);
let metrics = activity.shared_metrics();
activity
.run_with_driver(
adapter,
Arc::new(nmbrs_runtime::synthesis::OpBuilder::new(test_kernel())),
)
.await;
assert_eq!(
metrics.cycles_total.get(),
400,
"target concurrency must be maintained irrespective of error handling: \
a panicking op must not drop its fiber (ran {} of 400 cycles)",
metrics.cycles_total.get(),
);
assert!(
metrics.errors_total.get() >= 8,
"injected op panics should be counted as errors (got {})",
metrics.errors_total.get(),
);
}