geographdb-core 0.4.0

Geometric graph database core - 3D spatial indexing for code analysis
Documentation
//! Telemetry module for race detection and loop guards
//!
//! This module provides runtime verification for:
//! - Race condition detection in concurrent access patterns
//! - Loop/infinite recursion detection
//!
//! Enabled via the `telemetry` feature flag.

#[cfg(feature = "telemetry")]
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};

/// Operation tracer for detecting redundant calls
#[cfg(feature = "telemetry")]
pub struct OpTracer {
    call_counts: dashmap::DashMap<String, AtomicU64>,
}

#[cfg(feature = "telemetry")]
impl OpTracer {
    pub fn new() -> Self {
        Self {
            call_counts: dashmap::DashMap::new(),
        }
    }

    pub fn trace(&self, op: &str, file: &str, line: u32) {
        let key = format!("{}:{}:{}", file, line, op);
        let count = self
            .call_counts
            .entry(key.clone())
            .or_insert(AtomicU64::new(0));
        let new_count = count.fetch_add(1, Ordering::SeqCst) + 1;

        // Warn on suspicious patterns
        if new_count > 100 {
            tracing::warn!(
                "Operation {} called {} times - possible loop",
                key,
                new_count
            );
        }
    }
}

#[cfg(feature = "telemetry")]
impl Default for OpTracer {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(not(feature = "telemetry"))]
impl Default for OpTracer {
    fn default() -> Self {
        Self::new()
    }
}

/// Loop guard for preventing infinite recursion/iteration
#[cfg(feature = "telemetry")]
pub struct LoopGuard {
    name: &'static str,
    max_iterations: usize,
    counter: AtomicUsize,
}

#[cfg(feature = "telemetry")]
impl LoopGuard {
    pub fn new(name: &'static str, max_iterations: usize) -> Self {
        Self {
            name,
            max_iterations,
            counter: AtomicUsize::new(0),
        }
    }

    pub fn check(&self) -> Result<(), String> {
        let count = self.counter.fetch_add(1, Ordering::SeqCst);
        if count >= self.max_iterations {
            return Err(format!(
                "LoopGuard '{}' exceeded max iterations: {}",
                self.name, self.max_iterations
            ));
        }
        Ok(())
    }
}

#[cfg(feature = "telemetry")]
impl Drop for LoopGuard {
    fn drop(&mut self) {
        let count = self.counter.load(Ordering::SeqCst);
        if count > self.max_iterations / 2 {
            tracing::info!("LoopGuard '{}' iterations: {}", self.name, count);
        }
    }
}

/// No-op implementations when telemetry is disabled
#[cfg(not(feature = "telemetry"))]
pub struct OpTracer;

#[cfg(not(feature = "telemetry"))]
impl OpTracer {
    pub fn new() -> Self {
        Self
    }
    pub fn trace(&self, _op: &str, _file: &str, _line: u32) {}
}

#[cfg(not(feature = "telemetry"))]
pub struct LoopGuard;

#[cfg(not(feature = "telemetry"))]
impl LoopGuard {
    pub fn new(_name: &'static str, _max_iterations: usize) -> Self {
        Self
    }
    pub fn check(&self) -> Result<(), String> {
        Ok(())
    }
}