use crate::{GcConfig, GcPtr, GcStats, GenerationalAllocator, Result};
use runmat_builtins::Value;
use runmat_time::Instant;
use std::collections::HashSet;
pub struct MarkSweepCollector {
config: GcConfig,
marked_objects: parking_lot::Mutex<HashSet<usize>>,
collections_performed: usize,
total_objects_collected: usize,
}
impl MarkSweepCollector {
pub fn new(config: &GcConfig) -> Self {
Self {
config: config.clone(),
marked_objects: parking_lot::Mutex::new(HashSet::new()),
collections_performed: 0,
total_objects_collected: 0,
}
}
pub fn collect_young_generation(
&mut self,
allocator: &mut GenerationalAllocator,
roots: &[GcPtr<Value>],
stats: &GcStats,
) -> Result<usize> {
let _ = stats; log::debug!("Starting young generation collection");
let start_time = Instant::now();
self.mark_phase(roots, 0)?;
let mut collected = 0usize;
let mut any_survivor = false;
let allocated_ptrs = allocator.young_take_allocations();
let mut promoted_this_cycle = 0usize;
for &ptr in &allocated_ptrs {
let addr = ptr as usize;
if !self.marked_objects.lock().contains(&addr) {
collected += 1;
crate::gc_run_finalizer_for_addr(addr);
unsafe {
std::ptr::drop_in_place(ptr as *mut Value);
}
} else {
allocator.young_mark_survivor(ptr);
if allocator.note_survivor_and_maybe_promote(ptr) {
promoted_this_cycle += 1;
}
any_survivor = true;
}
}
if !any_survivor && !allocator.young_has_survivors() {
allocator.young_reset();
}
self.marked_objects.lock().clear();
self.collections_performed += 1;
self.total_objects_collected += collected;
let duration = start_time.elapsed();
if promoted_this_cycle > 0 {
stats.record_promotion(promoted_this_cycle);
}
log::debug!("Young generation collection completed: {collected} collected, {promoted_this_cycle} promoted in {duration:?}");
Ok(collected)
}
pub fn collect_all_generations(
&mut self,
allocator: &mut GenerationalAllocator,
roots: &[GcPtr<Value>],
stats: &GcStats,
) -> Result<usize> {
log::debug!("Starting full heap collection");
let start_time = Instant::now();
self.mark_phase(roots, usize::MAX)?;
let collected = {
let mut temp_collector = MarkSweepCollector::new(&self.config);
temp_collector.marked_objects = std::mem::take(&mut self.marked_objects);
let c = temp_collector.collect_young_generation(allocator, roots, stats)?;
self.marked_objects = temp_collector.marked_objects; c
};
self.collections_performed += 1;
self.total_objects_collected += collected;
let duration = start_time.elapsed();
log::debug!("Full heap collection completed: {collected} collected in {duration:?}");
Ok(collected)
}
fn mark_phase(&mut self, roots: &[GcPtr<Value>], max_generation: usize) -> Result<()> {
log::trace!("Starting mark phase with {} roots", roots.len());
self.marked_objects.lock().clear();
for root in roots.iter().cloned() {
let root_ptr: GcPtr<Value> = root;
if !root_ptr.is_null() {
self.mark_object(root_ptr, max_generation)?;
}
}
log::trace!(
"Mark phase completed: {} objects marked",
self.marked_objects.lock().len()
);
Ok(())
}
fn mark_object(&mut self, obj: GcPtr<Value>, max_generation: usize) -> Result<()> {
let ptr = unsafe { obj.as_raw() } as *const u8;
let ptr_addr = ptr as usize;
if self.marked_objects.lock().contains(&ptr_addr) {
return Ok(());
}
self.marked_objects.lock().insert(ptr_addr);
match &*obj {
Value::Cell(cells) => {
for cell_value in &cells.data {
self.mark_value_contents(cell_value, max_generation)?;
}
}
Value::HandleObject(h) => {
let tgt = h.target.clone();
if !tgt.is_null() {
self.mark_object(tgt, max_generation)?;
}
}
Value::Listener(l) => {
let tgt = l.target.clone();
if !tgt.is_null() {
self.mark_object(tgt, max_generation)?;
}
let cb = l.callback.clone();
if !cb.is_null() {
self.mark_object(cb, max_generation)?;
}
}
Value::Tensor(_) | Value::SparseTensor(_) | Value::ComplexTensor(_) => {
}
Value::GpuTensor(_) => {
}
Value::String(_) => {
}
Value::StringArray(_sa) => {
}
Value::Int(_)
| Value::Num(_)
| Value::Complex(_, _)
| Value::Bool(_)
| Value::LogicalArray(_)
| Value::Symbolic(_) => {
}
Value::FunctionHandle(_)
| Value::ExternalFunctionHandle(_)
| Value::MethodFunctionHandle(_)
| Value::BoundFunctionHandle { .. } => {}
Value::ClassRef(_) => {}
Value::Closure(c) => {
for v in &c.captures {
self.mark_value_contents(v, max_generation)?;
}
}
Value::Object(obj) => {
for v in obj.properties.values() {
self.mark_value_contents(v, max_generation)?;
}
}
Value::Struct(st) => {
for v in st.fields.values() {
self.mark_value_contents(v, max_generation)?;
}
}
Value::OutputList(values) => {
for v in values {
self.mark_value_contents(v, max_generation)?;
}
}
Value::MException(_e) => {
}
Value::CharArray(_ca) => {}
}
Ok(())
}
#[allow(clippy::only_used_in_recursion)]
fn mark_value_contents(&mut self, value: &Value, _max_generation: usize) -> Result<()> {
match value {
Value::Cell(cells) => {
for cell_value in &cells.data {
self.mark_value_contents(cell_value, _max_generation)?;
}
}
Value::HandleObject(h) => {
let tgt = h.target.clone();
if !tgt.is_null() {
self.mark_object(tgt, _max_generation)?;
}
}
Value::Listener(l) => {
let tgt = l.target.clone();
if !tgt.is_null() {
self.mark_object(tgt, _max_generation)?;
}
let cb = l.callback.clone();
if !cb.is_null() {
self.mark_object(cb, _max_generation)?;
}
}
Value::StringArray(_sa) => {}
Value::GpuTensor(_) => {}
Value::FunctionHandle(_)
| Value::ExternalFunctionHandle(_)
| Value::MethodFunctionHandle(_)
| Value::BoundFunctionHandle { .. } => {}
Value::ClassRef(_) => {}
Value::Closure(c) => {
for v in &c.captures {
self.mark_value_contents(v, _max_generation)?;
}
}
Value::Object(obj) => {
for v in obj.properties.values() {
self.mark_value_contents(v, _max_generation)?;
}
}
Value::Struct(st) => {
for v in st.fields.values() {
self.mark_value_contents(v, _max_generation)?;
}
}
Value::OutputList(values) => {
for v in values {
self.mark_value_contents(v, _max_generation)?;
}
}
Value::MException(_e) => {}
Value::CharArray(_ca) => {}
_ => {
}
}
Ok(())
}
#[allow(dead_code)]
fn sweep_young_generation(
&mut self,
_allocator: &mut GenerationalAllocator,
_stats: &GcStats,
) -> Result<usize> {
log::trace!("Starting sweep of young generation");
let mut collected = 0;
collected += self.simulate_sweep(_stats, "young generation");
log::trace!("Young generation sweep completed: {collected} objects collected");
Ok(collected)
}
#[allow(dead_code)]
fn sweep_all_generations(
&mut self,
_allocator: &mut GenerationalAllocator,
_stats: &GcStats,
) -> Result<usize> {
log::trace!("Starting sweep of all generations");
let mut collected = 0;
for generation in 0..self.config.num_generations {
collected += self.simulate_sweep(_stats, &format!("generation {generation}"));
}
log::trace!("Full heap sweep completed: {collected} objects collected");
Ok(collected)
}
#[allow(dead_code)]
fn simulate_sweep(&self, _stats: &GcStats, description: &str) -> usize {
let marked_objects = self.marked_objects.lock();
let simulated_collected = if marked_objects.is_empty() {
10 } else {
(marked_objects.len() as f64 * 0.2) as usize
};
log::trace!("Simulated sweep of {description}: {simulated_collected} objects collected");
simulated_collected
}
#[allow(dead_code)]
fn promote_survivors(
&mut self,
_allocator: &mut GenerationalAllocator,
_stats: &GcStats,
) -> Result<usize> {
log::trace!("Starting survivor promotion");
let marked_len = self.marked_objects.lock().len();
let promoted = if marked_len > 10 {
let promotion_count = marked_len / 4;
_stats.record_promotion(promotion_count);
promotion_count
} else {
0
};
log::trace!("Promotion completed: {promoted} objects promoted");
Ok(promoted)
}
pub fn reconfigure(&mut self, config: &GcConfig) -> Result<()> {
self.config = config.clone();
Ok(())
}
pub fn stats(&self) -> CollectorStats {
CollectorStats {
collections_performed: self.collections_performed,
total_objects_collected: self.total_objects_collected,
average_objects_per_collection: if self.collections_performed > 0 {
self.total_objects_collected as f64 / self.collections_performed as f64
} else {
0.0
},
marked_objects_count: self.marked_objects.lock().len(),
}
}
}
#[derive(Debug, Clone)]
pub struct CollectorStats {
pub collections_performed: usize,
pub total_objects_collected: usize,
pub average_objects_per_collection: f64,
pub marked_objects_count: usize,
}
pub struct ConcurrentCollector {
base_collector: MarkSweepCollector,
}
impl ConcurrentCollector {
pub fn new(config: &GcConfig) -> Self {
Self {
base_collector: MarkSweepCollector::new(config),
}
}
pub fn start_concurrent_collection(
&mut self,
_roots: &[GcPtr<Value>],
) -> Result<CollectionHandle> {
let objects_collected = 0; Ok(CollectionHandle {
is_completed: true,
objects_collected,
})
}
pub fn base_collector(&self) -> &MarkSweepCollector {
&self.base_collector
}
}
pub struct CollectionHandle {
pub is_completed: bool,
pub objects_collected: usize,
}
impl CollectionHandle {
pub fn wait_for_completion(&mut self) -> Result<usize> {
Ok(self.objects_collected)
}
pub fn is_complete(&self) -> bool {
self.is_completed
}
}
pub struct IncrementalCollector {
base_collector: MarkSweepCollector,
current_phase: CollectionPhase,
work_budget: usize, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CollectionPhase {
Idle,
Marking,
Sweeping,
Promoting,
}
impl IncrementalCollector {
pub fn new(config: &GcConfig) -> Self {
Self {
base_collector: MarkSweepCollector::new(config),
current_phase: CollectionPhase::Idle,
work_budget: 1000, }
}
pub fn do_incremental_work(&mut self, work_budget: usize) -> Result<IncrementalProgress> {
let actual_budget = work_budget.min(self.work_budget);
let start_time = Instant::now();
let objects_processed = 10;
std::thread::sleep(std::time::Duration::from_micros(1));
let work_done = start_time.elapsed().as_micros() as usize;
let work_done = work_done.max(1); let is_completed = true;
if is_completed {
self.current_phase = CollectionPhase::Idle;
}
Ok(IncrementalProgress {
phase: self.current_phase,
work_done: work_done.min(actual_budget),
is_collection_complete: is_completed,
objects_processed,
})
}
pub fn is_collecting(&self) -> bool {
self.current_phase != CollectionPhase::Idle
}
pub fn work_budget(&self) -> usize {
self.work_budget
}
pub fn set_work_budget(&mut self, budget: usize) {
self.work_budget = budget;
}
pub fn base_collector(&self) -> &MarkSweepCollector {
&self.base_collector
}
}
#[derive(Debug, Clone)]
pub struct IncrementalProgress {
pub phase: CollectionPhase,
pub work_done: usize,
pub is_collection_complete: bool,
pub objects_processed: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{GcConfig, GcStats, GenerationalAllocator};
#[test]
fn test_mark_sweep_collector_creation() {
let config = GcConfig::default();
let collector = MarkSweepCollector::new(&config);
let stats = collector.stats();
assert_eq!(stats.collections_performed, 0);
assert_eq!(stats.total_objects_collected, 0);
}
#[test]
fn test_young_generation_collection() {
let config = GcConfig::default();
let mut collector = MarkSweepCollector::new(&config);
let mut allocator = GenerationalAllocator::new(&config);
let stats = GcStats::new();
let roots = vec![];
let _collected = collector
.collect_young_generation(&mut allocator, &roots, &stats)
.expect("collection should succeed");
let collector_stats = collector.stats();
assert_eq!(collector_stats.collections_performed, 1);
}
#[test]
fn test_full_heap_collection() {
let config = GcConfig::default();
let mut collector = MarkSweepCollector::new(&config);
let mut allocator = GenerationalAllocator::new(&config);
let stats = GcStats::new();
let roots = vec![];
let _collected = collector
.collect_all_generations(&mut allocator, &roots, &stats)
.expect("collection should succeed");
let collector_stats = collector.stats();
assert_eq!(collector_stats.collections_performed, 1);
}
#[test]
fn test_concurrent_collector() {
let config = GcConfig::default();
let mut collector = ConcurrentCollector::new(&config);
let roots = vec![];
let handle = collector
.start_concurrent_collection(&roots)
.expect("should start collection");
assert!(handle.is_complete());
}
#[test]
fn test_incremental_collector() {
let config = GcConfig::default();
let mut collector = IncrementalCollector::new(&config);
assert!(!collector.is_collecting());
let progress = collector.do_incremental_work(100).expect("should do work");
assert!(progress.work_done > 0);
}
#[test]
fn test_mark_phase_with_roots() {
let config = GcConfig::default();
let mut collector = MarkSweepCollector::new(&config);
let roots = vec![GcPtr::null()];
let result = collector.mark_phase(&roots, 0);
assert!(result.is_ok());
assert_eq!(collector.marked_objects.lock().len(), 0);
}
}