rsgc 1.1.0

Concurrent GC library for Rust
Documentation
use std::time::{Instant, Duration};

pub trait PhaseVisitor {
    fn visit(&mut self, phase: &GCPhase);
    fn visit_mut(&mut self, phase: &mut GCPhase);
}


#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum PhaseType {
    Pause,
    Concurrent,
}

pub struct GCPhase {
    name: &'static str,
    level: i32,
    start: Instant,
    end: Instant,
    typ: PhaseType
}

impl GCPhase {
    pub fn set_name(&mut self, name: &'static str) {
        self.name = name;
    }

    pub fn set_level(&mut self, level: i32) {
        self.level = level;
    }

    pub fn set_start(&mut self, start: Instant) {
        self.start = start;
    }

    pub fn set_end(&mut self, end: Instant) {
        self.end = end;
    }

    pub fn set_type(&mut self, typ: PhaseType) {
        self.typ = typ;
    }

    pub fn name(&self) -> &'static str {
        self.name
    }

    pub fn level(&self) -> i32 {
        self.level
    }

    pub fn start(&self) -> Instant {
        self.start
    }

    pub fn end(&self) -> Instant {
        self.end
    }

    pub fn typ(&self) -> PhaseType {
        self.typ
    }

    pub fn duration(&self) -> std::time::Duration {
        self.end - self.start
    }

    pub fn new(name: &'static str, level: i32, start: Instant, end: Instant, typ: PhaseType) -> Self {
        Self {
            name,
            level,
            start,
            end,
            typ
        }
    }
    
    pub fn accept(&self, visitor: &mut impl PhaseVisitor) {
        visitor.visit(self);
    }

    pub fn accept_mut(&mut self, visitor: &mut impl PhaseVisitor) {
        visitor.visit_mut(self);
    }
}

pub struct PhasesStack {
    phase_indices: [i32; 6],
    next_phase_level: i32
}

impl PhasesStack {
    pub const fn new() -> Self {
        Self {
            phase_indices: [0; 6],
            next_phase_level: 0
        }
    }

    pub fn clear(&mut self) {
        self.next_phase_level = 0;
    }

    pub fn push(&mut self, phase_index: i32) {
        self.phase_indices[self.next_phase_level as usize] = phase_index;
        self.next_phase_level += 1;
    }

    pub fn pop(&mut self) -> i32 {
        self.next_phase_level -= 1;
        self.phase_indices[self.next_phase_level as usize]
    }

    pub fn count(&self) -> i32 {
        self.next_phase_level
    }

    pub fn phase_index(&self, level: i32) -> i32 {
        self.phase_indices[level as usize]
    }
}


pub struct TimePartitions {
    phases: Vec<Box<GCPhase>>,
    active_phases: PhasesStack,
    sum_of_pauses: Duration,
    longest_pause: Duration,
}

impl TimePartitions {
    pub fn new() -> Self {
        Self {
            phases: Vec::with_capacity(100),
            active_phases: PhasesStack::new(),
            sum_of_pauses: Duration::from_secs(0),
            longest_pause: Duration::from_secs(0),
        }
    }

    pub fn clear(&mut self) {
        self.phases.clear();
        self.active_phases.clear();
        self.sum_of_pauses = Duration::from_secs(0);
        self.longest_pause = Duration::from_secs(0);
    }

    pub fn report_gc_phase_start(&mut self, name: &'static str, time: Instant, typ: PhaseType) {
        let level = self.active_phases.count();

        let phase = GCPhase::new(
            name,
            level,
            time,
            Instant::now(),
            typ 
        );
        let index = self.phases.len();
        self.phases.push(Box::new(phase));
        self.active_phases.push(index as _);
    }

    pub fn report_gc_phase_start_top_level(&mut self, name: &'static str, time: Instant, typ: PhaseType) {
        let level = self.active_phases.count();
        assert!(level == 0, "must be a top-level phase");
        self.report_gc_phase_start(name, time, typ)
    }

    pub fn report_gc_phase_start_sub_phase(&mut self, name: &'static str, time: Instant) {
        let level = self.active_phases.count();
        assert!(level > 0, "must be a sub-phase");
        let typ = self.current_phase_type();
        self.report_gc_phase_start(name, time, typ);
    }

    pub fn report_gc_phase_end(&mut self, end: Instant) {
        let phase_index = self.active_phases.pop();
        let phase = &mut self.phases[phase_index as usize];
        phase.set_end(end);

        if phase.typ() == PhaseType::Pause && phase.level() == 0 {
            let pause = phase.end() - phase.start();

            self.sum_of_pauses += pause;
            self.longest_pause = std::cmp::max(self.longest_pause, pause);
        }
    }

    pub fn num_phases(&self) -> usize {
        self.phases.len()
    }

    pub fn phase(&self, index: usize) -> &GCPhase {
        &self.phases[index]
    }

    pub fn phase_mut(&mut self, index: usize) -> &mut GCPhase {
        &mut self.phases[index]
    }

    pub fn current_phase_type(&self) -> PhaseType {
        let index = self.active_phases.phase_index(self.active_phases.count() - 1);
        self.phases[index as usize].typ()
    }
}

pub struct GCTimer {
    gc_start: Instant,
    gc_end: Instant,
    time_partitions: TimePartitions,
    concurrent: bool
}

impl GCTimer {
    pub fn time_partitions(&self) -> &TimePartitions {
        &self.time_partitions
    }

    pub fn register_gc_start(&mut self, time: Instant) {
        self.time_partitions.clear();
        self.gc_start = time;
        if !self.concurrent {
            self.register_gc_pause_start("GC Pause", time);
        }
    }

    pub fn register_gc_end(&mut self, time: Instant) {
        if !self.concurrent {
            self.register_gc_pause_end(time);
        }
        self.gc_end = time;
    }

    pub fn register_gc_pause_start(&mut self, name: &'static str, time: Instant) {
        self.time_partitions.report_gc_phase_start_top_level(name, time, PhaseType::Pause);
    }

    pub fn register_gc_pause_end(&mut self, time: Instant) {
        self.time_partitions.report_gc_phase_end(time);
    }

    pub fn register_gc_phase_start(&mut self, name: &'static str, time: Instant) {
        self.time_partitions.report_gc_phase_start_sub_phase(name, time);
    }

    pub fn register_gc_phase_end(&mut self, time: Instant) {
        self.time_partitions.report_gc_phase_end(time);
    }

    pub fn register_gc_concurrent_start(&mut self,name: &'static str, time: Instant) {
        self.time_partitions.report_gc_phase_start_top_level(name, time, PhaseType::Concurrent);
    }

    pub fn register_gc_concurrent_end(&mut self, time: Instant) {
        self.time_partitions.report_gc_phase_end(time);
    }

    pub fn new(concurrent: bool) -> Self {
        Self {
            gc_start: Instant::now(),
            gc_end: Instant::now(),
            time_partitions: TimePartitions::new(),
            concurrent
        }
    }
}