1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
use super::worker::*;
use crate::mmtk::MMTK;
use crate::vm::VMBinding;
#[cfg(feature = "work_packet_stats")]
use std::any::{type_name, TypeId};
/// This defines a GC work packet which are assigned to the [`GCWorker`]s by the scheduler.
/// Work packets carry payloads that indicate the work to be done. For example, a work packet may
/// contain a pointer to a stack that must be scanned, or it may contain a large buffer of pointers
/// that need to be traced, or it might contain a range of static variables to be scanned, etc. The size
/// of the work packet will need to consider at least two points of tension: the work packet must be large
/// enough to ensure that the costs of managing the work packets do not dominate, and the packet must be
/// small enough that good load balancing is achieved.
pub trait GCWork<VM: VMBinding>: 'static + Send {
/// Define the work for this packet. However, this is not supposed to be called directly.
/// Usually `do_work_with_stat()` should be used.
///
/// Most work packets are polled and executed in the worker's main loop ([`GCWorker::run`])
/// using `do_work_with_stat`. If `do_work` is called directly during the execution of another
/// work packet, bypassing `do_work_with_stat()`, this work packet will not be counted into the
/// number of work packets executed, and the execution time of this work packet will be counted
/// as part of the execution time of the other work packet. Only call this method directly if
/// this is what you intend. But you should always consider adding the work packet
/// into a bucket so that other GC workers can execute it in parallel, unless the context-
/// switching overhead is a problem.
fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>);
/// Do work and collect statistics. This internally calls `do_work()`. In most cases,
/// this should be called rather than `do_work()` so that MMTk can correctly collect
/// statistics for the work packets.
/// If the feature "work_packet_stats" is not enabled, this call simply forwards the call
/// to `do_work()`.
fn do_work_with_stat(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
debug!("{}", std::any::type_name::<Self>());
debug_assert!(!worker.tls.0.0.is_null(), "TLS must be set correctly for a GC worker before the worker does any work. GC Worker {} has no valid tls.", worker.ordinal);
#[cfg(feature = "work_packet_stats")]
// Start collecting statistics
let stat = {
let mut worker_stat = worker.shared.borrow_stat_mut();
worker_stat.measure_work(TypeId::of::<Self>(), type_name::<Self>(), mmtk)
};
// Do the actual work
self.do_work(worker, mmtk);
#[cfg(feature = "work_packet_stats")]
// Finish collecting statistics
{
let mut worker_stat = worker.shared.borrow_stat_mut();
stat.end_of_work(&mut worker_stat);
}
}
/// Get the compile-time static type name for the work packet.
fn get_type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
}
use super::gc_work::ProcessEdgesWork;
use crate::plan::Plan;
/// This trait provides a group of associated types that are needed to
/// create GC work packets for a certain plan. For example, `GCWorkScheduler.schedule_common_work()`
/// needs this trait to schedule different work packets. For certain plans,
/// they may need to provide several types that implement this trait, e.g. one for
/// nursery GC, one for mature GC.
///
/// Note: Because `GCWorkContext` is often used as parameters of implementations of `GCWork`, we
/// let GCWorkContext require `Send + 'static`. Since `GCWorkContext` is just a group of
/// associated types, its implementations should not have any actual fields other than
/// `PhantomData`, and will automatically have `Send + 'static`.
pub trait GCWorkContext: Send + 'static {
type VM: VMBinding;
type PlanType: Plan<VM = Self::VM>;
// FIXME: We should use `SFTProcessEdges` as the default value for `DefaultProcessEdges`, and
// `UnsupportedProcessEdges` for `PinningProcessEdges`. However, this requires
// `associated_type_defaults` which has not yet been stablized.
// See: https://github.com/rust-lang/rust/issues/29661
/// The `ProcessEdgesWork` implementation to use for tracing edges that do not have special
/// pinning requirements. Concrete plans and spaces may choose to move or not to move the
/// objects the traced edges point to.
type DefaultProcessEdges: ProcessEdgesWork<VM = Self::VM>;
/// The `ProcessEdgesWork` implementation to use for tracing edges that must not be updated
/// (i.e. the objects the traced edges pointed to must not be moved). This is used for
/// implementing pinning roots and transitive pinning roots.
///
/// - For non-transitive pinning roots, `PinningProcessEdges` will be used to trace the edges
/// from roots to objects, but their descendents will be traced using `DefaultProcessEdges`.
/// - For transitive pinning roots, `PinningProcessEdges` will be used to trace the edges
/// from roots to objects, and will also be used to trace the outgoing edges of all objects
/// reachable from transitive pinning roots.
///
/// If a plan does not support object pinning, it should use `UnsupportedProcessEdges` for this
/// type member.
type PinningProcessEdges: ProcessEdgesWork<VM = Self::VM>;
}