Skip to main content

stryke/
parallel_trace.rs

1//! Optional stderr tracing for `mysync` mutations under `trace { ... }` and `fan`.
2
3use std::cell::Cell;
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use crate::value::PerlValue;
7
8static TRACE_ENABLED: AtomicBool = AtomicBool::new(false);
9
10thread_local! {
11    /// Fan / parallel worker label (`$_` index). `None` when not inside a labeled parallel block.
12    static WORKER_INDEX: Cell<Option<i64>> = const { Cell::new(None) };
13}
14
15/// Begin a `trace { ... }` region (all threads see the same flag).
16pub fn trace_enter() {
17    TRACE_ENABLED.store(true, Ordering::SeqCst);
18}
19
20pub fn trace_leave() {
21    TRACE_ENABLED.store(false, Ordering::SeqCst);
22}
23
24#[inline]
25pub fn is_enabled() -> bool {
26    TRACE_ENABLED.load(Ordering::SeqCst)
27}
28
29/// Set the current worker index for `fan [N] { }` (typically `$_` as integer).
30pub fn fan_worker_set_index(i: Option<i64>) {
31    WORKER_INDEX.with(|c| c.set(i));
32}
33
34/// Emit one line for a scalar mutation (mysync / atomic scalar).
35pub fn emit_scalar_mutation(var: &str, old: &PerlValue, new: &PerlValue) {
36    if !is_enabled() {
37        return;
38    }
39    match WORKER_INDEX.with(|c| c.get()) {
40        Some(i) => eprintln!("[thread {}] ${}: {} → {}", i, var, old, new),
41        None => eprintln!("[main] ${}: {} → {}", var, old, new),
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn emit_scalar_mutation_noops_when_trace_disabled() {
51        fan_worker_set_index(Some(0));
52        emit_scalar_mutation("x", &PerlValue::integer(1), &PerlValue::integer(2));
53        fan_worker_set_index(None);
54    }
55
56    #[test]
57    fn fan_worker_set_index_none_is_safe_after_some() {
58        fan_worker_set_index(Some(42));
59        fan_worker_set_index(None);
60    }
61}