use metricus_macros::{counter, span};
use std::sync::{Arc, Barrier};
use std::thread;
const THREADS_PER_FN: usize = 16;
const FN_COUNT: usize = 16;
macro_rules! define_counter_fns {
($($name:ident),* $(,)?) => {
$(
#[counter(measurement = "concurrent_first_touch_counter", tags(test = "race"))]
fn $name() {}
)*
const COUNTER_FNS: [fn(); FN_COUNT] = [$($name),*];
};
}
macro_rules! define_span_fns {
($($name:ident),* $(,)?) => {
$(
#[span(measurement = "concurrent_first_touch_span", tags(test = "race"))]
fn $name() {}
)*
const SPAN_FNS: [fn(); FN_COUNT] = [$($name),*];
};
}
define_counter_fns!(
counter_fn_00,
counter_fn_01,
counter_fn_02,
counter_fn_03,
counter_fn_04,
counter_fn_05,
counter_fn_06,
counter_fn_07,
counter_fn_08,
counter_fn_09,
counter_fn_10,
counter_fn_11,
counter_fn_12,
counter_fn_13,
counter_fn_14,
counter_fn_15,
);
define_span_fns!(
span_fn_00, span_fn_01, span_fn_02, span_fn_03, span_fn_04, span_fn_05, span_fn_06, span_fn_07, span_fn_08,
span_fn_09, span_fn_10, span_fn_11, span_fn_12, span_fn_13, span_fn_14, span_fn_15,
);
fn race_all(fns: &[fn(); FN_COUNT]) {
let mut handles = Vec::with_capacity(FN_COUNT * THREADS_PER_FN);
for f in fns {
let f = *f;
let barrier = Arc::new(Barrier::new(THREADS_PER_FN));
for _ in 0..THREADS_PER_FN {
let barrier = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
barrier.wait();
f();
}));
}
}
for handle in handles {
handle.join().expect(
"thread panicked -- likely the LazyCell concurrent first-touch race \
(metricus_macros should back #[counter]/#[span] with std::sync::LazyLock, not core::cell::LazyCell)",
);
}
}
#[test]
fn concurrent_first_touch_counters_do_not_panic() {
race_all(&COUNTER_FNS);
}
#[test]
fn concurrent_first_touch_spans_do_not_panic() {
race_all(&SPAN_FNS);
}