Skip to main content

optirs_gpu/memory/management/
garbage_collection.rs

1// Garbage collection for GPU memory management
2//
3// This module provides advanced garbage collection algorithms specifically
4// optimized for GPU memory patterns, including mark-and-sweep, generational,
5// incremental, and real-time garbage collection strategies.
6
7use std::collections::{HashMap, HashSet, VecDeque};
8use std::sync::{Arc, RwLock};
9use std::time::{Duration, Instant};
10
11/// Main garbage collection engine
12pub struct GarbageCollectionEngine {
13    /// GC configuration
14    config: GCConfig,
15    /// GC statistics
16    stats: GCStats,
17    /// Active GC algorithms
18    collectors: Vec<Box<dyn GarbageCollector>>,
19    /// Memory regions under management
20    memory_regions: HashMap<usize, MemoryRegion>,
21    /// Object reference tracking
22    reference_tracker: ReferenceTracker,
23    /// GC scheduling state
24    scheduler: GCScheduler,
25    /// Performance history
26    performance_history: VecDeque<GCPerformance>,
27}
28
29/// Garbage collection configuration
30#[derive(Debug, Clone)]
31pub struct GCConfig {
32    /// Enable automatic garbage collection
33    pub auto_gc: bool,
34    /// GC trigger threshold (memory usage ratio)
35    pub gc_threshold: f64,
36    /// Maximum pause time for real-time GC (milliseconds)
37    pub max_pause_time: Duration,
38    /// Enable generational collection
39    pub enable_generational: bool,
40    /// Enable incremental collection
41    pub enable_incremental: bool,
42    /// Enable concurrent collection
43    pub enable_concurrent: bool,
44    /// Young generation size ratio
45    pub young_gen_ratio: f64,
46    /// Survivor space ratio
47    pub survivor_ratio: f64,
48    /// Tenuring threshold for promotion
49    pub tenuring_threshold: u32,
50    /// Enable statistics collection
51    pub enable_stats: bool,
52    /// GC algorithm preference
53    pub preferred_algorithm: GCAlgorithm,
54    /// Enable parallel collection
55    pub parallel_gc: bool,
56    /// Number of GC worker threads
57    pub gc_threads: usize,
58}
59
60impl Default for GCConfig {
61    fn default() -> Self {
62        Self {
63            auto_gc: true,
64            gc_threshold: 0.8,
65            max_pause_time: Duration::from_millis(10),
66            enable_generational: true,
67            enable_incremental: true,
68            enable_concurrent: false,
69            young_gen_ratio: 0.3,
70            survivor_ratio: 0.1,
71            tenuring_threshold: 15,
72            enable_stats: true,
73            preferred_algorithm: GCAlgorithm::Generational,
74            parallel_gc: true,
75            gc_threads: 2,
76        }
77    }
78}
79
80/// Available garbage collection algorithms
81#[derive(Debug, Clone, PartialEq)]
82pub enum GCAlgorithm {
83    /// Mark and sweep collection
84    MarkSweep,
85    /// Copying collection
86    Copying,
87    /// Generational collection
88    Generational,
89    /// Incremental collection
90    Incremental,
91    /// Concurrent collection
92    Concurrent,
93    /// Reference counting
94    ReferenceCounting,
95    /// Adaptive algorithm selection
96    Adaptive,
97}
98
99/// GC statistics
100#[derive(Debug, Clone, Default)]
101pub struct GCStats {
102    /// Total GC cycles
103    pub total_cycles: u64,
104    /// Total time spent in GC
105    pub total_gc_time: Duration,
106    /// Total bytes collected
107    pub total_bytes_collected: u64,
108    /// Total objects collected
109    pub total_objects_collected: u64,
110    /// Average GC pause time
111    pub average_pause_time: Duration,
112    /// Maximum GC pause time
113    pub max_pause_time: Duration,
114    /// GC efficiency (bytes collected per millisecond)
115    pub gc_efficiency: f64,
116    /// Young generation collections
117    pub young_gen_collections: u64,
118    /// Old generation collections
119    pub old_gen_collections: u64,
120    /// Promotion rate (objects/sec)
121    pub promotion_rate: f64,
122    /// Memory reclaim rate
123    pub reclaim_rate: f64,
124    /// GC overhead percentage
125    pub gc_overhead: f64,
126    /// Last GC timestamp
127    pub last_gc_time: Option<Instant>,
128}
129
130/// Memory region managed by GC
131#[derive(Debug, Clone)]
132pub struct MemoryRegion {
133    /// Base address
134    pub base_addr: usize,
135    /// Region size
136    pub size: usize,
137    /// Generation (0 = young, 1+ = old)
138    pub generation: u32,
139    /// Objects in this region
140    pub objects: HashMap<usize, ObjectMetadata>,
141    /// Free space bitmap
142    pub free_bitmap: Vec<u64>,
143    /// Last collection time
144    pub last_collection: Option<Instant>,
145    /// Collection count
146    pub collection_count: u32,
147    /// Utilization ratio
148    pub utilization: f64,
149}
150
151/// Object metadata for GC tracking
152#[derive(Debug, Clone)]
153pub struct ObjectMetadata {
154    /// Object address
155    pub address: usize,
156    /// Object size
157    pub size: usize,
158    /// Object type identifier
159    pub type_id: u32,
160    /// Reference count
161    pub ref_count: u32,
162    /// Mark state for mark-and-sweep
163    pub marked: bool,
164    /// Age in collection cycles
165    pub age: u32,
166    /// Last access time
167    pub last_access: Option<Instant>,
168    /// Reference list (for precise GC)
169    pub references: Vec<usize>,
170}
171
172/// Reference tracking system
173pub struct ReferenceTracker {
174    /// Object reference graph
175    reference_graph: HashMap<usize, HashSet<usize>>,
176    /// Reverse reference mapping
177    reverse_references: HashMap<usize, HashSet<usize>>,
178    /// Root references (stack, globals, etc.)
179    root_references: HashSet<usize>,
180    /// Write barrier log for concurrent GC
181    write_barrier_log: VecDeque<WriteBarrierEntry>,
182}
183
184/// Write barrier entry for concurrent GC
185#[derive(Debug, Clone)]
186pub struct WriteBarrierEntry {
187    pub source: usize,
188    pub target: usize,
189    pub timestamp: Instant,
190}
191
192impl Default for ReferenceTracker {
193    fn default() -> Self {
194        Self::new()
195    }
196}
197
198impl ReferenceTracker {
199    pub fn new() -> Self {
200        Self {
201            reference_graph: HashMap::new(),
202            reverse_references: HashMap::new(),
203            root_references: HashSet::new(),
204            write_barrier_log: VecDeque::new(),
205        }
206    }
207
208    /// Add reference between objects
209    pub fn add_reference(&mut self, from: usize, to: usize) {
210        self.reference_graph.entry(from).or_default().insert(to);
211        self.reverse_references.entry(to).or_default().insert(from);
212    }
213
214    /// Remove reference between objects
215    pub fn remove_reference(&mut self, from: usize, to: usize) {
216        if let Some(refs) = self.reference_graph.get_mut(&from) {
217            refs.remove(&to);
218        }
219        if let Some(refs) = self.reverse_references.get_mut(&to) {
220            refs.remove(&from);
221        }
222    }
223
224    /// Add root reference
225    pub fn add_root(&mut self, obj: usize) {
226        self.root_references.insert(obj);
227    }
228
229    /// Remove root reference
230    pub fn remove_root(&mut self, obj: usize) {
231        self.root_references.remove(&obj);
232    }
233
234    /// Get all objects reachable from roots
235    pub fn get_reachable_objects(&self) -> HashSet<usize> {
236        let mut reachable = HashSet::new();
237        let mut work_list = VecDeque::new();
238
239        // Start with roots
240        for &root in &self.root_references {
241            reachable.insert(root);
242            work_list.push_back(root);
243        }
244
245        // Breadth-first traversal
246        while let Some(obj) = work_list.pop_front() {
247            if let Some(refs) = self.reference_graph.get(&obj) {
248                for &target in refs {
249                    if reachable.insert(target) {
250                        work_list.push_back(target);
251                    }
252                }
253            }
254        }
255
256        reachable
257    }
258
259    /// Record write barrier for concurrent GC
260    pub fn write_barrier(&mut self, source: usize, target: usize) {
261        let entry = WriteBarrierEntry {
262            source,
263            target,
264            timestamp: Instant::now(),
265        };
266        self.write_barrier_log.push_back(entry);
267    }
268}
269
270/// GC scheduling and coordination
271///
272/// `timing_state` and `trigger_conditions` are real and load-bearing: see
273/// `GarbageCollectionEngine::should_collect`, which evaluates
274/// `trigger_conditions` against live memory-usage and timing data to decide
275/// whether to run a collection right now. `scheduled_tasks`/`current_task`
276/// are a separate, unfinished priority/deadline-based task queue
277/// (`GCTask` has `priority`, `target_region`, `deadline`, ...) that nothing
278/// currently enqueues into or drains: `should_collect` returns a plain
279/// `bool` and `collect` iterates every tracked region directly rather than
280/// consuming a queued `GCTask`. Wiring a real task queue in means deciding
281/// how it should interact with that existing per-region collection loop
282/// (does `collect` start consuming `scheduled_tasks` instead? does
283/// `should_collect` enqueue a task rather than / in addition to returning
284/// `bool`?) -- a scheduling-policy decision, not a lint fix, so this is
285/// recorded as a finding rather than force-wired.
286pub struct GCScheduler {
287    /// Scheduled GC tasks (see the struct-level doc: not yet consumed).
288    #[allow(dead_code)]
289    scheduled_tasks: VecDeque<GCTask>,
290    /// Current executing task (see the struct-level doc: not yet set).
291    #[allow(dead_code)]
292    current_task: Option<GCTask>,
293    /// GC timing state
294    timing_state: GCTimingState,
295    /// Trigger conditions
296    trigger_conditions: Vec<GCTrigger>,
297}
298
299/// GC task representation
300#[derive(Debug, Clone)]
301pub struct GCTask {
302    pub id: u64,
303    pub algorithm: GCAlgorithm,
304    pub priority: GCPriority,
305    pub target_region: Option<usize>,
306    pub estimated_duration: Duration,
307    pub created_at: Instant,
308    pub deadline: Option<Instant>,
309}
310
311/// GC task priority
312#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
313pub enum GCPriority {
314    Low,
315    Normal,
316    High,
317    Critical,
318}
319
320/// GC timing state
321#[derive(Debug, Clone)]
322pub struct GCTimingState {
323    pub last_young_gc: Option<Instant>,
324    pub last_old_gc: Option<Instant>,
325    pub gc_frequency: f64,
326    pub allocation_rate: f64,
327    pub memory_pressure: f64,
328}
329
330/// GC trigger conditions
331#[derive(Debug, Clone)]
332pub enum GCTrigger {
333    MemoryThreshold(f64),
334    TimeInterval(Duration),
335    AllocationCount(u64),
336    ExplicitRequest,
337    MemoryPressure,
338}
339
340/// Garbage collector trait
341pub trait GarbageCollector: Send + Sync {
342    fn name(&self) -> &str;
343    fn can_collect(&self, region: &MemoryRegion) -> bool;
344    fn estimate_collection_time(&self, region: &MemoryRegion) -> Duration;
345    fn collect(
346        &mut self,
347        region: &mut MemoryRegion,
348        tracker: &mut ReferenceTracker,
349    ) -> Result<GCResult, GCError>;
350    fn get_statistics(&self) -> GCCollectorStats;
351    fn configure(&mut self, config: &GCConfig);
352}
353
354/// Result of a garbage collection cycle
355#[derive(Debug, Clone)]
356pub struct GCResult {
357    pub bytes_collected: usize,
358    pub objects_collected: u32,
359    pub collection_time: Duration,
360    pub algorithm_used: GCAlgorithm,
361    pub regions_collected: Vec<usize>,
362    pub promotion_count: u32,
363    pub compaction_performed: bool,
364    pub efficiency_score: f64,
365}
366
367/// GC collector statistics
368#[derive(Debug, Clone, Default)]
369pub struct GCCollectorStats {
370    pub collections: u64,
371    pub total_time: Duration,
372    pub total_bytes_collected: u64,
373    pub total_objects_collected: u64,
374    pub average_efficiency: f64,
375    pub success_rate: f64,
376}
377
378/// Mark and sweep garbage collector
379pub struct MarkSweepCollector {
380    stats: GCCollectorStats,
381    config: MarkSweepConfig,
382}
383
384/// Mark and sweep configuration
385#[derive(Debug, Clone)]
386pub struct MarkSweepConfig {
387    pub enable_compaction: bool,
388    pub mark_threshold: f64,
389    pub sweep_threshold: f64,
390    pub enable_parallel_marking: bool,
391    pub enable_parallel_sweeping: bool,
392}
393
394impl Default for MarkSweepConfig {
395    fn default() -> Self {
396        Self {
397            enable_compaction: true,
398            mark_threshold: 0.7,
399            sweep_threshold: 0.5,
400            enable_parallel_marking: true,
401            enable_parallel_sweeping: true,
402        }
403    }
404}
405
406impl MarkSweepCollector {
407    pub fn new(config: MarkSweepConfig) -> Self {
408        Self {
409            stats: GCCollectorStats::default(),
410            config,
411        }
412    }
413
414    /// Mark every object in `region` as reachable or not, persisting the
415    /// result on each [`ObjectMetadata::marked`] field for
416    /// [`Self::sweep_phase`] to read. The reachable set itself does not
417    /// need to be returned: it has already done its job once every object
418    /// carries its own verdict.
419    fn mark_phase(&self, region: &mut MemoryRegion, tracker: &ReferenceTracker) {
420        let reachable = tracker.get_reachable_objects();
421
422        // Mark all reachable objects in this region
423        for (addr, obj) in region.objects.iter_mut() {
424            obj.marked = reachable.contains(addr);
425        }
426    }
427
428    fn sweep_phase(&self, region: &mut MemoryRegion) -> (usize, u32) {
429        let mut bytes_collected = 0;
430        let mut objects_collected = 0;
431        let mut objects_to_remove = Vec::new();
432
433        for (addr, obj) in &region.objects {
434            if !obj.marked {
435                bytes_collected += obj.size;
436                objects_collected += 1;
437                objects_to_remove.push(*addr);
438            }
439        }
440
441        // Remove unmarked objects
442        for addr in objects_to_remove {
443            region.objects.remove(&addr);
444        }
445
446        // Reset marks for next collection
447        for obj in region.objects.values_mut() {
448            obj.marked = false;
449        }
450
451        (bytes_collected, objects_collected)
452    }
453}
454
455impl GarbageCollector for MarkSweepCollector {
456    fn name(&self) -> &str {
457        "MarkSweep"
458    }
459
460    fn can_collect(&self, region: &MemoryRegion) -> bool {
461        !region.objects.is_empty() && region.utilization < self.config.mark_threshold
462    }
463
464    fn estimate_collection_time(&self, region: &MemoryRegion) -> Duration {
465        let object_count = region.objects.len();
466        let base_time = Duration::from_micros((object_count * 10) as u64);
467
468        if self.config.enable_compaction {
469            base_time + Duration::from_micros((object_count * 5) as u64)
470        } else {
471            base_time
472        }
473    }
474
475    fn collect(
476        &mut self,
477        region: &mut MemoryRegion,
478        tracker: &mut ReferenceTracker,
479    ) -> Result<GCResult, GCError> {
480        let start_time = Instant::now();
481
482        // Mark phase
483        self.mark_phase(region, tracker);
484
485        // Sweep phase
486        let (bytes_collected, objects_collected) = self.sweep_phase(region);
487
488        let collection_time = start_time.elapsed();
489
490        // Update statistics
491        self.stats.collections += 1;
492        self.stats.total_time += collection_time;
493        self.stats.total_bytes_collected += bytes_collected as u64;
494        self.stats.total_objects_collected += objects_collected as u64;
495
496        let efficiency = if collection_time.as_millis() > 0 {
497            bytes_collected as f64 / collection_time.as_millis() as f64
498        } else {
499            0.0
500        };
501
502        self.stats.average_efficiency =
503            (self.stats.average_efficiency * (self.stats.collections - 1) as f64 + efficiency)
504                / self.stats.collections as f64;
505        self.stats.success_rate = 1.0; // Mark-sweep always succeeds
506
507        Ok(GCResult {
508            bytes_collected,
509            objects_collected,
510            collection_time,
511            algorithm_used: GCAlgorithm::MarkSweep,
512            regions_collected: vec![region.base_addr],
513            promotion_count: 0,
514            compaction_performed: self.config.enable_compaction,
515            efficiency_score: efficiency,
516        })
517    }
518
519    fn get_statistics(&self) -> GCCollectorStats {
520        self.stats.clone()
521    }
522
523    fn configure(&mut self, config: &GCConfig) {
524        // Update configuration based on global GC config
525        self.config.enable_parallel_marking = config.parallel_gc;
526        self.config.enable_parallel_sweeping = config.parallel_gc;
527    }
528}
529
530/// Generational garbage collector
531pub struct GenerationalCollector {
532    stats: GCCollectorStats,
533    config: GenerationalConfig,
534    young_gen_collector: Box<dyn GarbageCollector>,
535    old_gen_collector: Box<dyn GarbageCollector>,
536}
537
538/// Generational GC configuration
539#[derive(Debug, Clone)]
540pub struct GenerationalConfig {
541    pub young_gen_threshold: usize,
542    pub promotion_age: u32,
543    pub minor_gc_frequency: u32,
544    pub major_gc_threshold: f64,
545    pub enable_remembered_set: bool,
546}
547
548impl Default for GenerationalConfig {
549    fn default() -> Self {
550        Self {
551            young_gen_threshold: 1024 * 1024, // 1MB
552            promotion_age: 3,
553            minor_gc_frequency: 10,
554            major_gc_threshold: 0.8,
555            enable_remembered_set: true,
556        }
557    }
558}
559
560impl GenerationalCollector {
561    pub fn new(config: GenerationalConfig) -> Self {
562        let young_collector = Box::new(MarkSweepCollector::new(MarkSweepConfig::default()));
563        let old_collector = Box::new(MarkSweepCollector::new(MarkSweepConfig {
564            enable_compaction: true,
565            ..MarkSweepConfig::default()
566        }));
567
568        Self {
569            stats: GCCollectorStats::default(),
570            config,
571            young_gen_collector: young_collector,
572            old_gen_collector: old_collector,
573        }
574    }
575
576    fn should_promote(&self, obj: &ObjectMetadata) -> bool {
577        obj.age >= self.config.promotion_age
578    }
579
580    fn promote_objects(&self, region: &mut MemoryRegion) -> u32 {
581        let mut promoted = 0;
582
583        for obj in region.objects.values_mut() {
584            if self.should_promote(obj) && region.generation == 0 {
585                promoted += 1;
586                // In a real implementation, this would move the object to old generation
587            }
588        }
589
590        promoted
591    }
592}
593
594impl GarbageCollector for GenerationalCollector {
595    fn name(&self) -> &str {
596        "Generational"
597    }
598
599    fn can_collect(&self, region: &MemoryRegion) -> bool {
600        !region.objects.is_empty()
601    }
602
603    fn estimate_collection_time(&self, region: &MemoryRegion) -> Duration {
604        if region.generation == 0 {
605            self.young_gen_collector.estimate_collection_time(region)
606        } else {
607            self.old_gen_collector.estimate_collection_time(region)
608        }
609    }
610
611    fn collect(
612        &mut self,
613        region: &mut MemoryRegion,
614        tracker: &mut ReferenceTracker,
615    ) -> Result<GCResult, GCError> {
616        let start_time = Instant::now();
617
618        let result = if region.generation == 0 {
619            // Minor GC
620            self.young_gen_collector.collect(region, tracker)?
621        } else {
622            // Major GC
623            self.old_gen_collector.collect(region, tracker)?
624        };
625
626        // Handle promotion for young generation
627        let promotion_count = if region.generation == 0 {
628            self.promote_objects(region)
629        } else {
630            0
631        };
632
633        // Update ages
634        for obj in region.objects.values_mut() {
635            obj.age += 1;
636        }
637
638        let collection_time = start_time.elapsed();
639
640        // Update statistics
641        self.stats.collections += 1;
642        self.stats.total_time += collection_time;
643        self.stats.total_bytes_collected += result.bytes_collected as u64;
644        self.stats.total_objects_collected += result.objects_collected as u64;
645
646        Ok(GCResult {
647            promotion_count,
648            ..result
649        })
650    }
651
652    fn get_statistics(&self) -> GCCollectorStats {
653        self.stats.clone()
654    }
655
656    fn configure(&mut self, config: &GCConfig) {
657        self.young_gen_collector.configure(config);
658        self.old_gen_collector.configure(config);
659    }
660}
661
662/// Incremental garbage collector
663pub struct IncrementalCollector {
664    stats: GCCollectorStats,
665    config: IncrementalConfig,
666    current_phase: IncrementalPhase,
667    work_queue: VecDeque<IncrementalWork>,
668}
669
670/// Incremental GC configuration
671#[derive(Debug, Clone)]
672pub struct IncrementalConfig {
673    pub time_slice: Duration,
674    pub work_unit_size: usize,
675    pub pause_threshold: Duration,
676    pub enable_write_barriers: bool,
677}
678
679impl Default for IncrementalConfig {
680    fn default() -> Self {
681        Self {
682            time_slice: Duration::from_millis(2),
683            work_unit_size: 100,
684            pause_threshold: Duration::from_millis(5),
685            enable_write_barriers: true,
686        }
687    }
688}
689
690/// Incremental GC phases
691#[derive(Debug, Clone, PartialEq)]
692pub enum IncrementalPhase {
693    Idle,
694    Marking,
695    Sweeping,
696    Compacting,
697    Finalizing,
698}
699
700/// Incremental work unit
701#[derive(Debug, Clone)]
702pub struct IncrementalWork {
703    pub phase: IncrementalPhase,
704    pub region_addr: usize,
705    pub object_range: (usize, usize),
706    pub estimated_time: Duration,
707}
708
709impl IncrementalCollector {
710    pub fn new(config: IncrementalConfig) -> Self {
711        Self {
712            stats: GCCollectorStats::default(),
713            config,
714            current_phase: IncrementalPhase::Idle,
715            work_queue: VecDeque::new(),
716        }
717    }
718
719    fn schedule_incremental_work(&mut self, region: &MemoryRegion) {
720        let mut object_addrs: Vec<usize> = region.objects.keys().copied().collect();
721        // Sort so each chunk's (first, last) pair is a tight, ordered
722        // range rather than two arbitrary addresses from HashMap's
723        // unspecified iteration order.
724        object_addrs.sort_unstable();
725        let chunk_size = self.config.work_unit_size;
726
727        // Schedule marking work first, then sweeping: `work_queue` is a
728        // FIFO, so every marking work item is popped and every object's
729        // `marked` flag is final (see `perform_incremental_work`) before
730        // any sweeping work item runs. Without a sweeping phase ever being
731        // scheduled, the collector would mark forever and never reclaim
732        // anything.
733        for chunk in object_addrs.chunks(chunk_size) {
734            if !chunk.is_empty() {
735                self.work_queue.push_back(IncrementalWork {
736                    phase: IncrementalPhase::Marking,
737                    region_addr: region.base_addr,
738                    object_range: (chunk[0], chunk[chunk.len() - 1]),
739                    estimated_time: Duration::from_micros(chunk.len() as u64 * 10),
740                });
741            }
742        }
743        for chunk in object_addrs.chunks(chunk_size) {
744            if !chunk.is_empty() {
745                self.work_queue.push_back(IncrementalWork {
746                    phase: IncrementalPhase::Sweeping,
747                    region_addr: region.base_addr,
748                    object_range: (chunk[0], chunk[chunk.len() - 1]),
749                    estimated_time: Duration::from_micros(chunk.len() as u64 * 10),
750                });
751            }
752        }
753    }
754
755    fn perform_incremental_work(
756        &mut self,
757        region: &mut MemoryRegion,
758        tracker: &mut ReferenceTracker,
759    ) -> Option<GCResult> {
760        let time_budget = self.config.time_slice;
761        let start_time = Instant::now();
762        let mut work_done = false;
763        let mut bytes_collected = 0usize;
764        let mut objects_collected = 0u32;
765
766        while start_time.elapsed() < time_budget {
767            if let Some(work) = self.work_queue.pop_front() {
768                self.current_phase = work.phase.clone();
769                match work.phase {
770                    IncrementalPhase::Marking => {
771                        // Perform incremental marking
772                        let reachable = tracker.get_reachable_objects();
773                        for addr in work.object_range.0..=work.object_range.1 {
774                            if let Some(obj) = region.objects.get_mut(&addr) {
775                                obj.marked = reachable.contains(&addr);
776                            }
777                        }
778                        work_done = true;
779                    }
780                    IncrementalPhase::Sweeping => {
781                        // Perform incremental sweeping
782                        for addr in work.object_range.0..=work.object_range.1 {
783                            if let Some(obj) = region.objects.get(&addr) {
784                                if !obj.marked {
785                                    bytes_collected += obj.size;
786                                    objects_collected += 1;
787                                    region.objects.remove(&addr);
788                                }
789                            }
790                        }
791                        work_done = true;
792                    }
793                    _ => {}
794                }
795            } else {
796                break;
797            }
798        }
799
800        if work_done && self.work_queue.is_empty() {
801            // Collection complete: no more incremental work pending.
802            self.current_phase = IncrementalPhase::Idle;
803            Some(GCResult {
804                bytes_collected,
805                objects_collected,
806                collection_time: start_time.elapsed(),
807                algorithm_used: GCAlgorithm::Incremental,
808                regions_collected: vec![region.base_addr],
809                promotion_count: 0,
810                compaction_performed: false,
811                efficiency_score: 0.0,
812            })
813        } else {
814            None
815        }
816    }
817
818    /// The phase this collector is currently in (or [`IncrementalPhase::Idle`]
819    /// between incremental work slices) -- see `Self::perform_incremental_work`.
820    pub fn current_phase(&self) -> &IncrementalPhase {
821        &self.current_phase
822    }
823}
824
825impl GarbageCollector for IncrementalCollector {
826    fn name(&self) -> &str {
827        "Incremental"
828    }
829
830    fn can_collect(&self, region: &MemoryRegion) -> bool {
831        !region.objects.is_empty()
832    }
833
834    fn estimate_collection_time(&self, region: &MemoryRegion) -> Duration {
835        let object_count = region.objects.len();
836        Duration::from_millis(
837            (object_count / self.config.work_unit_size) as u64
838                * self.config.time_slice.as_millis() as u64,
839        )
840    }
841
842    fn collect(
843        &mut self,
844        region: &mut MemoryRegion,
845        tracker: &mut ReferenceTracker,
846    ) -> Result<GCResult, GCError> {
847        if self.work_queue.is_empty() {
848            self.schedule_incremental_work(region);
849        }
850
851        if let Some(result) = self.perform_incremental_work(region, tracker) {
852            self.stats.collections += 1;
853            self.stats.total_time += result.collection_time;
854            Ok(result)
855        } else {
856            Err(GCError::CollectionIncomplete(
857                "Incremental collection in progress".to_string(),
858            ))
859        }
860    }
861
862    fn get_statistics(&self) -> GCCollectorStats {
863        self.stats.clone()
864    }
865
866    fn configure(&mut self, config: &GCConfig) {
867        self.config.time_slice = config.max_pause_time;
868    }
869}
870
871/// GC performance metrics
872#[derive(Debug, Clone)]
873pub struct GCPerformance {
874    pub timestamp: Instant,
875    pub algorithm: GCAlgorithm,
876    pub collection_time: Duration,
877    pub bytes_collected: usize,
878    pub objects_collected: u32,
879    pub regions_affected: usize,
880    pub efficiency_score: f64,
881    pub memory_before: usize,
882    pub memory_after: usize,
883}
884
885impl GarbageCollectionEngine {
886    pub fn new(config: GCConfig) -> Self {
887        let mut collectors: Vec<Box<dyn GarbageCollector>> = Vec::new();
888
889        // Add default collectors
890        collectors.push(Box::new(
891            MarkSweepCollector::new(MarkSweepConfig::default()),
892        ));
893
894        if config.enable_generational {
895            collectors.push(Box::new(GenerationalCollector::new(
896                GenerationalConfig::default(),
897            )));
898        }
899
900        if config.enable_incremental {
901            collectors.push(Box::new(IncrementalCollector::new(
902                IncrementalConfig::default(),
903            )));
904        }
905
906        let gc_threshold = config.gc_threshold;
907        Self {
908            config,
909            stats: GCStats::default(),
910            collectors,
911            memory_regions: HashMap::new(),
912            reference_tracker: ReferenceTracker::new(),
913            scheduler: GCScheduler {
914                scheduled_tasks: VecDeque::new(),
915                current_task: None,
916                timing_state: GCTimingState {
917                    last_young_gc: None,
918                    last_old_gc: None,
919                    gc_frequency: 0.0,
920                    allocation_rate: 0.0,
921                    memory_pressure: 0.0,
922                },
923                trigger_conditions: vec![
924                    GCTrigger::MemoryThreshold(gc_threshold),
925                    GCTrigger::TimeInterval(Duration::from_secs(30)),
926                ],
927            },
928            performance_history: VecDeque::with_capacity(1000),
929        }
930    }
931
932    /// Register a memory region for GC management
933    pub fn register_region(&mut self, base_addr: usize, size: usize, generation: u32) {
934        let region = MemoryRegion {
935            base_addr,
936            size,
937            generation,
938            objects: HashMap::new(),
939            free_bitmap: vec![0; (size / 64) + 1],
940            last_collection: None,
941            collection_count: 0,
942            utilization: 0.0,
943        };
944
945        self.memory_regions.insert(base_addr, region);
946    }
947
948    /// Add object to GC tracking
949    pub fn track_object(
950        &mut self,
951        region_addr: usize,
952        obj_addr: usize,
953        size: usize,
954        type_id: u32,
955    ) -> Result<(), GCError> {
956        let region = self
957            .memory_regions
958            .get_mut(&region_addr)
959            .ok_or_else(|| GCError::RegionNotFound("Region not registered".to_string()))?;
960
961        let metadata = ObjectMetadata {
962            address: obj_addr,
963            size,
964            type_id,
965            ref_count: 0,
966            marked: false,
967            age: 0,
968            last_access: Some(Instant::now()),
969            references: Vec::new(),
970        };
971
972        region.objects.insert(obj_addr, metadata);
973        Ok(())
974    }
975
976    /// Check if GC should be triggered
977    pub fn should_collect(&mut self) -> bool {
978        if !self.config.auto_gc {
979            return false;
980        }
981
982        for trigger in &self.scheduler.trigger_conditions {
983            match trigger {
984                GCTrigger::MemoryThreshold(threshold) => {
985                    let total_used = self.calculate_total_memory_usage();
986                    let total_size = self.calculate_total_memory_size();
987                    if total_size > 0 && (total_used as f64 / total_size as f64) > *threshold {
988                        return true;
989                    }
990                }
991                GCTrigger::TimeInterval(interval) => {
992                    if let Some(last_gc) = self.stats.last_gc_time {
993                        if last_gc.elapsed() > *interval {
994                            return true;
995                        }
996                    } else {
997                        return true; // First GC
998                    }
999                }
1000                GCTrigger::MemoryPressure if self.scheduler.timing_state.memory_pressure > 0.8 => {
1001                    return true;
1002                }
1003                _ => {}
1004            }
1005        }
1006
1007        false
1008    }
1009
1010    /// Trigger garbage collection
1011    pub fn collect(&mut self) -> Result<Vec<GCResult>, GCError> {
1012        let mut results = Vec::new();
1013
1014        // First, collect collector indices for each region
1015        let collector_indices: Result<Vec<(usize, usize)>, GCError> = self
1016            .memory_regions
1017            .iter()
1018            .map(|(addr, region)| {
1019                let collector_index = self.select_collector(region)?;
1020                Ok((*addr, collector_index))
1021            })
1022            .collect();
1023
1024        let collector_indices = collector_indices?;
1025
1026        // Now perform collections
1027        for (region_addr, collector_index) in collector_indices {
1028            // Calculate utilization before getting mutable reference
1029            let utilization = {
1030                let region = self
1031                    .memory_regions
1032                    .get(&region_addr)
1033                    .ok_or_else(|| GCError::InvalidRegion("Region not found".to_string()))?;
1034                self.calculate_region_utilization(region)
1035            };
1036
1037            let region = self
1038                .memory_regions
1039                .get_mut(&region_addr)
1040                .ok_or_else(|| GCError::InvalidRegion("Region not found".to_string()))?;
1041
1042            let collector = &mut self.collectors[collector_index];
1043
1044            // Perform collection
1045            let result = collector.collect(region, &mut self.reference_tracker)?;
1046
1047            // Update region state
1048            region.last_collection = Some(Instant::now());
1049            region.collection_count += 1;
1050            region.utilization = utilization;
1051
1052            // Update global statistics
1053            self.stats.total_cycles += 1;
1054            self.stats.total_gc_time += result.collection_time;
1055            self.stats.total_bytes_collected += result.bytes_collected as u64;
1056            self.stats.total_objects_collected += result.objects_collected as u64;
1057
1058            // Update average pause time
1059            let pause_time = result.collection_time;
1060            if pause_time > self.stats.max_pause_time {
1061                self.stats.max_pause_time = pause_time;
1062            }
1063
1064            let total_time = self.stats.average_pause_time.as_nanos() as u64
1065                * (self.stats.total_cycles - 1)
1066                + pause_time.as_nanos() as u64;
1067            self.stats.average_pause_time =
1068                Duration::from_nanos(total_time / self.stats.total_cycles);
1069
1070            // Record performance
1071            let performance = GCPerformance {
1072                timestamp: Instant::now(),
1073                algorithm: result.algorithm_used.clone(),
1074                collection_time: result.collection_time,
1075                bytes_collected: result.bytes_collected,
1076                objects_collected: result.objects_collected,
1077                regions_affected: 1,
1078                efficiency_score: result.efficiency_score,
1079                memory_before: region.size, // Simplified
1080                memory_after: region.size - result.bytes_collected,
1081            };
1082
1083            self.performance_history.push_back(performance);
1084            if self.performance_history.len() > 1000 {
1085                self.performance_history.pop_front();
1086            }
1087
1088            results.push(result);
1089        }
1090
1091        self.stats.last_gc_time = Some(Instant::now());
1092        Ok(results)
1093    }
1094
1095    fn select_collector(&self, region: &MemoryRegion) -> Result<usize, GCError> {
1096        for (i, collector) in self.collectors.iter().enumerate() {
1097            if collector.can_collect(region) {
1098                return Ok(i);
1099            }
1100        }
1101
1102        Err(GCError::NoSuitableCollector(
1103            "No collector available for region".to_string(),
1104        ))
1105    }
1106
1107    fn calculate_total_memory_usage(&self) -> usize {
1108        self.memory_regions
1109            .values()
1110            .map(|region| region.objects.values().map(|obj| obj.size).sum::<usize>())
1111            .sum()
1112    }
1113
1114    fn calculate_total_memory_size(&self) -> usize {
1115        self.memory_regions.values().map(|region| region.size).sum()
1116    }
1117
1118    fn calculate_region_utilization(&self, region: &MemoryRegion) -> f64 {
1119        let used_size: usize = region.objects.values().map(|obj| obj.size).sum();
1120        used_size as f64 / region.size as f64
1121    }
1122
1123    /// Get GC statistics
1124    pub fn get_stats(&self) -> &GCStats {
1125        &self.stats
1126    }
1127
1128    /// Get performance history
1129    pub fn get_performance_history(&self) -> &VecDeque<GCPerformance> {
1130        &self.performance_history
1131    }
1132
1133    /// Get collector information
1134    pub fn get_collector_info(&self) -> Vec<(String, GCCollectorStats)> {
1135        self.collectors
1136            .iter()
1137            .map(|collector| (collector.name().to_string(), collector.get_statistics()))
1138            .collect()
1139    }
1140
1141    /// Force collection on specific region
1142    pub fn force_collect_region(&mut self, region_addr: usize) -> Result<GCResult, GCError> {
1143        // First get collector index with immutable borrow
1144        let collector_index = {
1145            let region = self
1146                .memory_regions
1147                .get(&region_addr)
1148                .ok_or_else(|| GCError::RegionNotFound("Region not found".to_string()))?;
1149            self.select_collector(region)?
1150        };
1151
1152        // Now get mutable reference and perform collection
1153        let region = self
1154            .memory_regions
1155            .get_mut(&region_addr)
1156            .ok_or_else(|| GCError::RegionNotFound("Region not found".to_string()))?;
1157
1158        let collector = &mut self.collectors[collector_index];
1159        collector.collect(region, &mut self.reference_tracker)
1160    }
1161
1162    /// Add reference between objects
1163    pub fn add_reference(&mut self, from: usize, to: usize) {
1164        self.reference_tracker.add_reference(from, to);
1165    }
1166
1167    /// Remove reference between objects  
1168    pub fn remove_reference(&mut self, from: usize, to: usize) {
1169        self.reference_tracker.remove_reference(from, to);
1170    }
1171
1172    /// Add root reference
1173    pub fn add_root_reference(&mut self, obj: usize) {
1174        self.reference_tracker.add_root(obj);
1175    }
1176
1177    /// Remove root reference
1178    pub fn remove_root_reference(&mut self, obj: usize) {
1179        self.reference_tracker.remove_root(obj);
1180    }
1181}
1182
1183/// GC errors
1184#[derive(Debug, Clone)]
1185pub enum GCError {
1186    RegionNotFound(String),
1187    ObjectNotFound(String),
1188    CollectionFailed(String),
1189    CollectionIncomplete(String),
1190    NoSuitableCollector(String),
1191    ConfigurationError(String),
1192    InternalError(String),
1193    InvalidRegion(String),
1194}
1195
1196impl std::fmt::Display for GCError {
1197    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1198        match self {
1199            GCError::RegionNotFound(msg) => write!(f, "Region not found: {}", msg),
1200            GCError::ObjectNotFound(msg) => write!(f, "Object not found: {}", msg),
1201            GCError::CollectionFailed(msg) => write!(f, "Collection failed: {}", msg),
1202            GCError::CollectionIncomplete(msg) => write!(f, "Collection incomplete: {}", msg),
1203            GCError::NoSuitableCollector(msg) => write!(f, "No suitable collector: {}", msg),
1204            GCError::ConfigurationError(msg) => write!(f, "Configuration error: {}", msg),
1205            GCError::InternalError(msg) => write!(f, "Internal error: {}", msg),
1206            GCError::InvalidRegion(msg) => write!(f, "Invalid region: {}", msg),
1207        }
1208    }
1209}
1210
1211impl std::error::Error for GCError {}
1212
1213/// Thread-safe garbage collection engine
1214pub struct ThreadSafeGCEngine {
1215    engine: Arc<RwLock<GarbageCollectionEngine>>,
1216}
1217
1218impl ThreadSafeGCEngine {
1219    pub fn new(config: GCConfig) -> Self {
1220        Self {
1221            engine: Arc::new(RwLock::new(GarbageCollectionEngine::new(config))),
1222        }
1223    }
1224
1225    pub fn should_collect(&self) -> bool {
1226        let mut engine = self.engine.write().unwrap_or_else(|e| e.into_inner());
1227        engine.should_collect()
1228    }
1229
1230    pub fn collect(&self) -> Result<Vec<GCResult>, GCError> {
1231        let mut engine = self.engine.write().unwrap_or_else(|e| e.into_inner());
1232        engine.collect()
1233    }
1234
1235    pub fn get_stats(&self) -> GCStats {
1236        let engine = self.engine.read().unwrap_or_else(|e| e.into_inner());
1237        engine.get_stats().clone()
1238    }
1239
1240    pub fn track_object(
1241        &self,
1242        region_addr: usize,
1243        obj_addr: usize,
1244        size: usize,
1245        type_id: u32,
1246    ) -> Result<(), GCError> {
1247        let mut engine = self.engine.write().unwrap_or_else(|e| e.into_inner());
1248        engine.track_object(region_addr, obj_addr, size, type_id)
1249    }
1250}
1251
1252#[cfg(test)]
1253mod tests {
1254    use super::*;
1255
1256    #[test]
1257    fn test_gc_engine_creation() {
1258        let config = GCConfig::default();
1259        let engine = GarbageCollectionEngine::new(config);
1260        assert!(!engine.collectors.is_empty());
1261    }
1262
1263    #[test]
1264    fn test_region_registration() {
1265        let config = GCConfig::default();
1266        let mut engine = GarbageCollectionEngine::new(config);
1267
1268        engine.register_region(0x1000, 4096, 0);
1269        assert!(engine.memory_regions.contains_key(&0x1000));
1270    }
1271
1272    #[test]
1273    fn test_object_tracking() {
1274        let config = GCConfig::default();
1275        let mut engine = GarbageCollectionEngine::new(config);
1276
1277        engine.register_region(0x1000, 4096, 0);
1278        let result = engine.track_object(0x1000, 0x1100, 64, 1);
1279        assert!(result.is_ok());
1280    }
1281
1282    #[test]
1283    fn test_reference_tracking() {
1284        let mut tracker = ReferenceTracker::new();
1285
1286        tracker.add_root(100);
1287        tracker.add_reference(100, 200);
1288        tracker.add_reference(200, 300);
1289
1290        let reachable = tracker.get_reachable_objects();
1291        assert!(reachable.contains(&100));
1292        assert!(reachable.contains(&200));
1293        assert!(reachable.contains(&300));
1294    }
1295
1296    #[test]
1297    fn test_mark_sweep_collector() {
1298        let config = MarkSweepConfig::default();
1299        let collector = MarkSweepCollector::new(config);
1300
1301        assert_eq!(collector.name(), "MarkSweep");
1302    }
1303
1304    #[test]
1305    fn test_generational_collector() {
1306        let config = GenerationalConfig::default();
1307        let collector = GenerationalCollector::new(config);
1308
1309        assert_eq!(collector.name(), "Generational");
1310    }
1311
1312    #[test]
1313    fn test_incremental_collector() {
1314        let config = IncrementalConfig::default();
1315        let collector = IncrementalCollector::new(config);
1316
1317        assert_eq!(collector.name(), "Incremental");
1318        assert_eq!(*collector.current_phase(), IncrementalPhase::Idle);
1319    }
1320
1321    #[test]
1322    fn test_incremental_collector_marks_then_sweeps_and_returns_to_idle() {
1323        let config = IncrementalConfig::default();
1324        let mut collector = IncrementalCollector::new(config);
1325        let mut tracker = ReferenceTracker::new();
1326
1327        // Object 100 is reachable (rooted); 200 and 300 are not.
1328        tracker.add_root(100);
1329
1330        let mut objects = HashMap::new();
1331        for addr in [100usize, 200, 300] {
1332            objects.insert(
1333                addr,
1334                ObjectMetadata {
1335                    address: addr,
1336                    size: 64,
1337                    type_id: 0,
1338                    ref_count: 0,
1339                    marked: false,
1340                    age: 0,
1341                    last_access: Some(Instant::now()),
1342                    references: Vec::new(),
1343                },
1344            );
1345        }
1346        let mut region = MemoryRegion {
1347            base_addr: 0x1000,
1348            size: 4096,
1349            generation: 0,
1350            objects,
1351            free_bitmap: Vec::new(),
1352            last_collection: None,
1353            collection_count: 0,
1354            utilization: 0.0,
1355        };
1356
1357        // Drive the collector to completion: `collect` returns
1358        // `Err(CollectionIncomplete)` while incremental work remains
1359        // queued, and `Ok(GCResult)` only once marking and sweeping have
1360        // both fully drained.
1361        let mut result = None;
1362        for _ in 0..100 {
1363            match collector.collect(&mut region, &mut tracker) {
1364                Ok(r) => {
1365                    result = Some(r);
1366                    break;
1367                }
1368                Err(GCError::CollectionIncomplete(_)) => continue,
1369                Err(e) => panic!("unexpected GC error: {e:?}"),
1370            }
1371        }
1372        let result = result.expect("incremental collection should complete within 100 slices");
1373
1374        // The two unreachable objects (200, 300; 64 bytes each) must have
1375        // been genuinely swept, not just marked-and-left-behind.
1376        assert_eq!(result.objects_collected, 2);
1377        assert_eq!(result.bytes_collected, 128);
1378        assert!(region.objects.contains_key(&100));
1379        assert!(!region.objects.contains_key(&200));
1380        assert!(!region.objects.contains_key(&300));
1381        assert_eq!(*collector.current_phase(), IncrementalPhase::Idle);
1382    }
1383
1384    #[test]
1385    fn test_thread_safe_gc_engine() {
1386        let config = GCConfig::default();
1387        let engine = ThreadSafeGCEngine::new(config);
1388
1389        let stats = engine.get_stats();
1390        assert_eq!(stats.total_cycles, 0);
1391    }
1392}