1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/// ! Cycle collecting reference counted pointers for Rust with a safe api.
/// !
/// ! This crate is most useful when you have a data structure with inter-dependent nodes of
/// ! arbitrary lifetimes and no clear parent-children relationships.
/// !
/// ! If you can be certain that the layout of your data will be acyclic, or [`std::rc::Weak`]
/// ! would be sufficient to prevent leaks, this type is probably not a good fit. If you know all
/// ! of your data will be dropped after a certain phase of your program completes, you will
/// ! probably prefer arena allocation through a crate like `typed-arena`, `generational-arena`,
/// ! etc.
/// !
/// ! # Basic Example
/// ! ```
/// ! # use std::cell::RefCell;
/// ! # use tracing_rc::rc::{
/// ! #     collect_full,
/// ! #     Gc,
/// ! #     GcVisitor,
/// ! #     Traceable,
/// ! # };
/// !
/// ! struct GraphNode<T: 'static> {
/// !     data: T,
/// !     edge: Option<Gc<RefCell<GraphNode<T>>>>,
/// ! }
/// !
/// ! impl<T> Traceable for GraphNode<T> {
/// !     fn visit_children(&self, visitor: &mut GcVisitor) {
/// !         self.edge.visit_children(visitor);
/// !     }
/// ! }
/// !
/// ! # fn main() {
/// !     {
/// !         let node_a = Gc::new(RefCell::new(GraphNode {
/// !             data: 10,
/// !             edge: None,
/// !         }));
/// !         let node_b = Gc::new(RefCell::new(GraphNode {
/// !             data: 11,
/// !             edge: None,
/// !         }));
/// !         let node_c = Gc::new(RefCell::new(GraphNode {
/// !             data: 12,
/// !             edge: Some(node_a.clone()),
/// !         }));
/// !
/// !         node_a.borrow_mut().edge = Some(node_b.clone());
/// !         node_b.borrow_mut().edge = Some(node_c);
/// !
/// !         let a = node_a.borrow();
/// !         let b = a.edge.as_ref().unwrap().borrow();
/// !         let c = b.edge.as_ref().unwrap().borrow();
/// !
/// !         assert_eq!(a.data, c.edge.as_ref().unwrap().borrow().data);
/// !         // all of the nodes go out of scope at this point and would normally be leaked.
/// !     }
/// !
/// !     // In this simple example, we always have cycles and our program is complete after this,
/// !     // so we can't take advantage of the young generation picking up acyclic pointers without
/// !     // tracing.
/// !     collect_full();
/// !
/// !     // All leaked nodes have been cleaned up!
/// ! # }
/// ! ```
pub mod rc;

/// Controls the style of collection carried out.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CollectionType {
    /// Do a simple pass over the young gen, collecting non-cyclical pointers
    /// and moving old pointers to the old gen. Then perform a cycle-tracing
    /// collection over the old gen.
    Default,
    /// Only run collection for the young gen. This may still move pointers to the old gen if they
    /// qualify based on CollectOptions::old_gen_threshold
    YoungOnly,
}

impl Default for CollectionType {
    fn default() -> Self {
        Self::Default
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CollectOptions {
    /// The number of times a pointer may be seen in the young gen before moving it to the old
    /// gen for a full tracing collection. Setting this to zero will cause all pointers to move to
    /// the old gen if they cannot be immediately cleaned up.
    pub old_gen_threshold: usize,
    // The kind of collection to perform, e.g. just the young gen, or full tracing of both old &
    // young gen.
    pub kind: CollectionType,
}

impl CollectOptions {
    pub const DEFAULT: CollectOptions = CollectOptions {
        old_gen_threshold: 5,
        kind: CollectionType::Default,
    };
    pub const TRACE_AND_COLLECT_ALL: CollectOptions = Self::DEFAULT.set_old_gen_threshold(0);
    pub const YOUNG_ONLY: CollectOptions = Self::DEFAULT.set_kind(CollectionType::YoungOnly);

    pub const fn set_kind(self, kind: CollectionType) -> Self {
        let Self {
            old_gen_threshold,
            kind: _,
        } = self;

        Self {
            old_gen_threshold,
            kind,
        }
    }

    pub const fn set_old_gen_threshold(self, threshold: usize) -> Self {
        let Self {
            old_gen_threshold: _,
            kind,
        } = self;

        Self {
            old_gen_threshold: threshold,
            kind,
        }
    }
}

impl Default for CollectOptions {
    fn default() -> Self {
        Self::DEFAULT
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Status {
    Live,
    RecentlyDecremented,
    Dead,
}