mmtk/plan/tracing/gc_work/
closure.rs1use std::marker::PhantomData;
2
3use crate::{
4 plan::{
5 tracing::{gc_work::DefaultObjectTracerContext, SlotOfTrace, Trace},
6 VectorObjectQueue, VectorQueue,
7 },
8 scheduler::{GCWork, GCWorker, GCWorkerShared, WorkBucketStage},
9 util::{ObjectReference, VMWorkerThread},
10 vm::{slot::Slot, ObjectTracerContext, Scanning, VMBinding},
11 MMTK,
12};
13
14pub struct ProcessSlots<T: Trace> {
21 slots: Vec<SlotOfTrace<T>>,
22 bucket: WorkBucketStage,
23}
24
25impl<T: Trace> ProcessSlots<T> {
26 const SCAN_OBJECTS_IMMEDIATELY: bool = true;
27
28 pub fn new(slots: Vec<SlotOfTrace<T>>, bucket: WorkBucketStage) -> Self {
29 Self { slots, bucket }
30 }
31
32 fn process_slots(
33 &mut self,
34 worker: &mut GCWorker<T::VM>,
35 trace: T,
36 ) -> VectorQueue<ObjectReference> {
37 let mut queue = VectorObjectQueue::new();
38
39 for slot in self.slots.iter() {
40 if let Some(object) = slot.load() {
41 let new_object = trace.trace_object(worker, object, &mut queue);
42 if T::may_move_objects() && new_object != object {
43 slot.store(new_object);
44 }
45 }
46 }
47
48 queue
49 }
50
51 fn flush(&mut self, worker: &mut GCWorker<T::VM>, mut queue: VectorQueue<ObjectReference>) {
52 if queue.is_empty() {
53 return;
54 }
55
56 let queued_objects = queue.take();
57 let mut work = ProcessNodes::<T>::new(queued_objects, self.bucket);
58
59 if Self::SCAN_OBJECTS_IMMEDIATELY {
60 work.do_work(worker, worker.mmtk);
61 } else {
62 worker.add_work(self.bucket, work);
63 }
64 }
65}
66
67impl<T: Trace> GCWork<T::VM> for ProcessSlots<T> {
68 fn do_work(&mut self, worker: &mut GCWorker<T::VM>, mmtk: &'static MMTK<T::VM>) {
69 probe!(mmtk, process_slots, self.slots.len());
70
71 let trace = T::from_mmtk(mmtk);
72
73 #[cfg(feature = "extreme_assertions")]
74 if crate::util::slot_logger::should_check_duplicate_slots(mmtk.get_plan()) {
75 for slot in self.slots.iter() {
76 mmtk.slot_logger.log_slot(*slot);
78 }
79 }
80
81 let queue = self.process_slots(worker, trace);
82
83 self.flush(worker, queue);
84 }
85}
86
87pub struct ProcessNodes<T: Trace> {
97 objects: Vec<ObjectReference>,
98 bucket: WorkBucketStage,
99 phantom_data: PhantomData<T>,
100}
101
102impl<T: Trace> ProcessNodes<T> {
103 pub fn new(objects: Vec<ObjectReference>, bucket: WorkBucketStage) -> Self {
104 Self {
105 objects,
106 bucket,
107 phantom_data: PhantomData,
108 }
109 }
110
111 fn try_enqueue_slots(
112 &mut self,
113 worker: &mut GCWorker<T::VM>,
114 tls: VMWorkerThread,
115 trace: &T,
116 ) -> Vec<ObjectReference> {
117 let mut scan_later = Vec::new();
119
120 let mut slots = VectorQueue::new();
121
122 let flush = |slots: &mut VectorQueue<_>, worker: &mut GCWorker<T::VM>| {
123 let buffer = slots.take();
124 let work_packet = ProcessSlots::<T>::new(buffer, self.bucket);
125 worker.add_work(self.bucket, work_packet);
126 };
127
128 if crate::util::rust_util::unlikely(*worker.mmtk.get_options().count_live_bytes_in_gc) {
137 let mut live_bytes_stats = worker.shared.live_bytes_per_space.borrow_mut();
139 for object in self.objects.iter().copied() {
140 GCWorkerShared::<T::VM>::increase_live_bytes(&mut live_bytes_stats, object);
141 }
142 }
143
144 for object in self.objects.iter().copied() {
145 if <T::VM as VMBinding>::VMScanning::support_slot_enqueuing(tls, object) {
146 trace!("Scan object (slot) {}", object);
147 <T::VM as VMBinding>::VMScanning::scan_object(tls, object, &mut |slot| {
149 slots.push(slot);
150 if slots.is_full() {
151 flush(&mut slots, worker);
152 }
153 });
154 trace.post_scan_object(object);
155 } else {
156 scan_later.push(object);
163 }
164 }
165
166 if !slots.is_empty() {
167 flush(&mut slots, worker);
168 }
169
170 scan_later
171 }
172
173 fn do_node_enqueuing_tracing(
174 &mut self,
175 worker: &mut GCWorker<T::VM>,
176 tls: VMWorkerThread,
177 trace: T,
178 scan_later: Vec<ObjectReference>,
179 ) {
180 if scan_later.is_empty() {
181 return;
182 }
183
184 let object_tracer_context = DefaultObjectTracerContext::<T>::new(self.bucket);
185
186 object_tracer_context.with_tracer(worker, |object_tracer| {
187 for object in scan_later.iter().copied() {
189 trace!("Scan object (node) {}", object);
190 <T::VM as VMBinding>::VMScanning::scan_object_and_trace_edges(
191 tls,
192 object,
193 object_tracer,
194 );
195 trace.post_scan_object(object);
196 }
197 });
198 }
199}
200
201impl<T: Trace> GCWork<T::VM> for ProcessNodes<T> {
202 fn do_work(&mut self, worker: &mut GCWorker<T::VM>, mmtk: &'static MMTK<T::VM>) {
203 trace!("ScanObjects");
204
205 let tls = worker.tls;
206 let trace = T::from_mmtk(mmtk);
207
208 let scan_later = self.try_enqueue_slots(worker, tls, &trace);
210
211 let total_objects = self.objects.len();
212 let scan_and_trace = scan_later.len();
213 probe!(mmtk, process_nodes, total_objects, scan_and_trace);
214
215 self.do_node_enqueuing_tracing(worker, tls, trace, scan_later);
217
218 trace!("ScanObjects End");
219 }
220}