rsgc 1.1.0

Concurrent GC library for Rust
Documentation
use std::time::Instant;

use crate::{formatted_size, heap::{heap::heap, region::HeapOptions}, utils::number_seq::TruncatedSeq};

use super::Heuristics;

pub struct StaticHeuristics {
    degenerated_cycles_in_a_row: usize,
    successful_cycles_in_a_row: usize,
    cycle_start: Instant,
    last_cycle_end: Instant,

    gc_times_learned: usize,
    gc_time_penalties: isize,
    gc_time_history: Box<TruncatedSeq>,
}

impl StaticHeuristics {
    pub fn new(opts: &mut HeapOptions) -> Box<dyn Heuristics> {
        opts.uncommit = true;
        if opts.allocation_threshold == 0 {
            opts.allocation_threshold = 10;
        }

        if opts.immediate_threshold == 90 {
            opts.immediate_threshold = 100;
        }

        if opts.uncommit_delay == 5 * 60 * 1000 {
            opts.uncommit_delay = 1000;
        }

        if opts.guaranteed_gc_interval == 5 * 60 * 1000 {
            opts.guaranteed_gc_interval = 30000;
        }

        Box::new(Self {
            degenerated_cycles_in_a_row: 0,
            successful_cycles_in_a_row: 0,
            cycle_start: Instant::now(),
            last_cycle_end: Instant::now(),

            gc_times_learned: 0,
            gc_time_penalties: 0,
            gc_time_history: Box::new(TruncatedSeq::new(10, opts.adaptive_decay_factor)),
        })
    }
}

impl Heuristics for StaticHeuristics {
    fn gc_time_penalties(&self) -> isize {
        self.gc_time_penalties
    }

    fn set_gc_time_penalties(&mut self, val: isize) {
        self.gc_time_penalties = val;
    }

    fn gc_time_history(&self) -> &TruncatedSeq {
        self.gc_time_history.as_ref()
    }

    fn gc_time_history_mut(&mut self) -> &mut TruncatedSeq {
        self.gc_time_history.as_mut()
    }

    fn degenerate_cycles_in_a_row(&self) -> usize {
        self.degenerated_cycles_in_a_row
    }

    fn set_degenerate_cycles_in_a_row(&mut self, degenerate_cycles_in_a_row: usize) {
        self.degenerated_cycles_in_a_row = degenerate_cycles_in_a_row;
    }

    fn successful_cycles_in_a_row(&self) -> usize {
        self.successful_cycles_in_a_row
    }

    fn set_successful_cycles_in_a_row(&mut self, successful_cycles_in_a_row: usize) {
        self.successful_cycles_in_a_row = successful_cycles_in_a_row;
    }

    fn cycle_start(&self) -> Instant {
        self.cycle_start
    }

    fn set_cycle_start(&mut self, cycle_start: Instant) {
        self.cycle_start = cycle_start;
    }

    fn last_cycle_end(&self) -> Instant {
        self.last_cycle_end
    }

    fn set_last_cycle_end(&mut self, last_cycle_end: Instant) {
        self.last_cycle_end = last_cycle_end;
    }

    fn gc_times_learned(&self) -> usize {
        self.gc_times_learned
    }

    fn set_gc_times_learned(&mut self, gc_times_learned: usize) {
        self.gc_times_learned = gc_times_learned;
    }

    fn should_start_gc(&mut self) -> bool {
        let heap = heap();

        let max_capacity = heap.max_capacity();
        let available = heap.free_set().available();

        let threshold_bytes_allocated = max_capacity / 100 * heap.options().allocation_threshold;
        let min_threshold = max_capacity / 100 * heap.options().min_free_threshold;

        if available < min_threshold {
            log::info!(
                "Trigger: Free ({}) is below minimum threshold ({})",
                formatted_size(available),
                formatted_size(min_threshold)
            );
            return true;
        }

        let bytes_allocated = heap.bytes_allocated_since_gc_start();

        if bytes_allocated > threshold_bytes_allocated {
            log::info!(
                "Trigger: Allocated since last cycle ({}) is larger than allocation threshold ({})",
                formatted_size(bytes_allocated),
                formatted_size(threshold_bytes_allocated)
            );
            return true;
        }

        if heap.options().guaranteed_gc_interval > 0 {
            let last_time_ms = (Instant::now() - self.last_cycle_end()).as_millis();

            if last_time_ms > heap.options().guaranteed_gc_interval as u128 {
                log::info!(target: "gc", "Trigger: Time since last GC ({} ms) is larger than guaranteed interval ({} ms)", last_time_ms, heap.options().guaranteed_gc_interval);
                return true;
            }
        }

        false
    }
}