use crate::utils::{GcIntegration, GcIntegrationConfig, GcEnvironment, scan_value_for_gc_integration};
use crate::utils::gc::{gc_collect, gc_stats, gc_debug_info, GcStats, GcDebugInfo};
use crate::eval::{Value, ThreadSafeEnvironment, Evaluator, Continuation, StackTrace};
use crate::diagnostics::{Error, Result, Span};
use std::sync::{Arc, RwLock, Mutex};
use std::collections::HashMap;
use std::time::{Instant, Duration};
use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
#[derive(Debug)]
pub struct GcCoordinator {
integration: Arc<GcIntegration>,
active_sessions: RwLock<HashMap<SessionId, EvaluationSession>>,
global_roots: RwLock<Vec<GlobalRoot>>,
stats_collector: RwLock<GcStatsCollector>,
config: GcCoordinatorConfig,
next_session_id: AtomicU64,
}
#[derive(Debug, Clone)]
pub struct GcCoordinatorConfig {
pub auto_root_detection: bool,
pub gc_during_evaluation: bool,
pub max_gc_interval_ms: u64,
pub collect_statistics: bool,
pub preserve_continuations: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SessionId(u64);
#[derive(Debug)]
pub struct EvaluationSession {
pub id: SessionId,
pub environment_stack: Vec<Arc<ThreadSafeEnvironment>>,
pub continuations: Vec<Arc<Continuation>>,
pub stack_trace: Option<StackTrace>,
pub start_time: Instant,
pub active: AtomicBool,
}
#[derive(Debug, Clone)]
pub enum GlobalRoot {
Environment(Arc<ThreadSafeEnvironment>),
Value(Value),
StdlibBinding {
name: String,
value: Value
},
ModuleRoot {
module_id: String,
environment: Arc<ThreadSafeEnvironment>
},
}
#[derive(Debug)]
pub struct GcStatsCollector {
pub collection_history: Vec<GcCollectionEvent>,
pub total_collections: u64,
pub total_gc_time: Duration,
pub average_collection_time: Duration,
pub peak_memory_usage: usize,
}
#[derive(Debug, Clone)]
pub struct GcCollectionEvent {
pub timestamp: Instant,
pub stats: GcStats,
pub active_sessions: usize,
pub roots_scanned: usize,
}
impl GcCoordinator {
pub fn new(config: GcCoordinatorConfig) -> Result<Self> {
let gc_config = GcIntegrationConfig {
auto_register_environments: config.auto_root_detection,
preserve_stack_traces: true,
gc_aware_macros: true,
gc_threshold_size: 512, };
let integration = Arc::new(GcIntegration::new(gc_config));
Ok(Self {
integration,
active_sessions: RwLock::new(HashMap::new()),
global_roots: RwLock::new(Vec::new()),
stats_collector: RwLock::new(GcStatsCollector::new()),
config,
next_session_id: AtomicU64::new(1),
})
}
pub fn with_default_config() -> Result<Self> {
Self::new(GcCoordinatorConfig::default())
}
pub fn start_session(&self, initial_environment: Arc<ThreadSafeEnvironment>) -> SessionId {
let session_id = SessionId(self.next_session_id.fetch_add(1, Ordering::SeqCst));
let session = EvaluationSession {
id: session_id,
environment_stack: vec![initial_environment.clone()],
continuations: Vec::new(),
stack_trace: None,
start_time: Instant::now(),
active: AtomicBool::new(true),
};
if self.config.auto_root_detection {
self.integration.register_continuation_root(
crate::utils::gc::ObjectId::new(session_id.0)
);
}
if let Ok(mut sessions) = self.active_sessions.write() {
sessions.insert(session_id, session);
}
session_id
}
pub fn end_session(&self, session_id: SessionId) {
if let Ok(mut sessions) = self.active_sessions.write() {
if let Some(session) = sessions.remove(&session_id) {
session.active.store(false, Ordering::SeqCst);
if self.config.auto_root_detection {
self.integration.unregister_continuation_root(
crate::utils::gc::ObjectId::new(session_id.0)
);
}
}
}
}
pub fn push_environment(&self, session_id: SessionId, env: Arc<ThreadSafeEnvironment>) {
if let Ok(mut sessions) = self.active_sessions.write() {
if let Some(session) = sessions.get_mut(&session_id) {
session.environment_stack.push(env);
}
}
}
pub fn pop_environment(&self, session_id: SessionId) -> Option<Arc<ThreadSafeEnvironment>> {
if let Ok(mut sessions) = self.active_sessions.write() {
if let Some(session) = sessions.get_mut(&session_id) {
session.environment_stack.pop()
} else {
None
}
} else {
None
}
}
pub fn register_continuation(&self, session_id: SessionId, continuation: Arc<Continuation>) {
if let Ok(mut sessions) = self.active_sessions.write() {
if let Some(session) = sessions.get_mut(&session_id) {
session.continuations.push(continuation);
}
}
}
pub fn update_stack_trace(&self, session_id: SessionId, stack_trace: StackTrace) {
if let Ok(mut sessions) = self.active_sessions.write() {
if let Some(session) = sessions.get_mut(&session_id) {
session.stack_trace = Some(stack_trace);
}
}
}
pub fn add_global_root(&self, root: GlobalRoot) {
if let Ok(mut roots) = self.global_roots.write() {
roots.push(root);
}
}
pub fn comprehensive_root_scan(&self) -> ComprehensiveRootScanResult {
let mut session_roots = Vec::new();
let mut continuation_roots = Vec::new();
if let Ok(sessions) = self.active_sessions.read() {
for session in sessions.values() {
if session.active.load(Ordering::SeqCst) {
for env in &session.environment_stack {
let gc_env = GcEnvironment::new(env.clone());
session_roots.extend(gc_env.scan_for_gc_roots());
}
continuation_roots.extend(
session.continuations.iter().map(|c| c.id)
);
}
}
}
let global_roots = if let Ok(roots) = self.global_roots.read() {
roots.clone()
} else {
Vec::new()
};
ComprehensiveRootScanResult {
session_roots,
continuation_roots,
global_roots,
active_session_count: self.active_session_count(),
}
}
pub fn maybe_collect(&self) -> Option<GcCollectionResult> {
if !self.config.gc_during_evaluation {
return None;
}
if let Ok(stats) = self.stats_collector.read() {
if let Some(last_event) = stats.collection_history.last() {
let elapsed = last_event.timestamp.elapsed();
if elapsed.as_millis() < self.config.max_gc_interval_ms as u128 {
return None; }
}
}
Some(self.force_collect())
}
pub fn force_collect(&self) -> GcCollectionResult {
let start_time = Instant::now();
let root_scan = self.comprehensive_root_scan();
gc_collect();
let gc_stats = gc_stats();
let debug_info = gc_debug_info();
let collection_time = start_time.elapsed();
let result = GcCollectionResult {
collection_time,
roots_scanned: root_scan.session_roots.len() + root_scan.global_roots.len(),
active_sessions: root_scan.active_session_count,
gc_stats: gc_stats.last().cloned(),
debug_info,
};
if self.config.collect_statistics {
if let Ok(mut collector) = self.stats_collector.write() {
collector.record_collection(&result, &root_scan);
}
}
result
}
pub fn active_session_count(&self) -> usize {
if let Ok(sessions) = self.active_sessions.read() {
sessions.values()
.filter(|s| s.active.load(Ordering::SeqCst))
.count()
} else {
0
}
}
pub fn get_statistics(&self) -> Option<GcStatsSummary> {
if !self.config.collect_statistics {
return None;
}
if let Ok(collector) = self.stats_collector.read() {
Some(collector.summarize())
} else {
None
}
}
pub fn config(&self) -> &GcCoordinatorConfig {
&self.config
}
pub fn debug_info(&self) -> GcCoordinatorDebugInfo {
GcCoordinatorDebugInfo {
active_sessions: self.active_session_count(),
global_roots: self.global_roots.read()
.map(|r| r.len()).unwrap_or(0),
gc_debug_info: gc_debug_info(),
statistics: self.get_statistics(),
}
}
}
#[derive(Debug)]
pub struct ComprehensiveRootScanResult {
pub session_roots: Vec<Value>,
pub continuation_roots: Vec<u64>,
pub global_roots: Vec<GlobalRoot>,
pub active_session_count: usize,
}
#[derive(Debug)]
pub struct GcCollectionResult {
pub collection_time: Duration,
pub roots_scanned: usize,
pub active_sessions: usize,
pub gc_stats: Option<GcStats>,
pub debug_info: GcDebugInfo,
}
#[derive(Debug, Clone)]
pub struct GcStatsSummary {
pub total_collections: u64,
pub total_gc_time: Duration,
pub average_collection_time: Duration,
pub peak_memory_usage: usize,
pub recent_events: Vec<GcCollectionEvent>,
}
#[derive(Debug)]
pub struct GcCoordinatorDebugInfo {
pub active_sessions: usize,
pub global_roots: usize,
pub gc_debug_info: GcDebugInfo,
pub statistics: Option<GcStatsSummary>,
}
impl Default for GcStatsCollector {
fn default() -> Self {
Self::new()
}
}
impl GcStatsCollector {
pub fn new() -> Self {
Self {
collection_history: Vec::new(),
total_collections: 0,
total_gc_time: Duration::from_secs(0),
average_collection_time: Duration::from_secs(0),
peak_memory_usage: 0,
}
}
pub fn record_collection(
&mut self,
result: &GcCollectionResult,
_root_scan: &ComprehensiveRootScanResult,
) {
let event = GcCollectionEvent {
timestamp: Instant::now(),
stats: result.gc_stats.clone().unwrap_or_default(),
active_sessions: result.active_sessions,
roots_scanned: result.roots_scanned,
};
self.collection_history.push(event);
self.total_collections += 1;
self.total_gc_time += result.collection_time;
self.peak_memory_usage = self.peak_memory_usage.max(
result.debug_info.total_memory
);
self.average_collection_time = self.total_gc_time / self.total_collections.max(1) as u32;
if self.collection_history.len() > 100 {
self.collection_history.remove(0);
}
}
pub fn summarize(&self) -> GcStatsSummary {
let recent_events = self.collection_history
.iter()
.rev()
.take(10)
.cloned()
.collect();
GcStatsSummary {
total_collections: self.total_collections,
total_gc_time: self.total_gc_time,
average_collection_time: self.average_collection_time,
peak_memory_usage: self.peak_memory_usage,
recent_events,
}
}
}
impl Default for GcCoordinatorConfig {
fn default() -> Self {
Self {
auto_root_detection: true,
gc_during_evaluation: true,
max_gc_interval_ms: 1000, collect_statistics: true,
preserve_continuations: true,
}
}
}
pub trait EvaluatorGcExt {
fn gc_coordinator(&self) -> Option<&GcCoordinator>;
fn with_gc_coordination(&mut self, coordinator: GcCoordinator);
fn start_gc_session(&self, env: Arc<ThreadSafeEnvironment>) -> Option<SessionId>;
fn end_gc_session(&self, session_id: SessionId);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eval::value::{ThreadSafeEnvironment, Value};
#[test]
fn test_gc_coordinator_creation() {
let coordinator = GcCoordinator::with_default_config().unwrap();
assert_eq!(coordinator.active_session_count(), 0);
}
#[test]
fn test_session_lifecycle() {
let coordinator = GcCoordinator::with_default_config().unwrap();
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
let session_id = coordinator.start_session(env.clone());
assert_eq!(coordinator.active_session_count(), 1);
coordinator.end_session(session_id);
assert_eq!(coordinator.active_session_count(), 0);
}
#[test]
fn test_environment_stack_management() {
let coordinator = GcCoordinator::with_default_config().unwrap();
let env1 = Arc::new(ThreadSafeEnvironment::new(None, 0));
let env2 = Arc::new(ThreadSafeEnvironment::new(None, 1));
let session_id = coordinator.start_session(env1);
coordinator.push_environment(session_id, env2.clone());
let popped = coordinator.pop_environment(session_id);
assert!(Arc::ptr_eq(&popped.unwrap(), &env2));
coordinator.end_session(session_id);
}
#[test]
fn test_global_roots() {
let coordinator = GcCoordinator::with_default_config().unwrap();
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
let root = GlobalRoot::Environment(env);
coordinator.add_global_root(root);
let scan_result = coordinator.comprehensive_root_scan();
assert_eq!(scan_result.global_roots.len(), 1);
}
#[test]
fn test_statistics_collection() {
let coordinator = GcCoordinator::with_default_config().unwrap();
let _result = coordinator.force_collect();
let stats = coordinator.get_statistics();
assert!(stats.is_some());
let stats = stats.unwrap();
assert!(stats.total_collections > 0);
}
}