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