use crate::memory::GLOBAL_MEMORY_STATS;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct GCConfig {
pub enable_incremental_gc: bool,
pub enable_adaptive_tuning: bool,
pub prefer_low_latency: bool,
pub gc_threshold: usize,
pub incremental_step: usize,
pub gc_interval: Duration,
pub memory_pressure_threshold: f64,
}
impl Default for GCConfig {
fn default() -> Self {
Self {
enable_incremental_gc: true,
enable_adaptive_tuning: true,
prefer_low_latency: true,
gc_threshold: 16 * 1024 * 1024, incremental_step: 1024 * 1024, gc_interval: Duration::from_millis(100),
memory_pressure_threshold: 0.8,
}
}
}
#[derive(Debug)]
pub struct IncrementalGC {
config: GCConfig,
state: Arc<Mutex<GCState>>,
is_running: AtomicBool,
total_allocated: AtomicUsize,
}
#[derive(Debug)]
struct GCState {
phase: GCPhase,
steps_completed: usize,
total_steps: usize,
start_time: Option<Instant>,
last_gc_time: Option<Instant>,
bytes_collected: usize,
total_pause_time: Duration,
}
impl Clone for GCState {
fn clone(&self) -> Self {
Self {
phase: self.phase,
steps_completed: self.steps_completed,
total_steps: self.total_steps,
start_time: self.start_time,
last_gc_time: self.last_gc_time,
bytes_collected: self.bytes_collected,
total_pause_time: self.total_pause_time,
}
}
}
impl Default for GCState {
fn default() -> Self {
Self {
phase: GCPhase::Idle,
steps_completed: 0,
total_steps: 0,
start_time: None,
last_gc_time: None,
bytes_collected: 0,
total_pause_time: Duration::from_millis(0),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GCPhase {
Idle,
Mark,
Sweep,
Compact,
Finished,
}
#[derive(Debug, Default)]
pub struct GCStats {
pub gc_count: AtomicUsize,
pub total_bytes_collected: AtomicUsize,
pub total_pause_time: AtomicUsize,
pub avg_gc_time: Duration,
pub incremental_gc_count: AtomicUsize,
pub full_gc_count: AtomicUsize,
pub memory_pressure_level: f64,
pub success_rate: f64,
}
impl Clone for GCStats {
fn clone(&self) -> Self {
Self {
gc_count: AtomicUsize::new(self.gc_count.load(Ordering::Relaxed)),
total_bytes_collected: AtomicUsize::new(
self.total_bytes_collected.load(Ordering::Relaxed),
),
total_pause_time: AtomicUsize::new(self.total_pause_time.load(Ordering::Relaxed)),
avg_gc_time: self.avg_gc_time,
incremental_gc_count: AtomicUsize::new(
self.incremental_gc_count.load(Ordering::Relaxed),
),
full_gc_count: AtomicUsize::new(self.full_gc_count.load(Ordering::Relaxed)),
memory_pressure_level: self.memory_pressure_level,
success_rate: self.success_rate,
}
}
}
impl IncrementalGC {
pub fn new(config: GCConfig) -> Self {
Self {
config: config.clone(),
state: Arc::new(Mutex::new(GCState::default())),
is_running: AtomicBool::new(false),
total_allocated: AtomicUsize::new(0),
}
}
pub fn record_allocation(&self, size: usize) {
self.total_allocated.fetch_add(size, Ordering::Relaxed);
let current: _ = self.total_allocated.load(Ordering::Relaxed);
if current >= self.config.gc_threshold {
let _: _ = self.trigger_incremental_gc();
}
}
pub fn trigger_incremental_gc(&self) -> Result<GCStats, &'static str> {
if self
.is_running
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
return Err("GC already running");
}
let start_time: _ = Instant::now();
{
let mut state = self.state.lock().unwrap();
state.start_time = Some(start_time);
state.phase = GCPhase::Mark;
}
let _ = self.run_incremental_collection();
{
let mut state = self.state.lock().unwrap();
state.last_gc_time = Some(Instant::now());
state.phase = GCPhase::Finished;
let pause_time: _ = start_time.elapsed();
state.total_pause_time += pause_time;
}
self.is_running.store(false, Ordering::Relaxed);
Ok(self.get_stats())
}
fn run_incremental_collection(&self) -> Result<(), &'static str> {
if !self.config.enable_incremental_gc {
return self.run_full_collection();
}
let mut steps_completed = 0;
let total_steps: _ = self.config.gc_threshold / self.config.incremental_step;
{
let mut state = self.state.lock().unwrap();
state.phase = GCPhase::Mark;
state.total_steps = total_steps;
}
for step in 0..total_steps {
std::thread::sleep(Duration::from_micros(100));
{
let mut state = self.state.lock().unwrap();
state.steps_completed = step + 1;
}
steps_completed = step + 1;
if self.should_pause_incremental() {
break;
}
}
if steps_completed < total_steps {
self.run_full_collection()?;
}
Ok(())
}
fn run_full_collection(&self) -> Result<(), &'static str> {
{
let mut state = self.state.lock().unwrap();
state.phase = GCPhase::Mark;
state.total_steps = 1;
}
std::thread::sleep(Duration::from_millis(10));
{
let mut state = self.state.lock().unwrap();
state.phase = GCPhase::Sweep;
}
std::thread::sleep(Duration::from_millis(5));
{
let mut state = self.state.lock().unwrap();
state.phase = GCPhase::Compact;
}
std::thread::sleep(Duration::from_millis(5));
let collected_bytes: _ = self.simulate_collection();
{
let mut state = self.state.lock().unwrap();
state.bytes_collected = collected_bytes;
state.phase = GCPhase::Finished;
}
GLOBAL_MEMORY_STATS.record_deallocation(collected_bytes);
Ok(())
}
fn simulate_collection(&self) -> usize {
let current_usage: _ = GLOBAL_MEMORY_STATS.get_stats().current_usage;
let collected: _ = (current_usage as f64 * 0.3) as usize; collected
}
fn should_pause_incremental(&self) -> bool {
let current_usage: _ = GLOBAL_MEMORY_STATS.get_stats().current_usage;
let pressure: _ = current_usage as f64 / self.config.gc_threshold as f64;
pressure > self.config.memory_pressure_threshold
}
pub fn get_state(&self) -> GCStateSnapshot {
let state: _ = self.state.lock().unwrap();
GCStateSnapshot {
phase: state.phase,
steps_completed: state.steps_completed,
total_steps: state.total_steps,
start_time: state.start_time,
last_gc_time: state.last_gc_time,
bytes_collected: state.bytes_collected,
total_pause_time: state.total_pause_time,
}
}
pub fn get_stats(&self) -> GCStats {
let global_stats = GLOBAL_MEMORY_STATS.get_stats();
let state = self.state.lock().unwrap();
let gc_run = state.last_gc_time.is_some();
let count = if gc_run { 1usize } else { 0usize };
GCStats {
gc_count: AtomicUsize::new(count),
total_bytes_collected: AtomicUsize::new(state.bytes_collected),
total_pause_time: AtomicUsize::new(state.total_pause_time.as_micros() as usize),
avg_gc_time: if count > 0 {
state.total_pause_time / count as u32
} else {
Duration::from_secs(0)
},
incremental_gc_count: AtomicUsize::new(
if gc_run && self.config.enable_incremental_gc {
1
} else {
0
},
),
full_gc_count: AtomicUsize::new(if gc_run && !self.config.enable_incremental_gc {
1
} else {
0
}),
memory_pressure_level: global_stats.current_usage as f64
/ self.config.gc_threshold as f64,
success_rate: 1.0,
}
}
pub fn adaptive_tune(&self) -> TuningRecommendation {
let stats: _ = self.get_stats();
let mut recommendations = Vec::new();
if stats.memory_pressure_level > 0.9 {
recommendations.push(TuningSuggestion::DecreaseThreshold);
} else if stats.memory_pressure_level < 0.5 {
recommendations.push(TuningSuggestion::IncreaseThreshold);
}
if stats.avg_gc_time.as_millis() > 10 {
recommendations.push(TuningSuggestion::DecreaseStepSize);
}
TuningRecommendation {
suggestions: recommendations,
confidence: 0.8,
}
}
pub fn cleanup(&self) {
self.is_running.store(false, Ordering::Relaxed);
}
}
#[derive(Debug, Clone)]
pub struct GCStateSnapshot {
pub phase: GCPhase,
pub steps_completed: usize,
pub total_steps: usize,
pub start_time: Option<Instant>,
pub last_gc_time: Option<Instant>,
pub bytes_collected: usize,
pub total_pause_time: Duration,
}
#[derive(Debug, Clone)]
pub struct TuningRecommendation {
pub suggestions: Vec<TuningSuggestion>,
pub confidence: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TuningSuggestion {
IncreaseThreshold,
DecreaseThreshold,
IncreaseStepSize,
DecreaseStepSize,
EnableIncremental,
DisableIncremental,
}
impl Drop for IncrementalGC {
fn drop(&mut self) {
self.cleanup();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_incremental_gc_creation() {
let config: _ = GCConfig::default();
let gc: _ = IncrementalGC::new(config);
let state: _ = gc.get_state();
assert_eq!(state.phase, GCPhase::Idle);
}
#[test]
fn test_record_allocation() {
let config: _ = GCConfig::default();
let gc: _ = IncrementalGC::new(config);
gc.record_allocation(1024);
assert!(gc.total_allocated.load(Ordering::Relaxed) >= 1024);
}
#[test]
fn test_gc_phases() {
let config: _ = GCConfig {
gc_threshold: 1024,
..Default::default()
};
let gc: _ = IncrementalGC::new(config);
let _: _ = gc.trigger_incremental_gc();
let state: _ = gc.get_state();
assert_eq!(state.phase, GCPhase::Finished);
}
#[test]
fn test_adaptive_tuning() {
let config: _ = GCConfig::default();
let gc: _ = IncrementalGC::new(config);
let recommendation: _ = gc.adaptive_tune();
assert!(!recommendation.suggestions.is_empty() || recommendation.suggestions.is_empty());
assert!(recommendation.confidence >= 0.0 && recommendation.confidence <= 1.0);
}
#[test]
fn test_gc_stats() {
let config: _ = GCConfig::default();
let gc: _ = IncrementalGC::new(config);
let stats: _ = gc.get_stats();
assert_eq!(stats.gc_count.load(Ordering::Relaxed), 0);
assert!(stats.memory_pressure_level >= 0.0);
}
}