1use 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 static WORKER_INDEX: Cell<Option<i64>> = const { Cell::new(None) };
13}
14
15pub 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
29pub fn fan_worker_set_index(i: Option<i64>) {
31 WORKER_INDEX.with(|c| c.set(i));
32}
33
34pub 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}