mmtk/vm/
scanning.rs

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