mmtk/util/
treadmill.rs

1use std::collections::HashSet;
2use std::mem::swap;
3use std::sync::Mutex;
4
5use crate::util::ObjectReference;
6
7use super::object_enum::ObjectEnumerator;
8
9/// A data structure for recording objects in the LOS.
10///
11/// All operations are protected by a single mutex [`TreadMill::sync`].
12pub struct TreadMill {
13    sync: Mutex<TreadMillSync>,
14}
15
16/// The synchronized part of [`TreadMill`]
17#[derive(Default)]
18struct TreadMillSync {
19    /// The from-space.  During GC, it contains old objects with unknown liveness.
20    from_space: HashSet<ObjectReference>,
21    /// The to-space.  During mutator time, it contains old objects; during GC, it contains objects
22    /// determined to be live.
23    to_space: HashSet<ObjectReference>,
24    /// The collection nursery.  During GC, it contains young objects with unknown liveness.
25    collect_nursery: HashSet<ObjectReference>,
26    /// The allocation nursery.  During mutator time, it contains young objects; during GC, it
27    /// remains empty.
28    alloc_nursery: HashSet<ObjectReference>,
29}
30
31impl std::fmt::Debug for TreadMill {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        let sync = self.sync.lock().unwrap();
34        f.debug_struct("TreadMill")
35            .field("from_space", &sync.from_space)
36            .field("to_space", &sync.to_space)
37            .field("collect_nursery", &sync.collect_nursery)
38            .field("alloc_nursery", &sync.alloc_nursery)
39            .finish()
40    }
41}
42
43impl TreadMill {
44    pub fn new() -> Self {
45        TreadMill {
46            sync: Mutex::new(Default::default()),
47        }
48    }
49
50    /// Add an object to the treadmill.
51    ///
52    /// New objects are normally added to `alloc_nursery`.  But when allocating as live (e.g. when
53    /// concurrent marking is active), we directly add into the `to_space`.
54    pub fn add_to_treadmill(&self, object: ObjectReference, nursery: bool) {
55        let mut sync = self.sync.lock().unwrap();
56        if nursery {
57            trace!("Adding {} to alloc_nursery", object);
58            sync.alloc_nursery.insert(object);
59        } else {
60            trace!("Adding {} to to_space", object);
61            sync.to_space.insert(object);
62        }
63    }
64
65    /// Take all objects from the `collect_nursery`.  This is called during sweeping at which time
66    /// all unreachable young objects are in the collection nursery.
67    pub fn collect_nursery(&self) -> impl IntoIterator<Item = ObjectReference> {
68        let mut sync = self.sync.lock().unwrap();
69        std::mem::take(&mut sync.collect_nursery)
70    }
71
72    /// Take all objects from the `from_space`.  This is called during sweeping at which time all
73    /// unreachable old objects are in the from-space.
74    pub fn collect_mature(&self) -> impl IntoIterator<Item = ObjectReference> {
75        let mut sync = self.sync.lock().unwrap();
76        std::mem::take(&mut sync.from_space)
77    }
78
79    /// Move an object to `to_space`.  Called when an object is determined to be reachable.
80    pub fn copy(&self, object: ObjectReference, is_in_nursery: bool) {
81        let mut sync = self.sync.lock().unwrap();
82        if is_in_nursery {
83            debug_assert!(
84                sync.collect_nursery.contains(&object),
85                "copy source object ({}) must be in collect_nursery",
86                object
87            );
88            sync.collect_nursery.remove(&object);
89        } else {
90            debug_assert!(
91                sync.from_space.contains(&object),
92                "copy source object ({}) must be in from_space",
93                object
94            );
95            sync.from_space.remove(&object);
96        }
97        sync.to_space.insert(object);
98    }
99
100    /// Return true if the to-space is empty.
101    pub fn is_to_space_empty(&self) -> bool {
102        let sync = self.sync.lock().unwrap();
103        sync.to_space.is_empty()
104    }
105
106    /// Return true if the from-space is empty.
107    pub fn is_from_space_empty(&self) -> bool {
108        let sync = self.sync.lock().unwrap();
109        sync.from_space.is_empty()
110    }
111
112    /// Return true if the allocation nursery is empty.
113    pub fn is_alloc_nursery_empty(&self) -> bool {
114        let sync = self.sync.lock().unwrap();
115        sync.alloc_nursery.is_empty()
116    }
117
118    /// Return true if the collection nursery is empty.
119    pub fn is_collect_nursery_empty(&self) -> bool {
120        let sync = self.sync.lock().unwrap();
121        sync.collect_nursery.is_empty()
122    }
123
124    /// Flip object sets.
125    ///
126    /// It will flip the allocation nursery and the collection nursery.
127    ///
128    /// If `full_heap` is true, it will also flip the from-space and the to-space.
129    pub fn flip(&mut self, full_heap: bool) {
130        let sync = self.sync.get_mut().unwrap();
131        swap(&mut sync.alloc_nursery, &mut sync.collect_nursery);
132        trace!("Flipped alloc_nursery and collect_nursery");
133        if full_heap {
134            swap(&mut sync.from_space, &mut sync.to_space);
135            trace!("Flipped from_space and to_space");
136        }
137    }
138
139    /// Enumerate objects.
140    ///
141    /// Objects in the allocation nursery and the to-spaces are always enumerated.  They include all
142    /// objects during mutator time, and objects determined to be live during a GC.
143    ///
144    /// If `all` is true, it will enumerate the collection nursery and the from-space, too.
145    pub(crate) fn enumerate_objects(&self, enumerator: &mut dyn ObjectEnumerator, all: bool) {
146        let sync = self.sync.lock().unwrap();
147        let mut enumerated = 0usize;
148        let mut visit_objects = |set: &HashSet<ObjectReference>| {
149            for object in set.iter() {
150                enumerator.visit_object(*object);
151                enumerated += 1;
152            }
153        };
154        visit_objects(&sync.alloc_nursery);
155        visit_objects(&sync.to_space);
156
157        if all {
158            visit_objects(&sync.collect_nursery);
159            visit_objects(&sync.from_space);
160        }
161
162        debug!("Enumerated {enumerated} objects in LOS.  all: {all}.  from_space: {fs}, to_space: {ts}, collect_nursery: {cn}, alloc_nursery: {an}",
163            fs=sync.from_space.len(),
164            ts=sync.to_space.len(),
165            cn=sync.collect_nursery.len(),
166            an=sync.alloc_nursery.len(),
167        );
168    }
169}
170
171impl Default for TreadMill {
172    fn default() -> Self {
173        Self::new()
174    }
175}