@@ -282,64 +282,95 @@ pub trait Scanning<VM: VMBinding> {
282282
283283 /// Process weak references.
284284 ///
285- /// This function is called after a transitive closure is completed.
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.
286288 ///
287289 /// MMTk core enables the VM binding to do the following in this function:
288290 ///
289- /// 1. Query if an object is already reached in this transitive closure.
291+ /// 1. Query if an object is already reached.
292+ /// - by calling `ObjectReference::is_reachable()`
290293 /// 2. Get the new address of an object if it is already reached.
294+ /// - by calling `ObjectReference::get_forwarded_object()`
291295 /// 3. Keep an object and its descendents alive if not yet reached.
296+ /// - using `tracer_context`
292297 /// 4. Request this function to be called again after transitive closure is finished again.
293- ///
294- /// The VM binding can query if an object is currently reached by calling
295- /// `ObjectReference::is_reachable()`.
296- ///
297- /// If an object is already reached, the VM binding can get its new address by calling
298- /// `ObjectReference::get_forwarded_object()` as the object may have been moved.
299- ///
300- /// If an object is not yet reached, the VM binding can keep that object and its descendents
301- /// alive. To do this, the VM binding should use `tracer_context.with_tracer` to get access to
302- /// an `ObjectTracer`, and then call its `trace_object(object)` method. The `trace_object`
303- /// method will return the new address of the `object` if it moved the object, or its original
304- /// address if not moved. Implementation-wise, the `ObjectTracer` may contain an internal
305- /// queue for newly traced objects, and will flush the queue when `tracer_context.with_tracer`
306- /// returns. Therefore, it is recommended to reuse the `ObjectTracer` instance to trace
307- /// multiple objects.
308- ///
309- /// *Note that if `trace_object` is called on an already reached object, the behavior will be
310- /// equivalent to `ObjectReference::get_forwarded_object()`. It will return the new address if
311- /// the GC already moved the object when tracing that object, or the original address if the GC
312- /// did not move the object when tracing it. In theory, the VM binding can use `trace_object`
313- /// wherever `ObjectReference::get_forwarded_object()` is needed. However, if a VM never
314- /// resurrects objects, it should completely avoid touching `tracer_context`, and exclusively
315- /// use `ObjectReference::get_forwarded_object()` to get new addresses of objects. By doing
316- /// so, the VM binding can avoid accidentally resurrecting objects.*
317- ///
318- /// The VM binding can return `true` from `process_weak_refs` to request `process_weak_refs`
319- /// to be called again after the MMTk core finishes transitive closure again from the objects
320- /// newly visited by `ObjectTracer::trace_object`. This is useful if a VM supports multiple
321- /// levels of reachabilities (such as Java) or ephemerons.
322- ///
323- /// Implementation-wise, this function is called as the "sentinel" of the `VMRefClosure` work
324- /// bucket, which means it is called when all work packets in that bucket have finished. The
325- /// `tracer_context` expands the transitive closure by adding more work packets in the same
326- /// bucket. This means if `process_weak_refs` returns true, those work packets will have
327- /// finished (completing the transitive closure) by the time `process_weak_refs` is called
328- /// again. The VM binding can make use of this by adding custom work packets into the
329- /// `VMRefClosure` bucket. The bucket will be `VMRefForwarding`, instead, when forwarding.
330- /// See below.
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.
331329 ///
332330 /// The `memory_manager::is_mmtk_object` function can be used in this function if
333331 /// - the "is_mmtk_object" feature is enabled, and
334332 /// - `VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING` is true.
335333 ///
336334 /// Arguments:
337335 /// * `worker`: The current GC worker.
338- /// * `tracer_context`: Use this to get access an `ObjectTracer` and use it to retain and
339- /// update weak references.
340- ///
341- /// This function shall return true if this function needs to be called again after the GC
342- /// finishes expanding the transitive closure from the objects kept alive.
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.
343374 fn process_weak_refs (
344375 _worker : & mut GCWorker < VM > ,
345376 _tracer_context : impl ObjectTracerContext < VM > ,
0 commit comments