mmtk/vm/scanning.rs
1use crate::plan::Mutator;
2use crate::scheduler::gc_work::RootKind;
3use crate::scheduler::GCWorker;
4use crate::util::ObjectReference;
5use crate::util::VMWorkerThread;
6use crate::vm::slot::Slot;
7use crate::vm::VMBinding;
8
9/// Default for [`Scanning::UNIQUE_OBJECT_ENQUEUING`]
10pub(crate) const DEFAULT_UNIQUE_OBJECT_ENQUEUING: bool = false;
11
12/// Callback trait of scanning functions that report slots.
13pub trait SlotVisitor<SL: Slot> {
14 /// Call this function for each slot.
15 fn visit_slot(&mut self, slot: SL);
16}
17
18/// This lets us use closures as SlotVisitor.
19impl<SL: Slot, F: FnMut(SL)> SlotVisitor<SL> for F {
20 fn visit_slot(&mut self, slot: SL) {
21 #[cfg(debug_assertions)]
22 trace!(
23 "(FunctionClosure) Visit slot {:?} (pointing to {:?})",
24 slot,
25 slot.load()
26 );
27 self(slot)
28 }
29}
30
31/// Callback trait of scanning functions that directly trace through object graph edges.
32pub trait ObjectTracer {
33 /// Call this function to trace through an object graph edge which points to `object`.
34 ///
35 /// The return value is the new object reference for `object` if it is moved, or `object` if
36 /// not moved. If moved, the caller should update the slot that holds the reference to
37 /// `object` so that it points to the new location.
38 ///
39 /// Note: This function is performance-critical, therefore must be implemented efficiently.
40 fn trace_object(&mut self, object: ObjectReference) -> ObjectReference;
41}
42
43/// This lets us use closures as ObjectTracer.
44impl<F: FnMut(ObjectReference) -> ObjectReference> ObjectTracer for F {
45 fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
46 self(object)
47 }
48}
49
50/// An `ObjectTracerContext` gives a GC worker temporary access to an [`ObjectTracer`], allowing the
51/// GC worker to trace objects. This trait is intended to abstract out the implementation details
52/// of tracing objects, enqueuing objects, and creating work packets that expand the transitive
53/// closure, allowing the VM binding to focus on VM-specific parts.
54///
55/// This trait is used during root scanning and binding-side weak reference processing.
56pub trait ObjectTracerContext<VM: VMBinding>: Clone + Send + 'static {
57 /// The concrete [`ObjectTracer`] type.
58 ///
59 /// The lifetime parameter `'w` is the lifetime of the `&'w mut GCWorker<VM>` passed to the
60 /// [`Self::with_tracer`] method. It is borrowed by the [`ObjectTracer`] passed to the `func`
61 /// callback of [`Self::with_tracer`].
62 type TracerType<'w>: ObjectTracer;
63
64 /// Create a temporary [`ObjectTracer`] and provide access in the scope of `func`.
65 ///
66 /// When [`ObjectTracer::trace_object`] is called, if the traced object is first visited in this
67 /// transitive closure, it will be enqueued. After `func` returns, the implememtation will
68 /// create work packets to continue computing the transitive closure from the newly enqueued
69 /// objects.
70 ///
71 /// API functions that provide [`ObjectTracerContext`] should document
72 /// 1. on which fields the user is supposed to call [`ObjectTracer::trace_object`], and
73 /// 2. which work bucket the generated work packet will be added to. Sometimes the user needs
74 /// to know when the computing of transitive closure finishes.
75 ///
76 /// Arguments:
77 /// - `worker`: The current GC worker.
78 /// - `func`: A caller-supplied closure in which the created `ObjectTracer` can be used.
79 ///
80 /// Returns: The return value of `func`.
81 fn with_tracer<'w, R, F>(&self, worker: &'w mut GCWorker<VM>, func: F) -> R
82 where
83 F: FnOnce(&mut Self::TracerType<'w>) -> R;
84}
85
86/// Root-scanning methods use this trait to create work packets for processing roots.
87///
88/// Notes on the required traits:
89///
90/// - `Clone`: The VM may divide one root-scanning call (such as `scan_vm_specific_roots`) into
91/// multiple work packets to scan roots in parallel. In this case, the factory shall be cloned
92/// to be given to multiple work packets.
93///
94/// Cloning may be expensive if a factory contains many states. If the states are immutable, a
95/// `RootsWorkFactory` implementation may hold those states in an `Arc` field so that multiple
96/// factory instances can still share the part held in the `Arc` even after cloning.
97///
98/// - `Send` + 'static: The factory will be given to root-scanning work packets.
99/// Because work packets are distributed to and executed on different GC workers,
100/// it needs `Send` to be sent between threads. `'static` means it must not have
101/// references to variables with limited lifetime (such as local variables), because
102/// it needs to be moved between threads.
103pub trait RootsWorkFactory<SL: Slot>: Clone + Send + 'static {
104 // TODO:
105 // 1. Rename the functions and remove the repeating `create_process_` and `_work`.
106 // 2. Rename the functions to reflect both the form (slots / nodes) and the semantics (pinning
107 // / transitive pinning / non-pinning) of each function.
108 // 3. Introduce a function to give the VM binding a way to update root edges without
109 // representing the roots as slots. See: https://github.com/mmtk/mmtk-core/issues/710
110
111 /// Create work packets to handle non-pinned roots. The roots are represented as slots so that
112 /// they can be updated.
113 ///
114 /// The work packet may update the slots.
115 ///
116 /// Equivalent to `self.create_process_roots_work_experimental(slots, RootKind::Strong)`.
117 ///
118 /// Arguments:
119 /// * `slots`: A vector of slots.
120 fn create_process_roots_work(&mut self, slots: Vec<SL>) {
121 self.create_process_roots_work_with_root_kind(slots, RootKind::Strong);
122 }
123
124 /// An experimental API to support weak and young code cache roots.
125 ///
126 /// Currently only used by the LXR plan and the OpenJDK binding.
127 fn create_process_roots_work_with_root_kind(&mut self, slots: Vec<SL>, kind: RootKind);
128
129 /// Create work packets to handle non-transitively pinning roots.
130 ///
131 /// The work packet will prevent the objects in `nodes` from moving,
132 /// i.e. they will be pinned for the duration of the GC.
133 /// But it will not prevent the children of those objects from moving.
134 ///
135 /// This method is useful for conservative stack scanning, or VMs that cannot update some
136 /// of the root slots.
137 ///
138 /// Only available with the `non_moving_root` feature.
139 ///
140 /// Arguments:
141 /// * `nodes`: A vector of references to objects pointed by edges from roots.
142 #[cfg(feature = "non_moving_root")]
143 fn create_process_pinning_roots_work(&mut self, nodes: Vec<ObjectReference>);
144
145 /// Create work packets to handle transitively pinning (TP) roots.
146 ///
147 /// Similar to `create_process_pinning_roots_work`, this work packet will not move objects in `nodes`.
148 /// Unlike `create_process_pinning_roots_work`, no objects in the transitive closure of `nodes` will be moved, either.
149 ///
150 /// Only available with the `non_moving_root` feature.
151 ///
152 /// Arguments:
153 /// * `nodes`: A vector of references to objects pointed by edges from roots.
154 #[cfg(feature = "non_moving_root")]
155 fn create_process_tpinning_roots_work(&mut self, nodes: Vec<ObjectReference>);
156}
157
158/// For USDT tracepoints for roots.
159/// Keep in sync with `tools/tracing/timeline/visualize.py`.
160#[repr(usize)]
161pub(crate) enum RootsKind {
162 NORMAL = 0,
163 #[cfg(feature = "non_moving_root")]
164 PINNING = 1,
165 #[cfg(feature = "non_moving_root")]
166 TPINNING = 2,
167}
168
169/// VM-specific methods for scanning roots/objects.
170pub trait Scanning<VM: VMBinding> {
171 /// When set to `true`, all plans will guarantee that during each GC, each live object is
172 /// enqueued at most once, and therefore scanned (by either [`Scanning::scan_object`] or
173 /// [`Scanning::scan_object_and_trace_edges`]) at most once.
174 ///
175 /// When set to `false`, MMTk may enqueue an object multiple times due to optimizations, such as
176 /// using non-atomic operatios to mark objects. Consequently, an object may be scanned multiple
177 /// times during a GC.
178 ///
179 /// The default value is `false` because duplicated object-enqueuing is benign for most VMs, and
180 /// related optimizations, such as non-atomic marking, can improve GC speed. VM bindings can
181 /// override this if they need. For example, some VMs piggyback on object-scanning to visit
182 /// objects during a GC, but may have data race if multiple GC workers visit the same object at
183 /// the same time. Such VMs can set this constant to `true` to workaround this problem.
184 const UNIQUE_OBJECT_ENQUEUING: bool = DEFAULT_UNIQUE_OBJECT_ENQUEUING;
185
186 /// Return true if the given object supports slot enqueuing.
187 ///
188 /// - If this returns true, MMTk core will call `scan_object` on the object.
189 /// - Otherwise, MMTk core will call `scan_object_and_trace_edges` on the object.
190 ///
191 /// For maximum performance, the VM should support slot-enqueuing for as many objects as
192 /// practical. Also note that this method is called for every object to be scanned, so it
193 /// must be fast. The VM binding should avoid expensive checks and keep it as efficient as
194 /// possible.
195 ///
196 /// Arguments:
197 /// * `tls`: The VM-specific thread-local storage for the current worker.
198 /// * `object`: The object to be scanned.
199 fn support_slot_enqueuing(_tls: VMWorkerThread, _object: ObjectReference) -> bool {
200 true
201 }
202
203 /// Delegated scanning of a object, visiting each reference field encountered.
204 ///
205 /// The VM shall call `slot_visitor.visit_slot` on each reference field. This effectively
206 /// visits all outgoing edges from the current object in the form of slots.
207 ///
208 /// The VM may skip a reference field if it is not holding an object reference (e.g. if the
209 /// field is holding a null reference, or a tagged non-reference value such as small integer).
210 /// Even if not skipped, [`Slot::load`] will still return `None` if the slot is not holding an
211 /// object reference.
212 ///
213 /// The `memory_manager::is_mmtk_object` function can be used in this function if
214 /// - the "vo_bit" feature is enabled, and
215 /// - `VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING` is true.
216 ///
217 /// Arguments:
218 /// * `tls`: The VM-specific thread-local storage for the current worker.
219 /// * `object`: The object to be scanned.
220 /// * `slot_visitor`: Called back for each field.
221 fn scan_object(
222 tls: VMWorkerThread,
223 object: ObjectReference,
224 slot_visitor: &mut impl SlotVisitor<VM::VMSlot>,
225 );
226
227 /// Delegated scanning of a object, visiting each reference field encountered, and tracing the
228 /// objects pointed by each field.
229 ///
230 /// The VM shall call `object_tracer.trace_object` with the argument being the object reference
231 /// held in each reference field. If the GC moves the object, the VM shall update the field so
232 /// that it refers to the object using the object reference returned from `trace_object`. This
233 /// effectively traces through all outgoing edges from the current object directly.
234 ///
235 /// The VM must skip reference fields that are not holding object references (e.g. if the
236 /// field is holding a null reference, or a tagged non-reference value such as small integer).
237 ///
238 /// The `memory_manager::is_mmtk_object` function can be used in this function if
239 /// - the "vo_bit" feature is enabled, and
240 /// - `VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING` is true.
241 ///
242 /// Arguments:
243 /// * `tls`: The VM-specific thread-local storage for the current worker.
244 /// * `object`: The object to be scanned.
245 /// * `object_tracer`: Called back for the object reference held in each field.
246 fn scan_object_and_trace_edges<OT: ObjectTracer>(
247 _tls: VMWorkerThread,
248 _object: ObjectReference,
249 _object_tracer: &mut OT,
250 ) {
251 unreachable!("scan_object_and_trace_edges() will not be called when support_slot_enqueuing() is always true.")
252 }
253
254 /// MMTk calls this method at the first time during a collection that thread's stacks
255 /// have been scanned. This can be used (for example) to clean up
256 /// obsolete compiled methods that are no longer being executed.
257 ///
258 /// Arguments:
259 /// * `partial_scan`: Whether the scan was partial or full-heap.
260 /// * `tls`: The GC thread that is performing the thread scan.
261 fn notify_initial_thread_scan_complete(partial_scan: bool, tls: VMWorkerThread);
262
263 /// Scan one mutator for stack roots.
264 ///
265 /// Some VM bindings may not be able to implement this method.
266 /// For example, the VM binding may only be able to enumerate all threads and
267 /// scan them while enumerating, but cannot scan stacks individually when given
268 /// the references of threads.
269 /// In that case, it can leave this method empty, and deal with stack
270 /// roots in [`Scanning::scan_vm_specific_roots`]. However, in that case, MMTk
271 /// does not know those roots are stack roots, and cannot perform any possible
272 /// optimization for the stack roots.
273 ///
274 /// The `memory_manager::is_mmtk_object` function can be used in this function if
275 /// - the "vo_bit" feature is enabled.
276 ///
277 /// Arguments:
278 /// * `tls`: The GC thread that is performing this scanning.
279 /// * `mutator`: The reference to the mutator whose roots will be scanned.
280 /// * `factory`: The VM uses it to create work packets for scanning roots.
281 fn scan_roots_in_mutator_thread(
282 tls: VMWorkerThread,
283 mutator: &'static mut Mutator<VM>,
284 factory: impl RootsWorkFactory<VM::VMSlot>,
285 );
286
287 /// Scan VM-specific roots. The creation of all root scan tasks (except thread scanning)
288 /// goes here.
289 ///
290 /// The `memory_manager::is_mmtk_object` function can be used in this function if
291 /// - the "vo_bit" feature is enabled.
292 ///
293 /// Arguments:
294 /// * `tls`: The GC thread that is performing this scanning.
295 /// * `factory`: The VM uses it to create work packets for scanning roots.
296 fn scan_vm_specific_roots(tls: VMWorkerThread, factory: impl RootsWorkFactory<VM::VMSlot>);
297
298 /// Return whether the VM supports return barriers. This is unused at the moment.
299 fn supports_return_barrier() -> bool;
300
301 /// Prepare for another round of root scanning in the same GC. Some GC algorithms
302 /// need multiple transitive closures, and each transitive closure starts from
303 /// root scanning. We expect the binding to provide the same root set for every
304 /// round of root scanning in the same GC. Bindings can use this call to get
305 /// ready for another round of root scanning to make sure that the same root
306 /// set will be returned in the upcoming calls of root scanning methods,
307 /// such as [`crate::vm::Scanning::scan_roots_in_mutator_thread`] and
308 /// [`crate::vm::Scanning::scan_vm_specific_roots`].
309 fn prepare_for_roots_re_scanning();
310
311 /// Process weak references.
312 ///
313 /// This function is called in a GC after the transitive closure from roots is computed, that
314 /// is, all reachable objects from roots are reached. This function gives the VM binding an
315 /// opportunitiy to process finalizers and weak references.
316 ///
317 /// MMTk core enables the VM binding to do the following in this function:
318 ///
319 /// 1. Query if an object is already reached.
320 /// - by calling `ObjectReference::is_reachable()`
321 /// 2. Get the new address of an object if it is already reached.
322 /// - by calling `ObjectReference::get_forwarded_object()`
323 /// 3. Keep an object and its descendents alive if not yet reached.
324 /// - using `tracer_context`
325 /// 4. Request this function to be called again after transitive closure is finished again.
326 /// - by returning `true`
327 ///
328 /// The `tracer_context` parameter provides the VM binding the mechanism for retaining
329 /// unreachable objects (i.e. keeping them alive in this GC). The following snippet shows a
330 /// typical use case of handling finalizable objects for a Java-like language.
331 ///
332 /// ```rust
333 /// let finalizable_objects: Vec<ObjectReference> = my_vm::get_finalizable_object();
334 /// let mut new_finalizable_objects = vec![];
335 ///
336 /// tracer_context.with_tracer(worker, |tracer| {
337 /// for object in finalizable_objects {
338 /// if object.is_reachable() {
339 /// // `object` is still reachable.
340 /// // It may have been moved if it is a copying GC.
341 /// let new_object = object.get_forwarded_object().unwrap_or(object);
342 /// new_finalizable_objects.push(new_object);
343 /// } else {
344 /// // `object` is unreachable.
345 /// // Retain it, and enqueue it for postponed finalization.
346 /// let new_object = tracer.trace_object(object);
347 /// my_vm::enqueue_finalizable_object_to_be_executed_later(new_object);
348 /// }
349 /// }
350 /// });
351 /// ```
352 ///
353 /// Within the closure `|tracer| { ... }`, the VM binding can call `tracer.trace_object(object)`
354 /// to retain `object` and get its new address if moved. After `with_tracer` returns, it will
355 /// create work packets in the `VMRefClosure` work bucket to compute the transitive closure from
356 /// the objects retained in the closure.
357 ///
358 /// The `memory_manager::is_mmtk_object` function can be used in this function if
359 /// - the "vo_bit" feature is enabled, and
360 /// - `VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING` is true.
361 ///
362 /// Arguments:
363 /// * `worker`: The current GC worker.
364 /// * `tracer_context`: Use this to get access an `ObjectTracer` and use it to retain and update
365 /// weak references.
366 ///
367 /// If `process_weak_refs` returns `true`, then `process_weak_refs` will be called again after
368 /// all work packets in the `VMRefClosure` work bucket has been executed, by which time all
369 /// objects reachable from the objects retained in this function will have been reached.
370 ///
371 /// # Performance notes
372 ///
373 /// **Retain as many objects as needed in one invocation of `tracer_context.with_tracer`, and
374 /// avoid calling `with_tracer` again and again** for each object. The `tracer` provided by
375 /// `ObjectTracerFactory::with_tracer` enqueues retained objects in an internal list specific to
376 /// this invocation of `with_tracer`, and will create reasonably sized work packets to compute
377 /// the transitive closure. This means the invocation of `with_tracer` has a non-trivial
378 /// overhead, but each invocation of `tracer.trace_object` is cheap.
379 ///
380 /// *Don't do this*:
381 ///
382 /// ```rust
383 /// for object in objects {
384 /// tracer_context.with_tracer(worker, |tracer| { // This is expensive! DON'T DO THIS!
385 /// tracer.trace_object(object);
386 /// });
387 /// }
388 /// ```
389 ///
390 /// **Use `ObjectReference::get_forwarded_object()` to get the forwarded address of reachable
391 /// objects. Only use `tracer.trace_object` for retaining unreachable objects.** If
392 /// `trace_object` is called on an already reached object, it will also return its new address
393 /// if moved. However, `tracer_context.with_tracer` has a cost, and the VM binding may
394 /// accidentally "resurrect" dead objects if failed to check `object.is_reachable()` first. If
395 /// the VM binding does not intend to retain any objects, it should completely avoid touching
396 /// `tracer_context`.
397 ///
398 /// **Clone the `tracer_context` for parallelism.** The `ObjectTracerContext` has `Clone` as
399 /// its supertrait. The VM binding can clone it and distribute each clone into a work packet.
400 /// By doing so, the VM binding can parallelize the processing of finalizers and weak references
401 /// by creating multiple work packets.
402 fn process_weak_refs(
403 _worker: &mut GCWorker<VM>,
404 _tracer_context: impl ObjectTracerContext<VM>,
405 ) -> bool {
406 false
407 }
408
409 /// Forward weak references.
410 ///
411 /// This function will only be called in the forwarding stage when using the mark-compact GC
412 /// algorithm. Mark-compact computes transive closure twice during each GC. It marks objects
413 /// in the first transitive closure, and forward references in the second transitive closure.
414 ///
415 /// Arguments:
416 /// * `worker`: The current GC worker.
417 /// * `tracer_context`: Use this to get access an `ObjectTracer` and use it to update weak
418 /// references.
419 fn forward_weak_refs(
420 _worker: &mut GCWorker<VM>,
421 _tracer_context: impl ObjectTracerContext<VM>,
422 ) {
423 }
424}