diff --git a/Cargo.toml b/Cargo.toml index 0793ca3b13..374feb3ef9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,6 +143,11 @@ malloc_counted_size = [] # Count the size of all live objects in GC count_live_bytes_in_gc = [] +# Let the VM implement forwarding states (forwarding bits) and forwarding pointers. +# Useful for VMs that have to use the type tag to encode forwarding states due to the lack of spare +# bits in the header. +vm_forwarding = [] + # Do not modify the following line - ci-common.sh matches it # -- Mutally exclusive features -- # Only one feature from each group can be provided. Otherwise build will fail. diff --git a/src/policy/copyspace.rs b/src/policy/copyspace.rs index 8d08ec1507..9c392ba37c 100644 --- a/src/policy/copyspace.rs +++ b/src/policy/copyspace.rs @@ -235,30 +235,25 @@ impl CopySpace { ); trace!("attempting to forward"); - let forwarding_status = object_forwarding::attempt_to_forward::(object); - - trace!("checking if object is being forwarded"); - if object_forwarding::state_is_forwarded_or_being_forwarded(forwarding_status) { - trace!("... yes it is"); - let new_object = - object_forwarding::spin_and_get_forwarded_object::(object, forwarding_status); - trace!("Returning"); - new_object - } else { - trace!("... no it isn't. Copying"); - let new_object = object_forwarding::forward_object::( - object, - semantics.unwrap(), - worker.get_copy_context_mut(), - ); + match object_forwarding::ForwardingAttempt::::attempt(object) { + object_forwarding::ForwardingAttempt::Lost(lost) => { + trace!("... Lost the forwarding race. Another thread is forwarding the object"); + let new_object = lost.spin_and_get_forwarded_object(); + trace!("... {} is already forwarded to {}", object, new_object); + new_object + } + object_forwarding::ForwardingAttempt::Won(won) => { + trace!("... Won the forwarding race. Forwarding..."); + let new_object = + won.forward_object(semantics.unwrap(), worker.get_copy_context_mut()); - #[cfg(feature = "vo_bit")] - crate::util::metadata::vo_bit::set_vo_bit::(new_object); + #[cfg(feature = "vo_bit")] + crate::util::metadata::vo_bit::set_vo_bit::(new_object); - trace!("Forwarding pointer"); - queue.enqueue(new_object); - trace!("Copied [{:?} -> {:?}]", object, new_object); - new_object + queue.enqueue(new_object); + trace!("Copied [{:?} -> {:?}]", object, new_object); + new_object + } } } diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 3809f7bd24..2ac5990f36 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -583,75 +583,78 @@ impl ImmixSpace { #[cfg(feature = "vo_bit")] vo_bit::helper::on_trace_object::(object); - let forwarding_status = object_forwarding::attempt_to_forward::(object); - if object_forwarding::state_is_forwarded_or_being_forwarded(forwarding_status) { - // We lost the forwarding race as some other thread has set the forwarding word; wait - // until the object has been forwarded by the winner. Note that the object may not - // necessarily get forwarded since Immix opportunistically moves objects. - #[allow(clippy::let_and_return)] - let new_object = - object_forwarding::spin_and_get_forwarded_object::(object, forwarding_status); - #[cfg(debug_assertions)] - { - if new_object == object { - debug_assert!( + match object_forwarding::ForwardingAttempt::::attempt(object) { + object_forwarding::ForwardingAttempt::Lost(lost) => { + // We lost the forwarding race as some other thread has set the forwarding word; wait + // until the object has been forwarded by the winner. Note that the object may not + // necessarily get forwarded since Immix opportunistically moves objects. + #[allow(clippy::let_and_return)] + let new_object = lost.spin_and_get_forwarded_object(); + + #[cfg(debug_assertions)] + { + if new_object == object { + debug_assert!( self.is_marked(object) || self.defrag.space_exhausted() || self.is_pinned(object), "Forwarded object is the same as original object {} even though it should have been copied", object, ); - } else { - // new_object != object - debug_assert!( + } else { + // new_object != object + debug_assert!( !Block::containing::(new_object).is_defrag_source(), "Block {:?} containing forwarded object {} should not be a defragmentation source", Block::containing::(new_object), new_object, ); + } } + new_object } - new_object - } else if self.is_marked(object) { - // We won the forwarding race but the object is already marked so we clear the - // forwarding status and return the unmoved object - object_forwarding::clear_forwarding_bits::(object); - object - } else { - // We won the forwarding race; actually forward and copy the object if it is not pinned - // and we have sufficient space in our copy allocator - let new_object = if self.is_pinned(object) - || (!nursery_collection && self.defrag.space_exhausted()) - { - self.attempt_mark(object, self.mark_state); - object_forwarding::clear_forwarding_bits::(object); - Block::containing::(object).set_state(BlockState::Marked); - - #[cfg(feature = "vo_bit")] - vo_bit::helper::on_object_marked::(object); - - object - } else { - // We are forwarding objects. When the copy allocator allocates the block, it should - // mark the block. So we do not need to explicitly mark it here. + object_forwarding::ForwardingAttempt::Won(won) => { + if self.is_marked(object) { + // We won the forwarding race but the object is already marked so we clear the + // forwarding status and return the unmoved object + won.revert(); + object + } else { + // We won the forwarding race; actually forward and copy the object if it is not pinned + // and we have sufficient space in our copy allocator + let new_object = if self.is_pinned(object) + || (!nursery_collection && self.defrag.space_exhausted()) + { + self.attempt_mark(object, self.mark_state); + won.revert(); + Block::containing::(object).set_state(BlockState::Marked); + + #[cfg(feature = "vo_bit")] + vo_bit::helper::on_object_marked::(object); + + object + } else { + // We are forwarding objects. When the copy allocator allocates the block, it should + // mark the block. So we do not need to explicitly mark it here. - // Clippy complains if the "vo_bit" feature is not enabled. - #[allow(clippy::let_and_return)] - let new_object = - object_forwarding::forward_object::(object, semantics, copy_context); + // Clippy complains if the "vo_bit" feature is not enabled. + #[allow(clippy::let_and_return)] + let new_object = won.forward_object(semantics, copy_context); - #[cfg(feature = "vo_bit")] - vo_bit::helper::on_object_forwarded::(new_object); + #[cfg(feature = "vo_bit")] + vo_bit::helper::on_object_forwarded::(new_object); - new_object - }; - debug_assert_eq!( - Block::containing::(new_object).get_state(), - BlockState::Marked - ); + new_object + }; + debug_assert_eq!( + Block::containing::(new_object).get_state(), + BlockState::Marked + ); - queue.enqueue(new_object); - debug_assert!(new_object.is_live()); - self.unlog_object_if_needed(new_object); - new_object + queue.enqueue(new_object); + debug_assert!(new_object.is_live()); + self.unlog_object_if_needed(new_object); + new_object + } + } } } diff --git a/src/util/copy/mod.rs b/src/util/copy/mod.rs index e7e1226a41..d66d5a6a7d 100644 --- a/src/util/copy/mod.rs +++ b/src/util/copy/mod.rs @@ -8,6 +8,7 @@ use crate::policy::copyspace::CopySpaceCopyContext; use crate::policy::immix::ImmixSpace; use crate::policy::immix::{ImmixCopyContext, ImmixHybridCopyContext}; use crate::policy::space::Space; +#[cfg(not(feature = "vm_forwarding"))] use crate::util::object_forwarding; use crate::util::opaque_pointer::VMWorkerThread; use crate::util::{Address, ObjectReference}; @@ -111,6 +112,9 @@ impl GCWorkerCopyContext { /// * `semantics`: The copy semantic used for the copying. pub fn post_copy(&mut self, object: ObjectReference, bytes: usize, semantics: CopySemantics) { // Clear forwarding bits. + // Not used when using VM-side forwarding implementation because the VM binding is supposed + // to ensure the copied object is not in the "being forwarded" or "forwarded" state. + #[cfg(not(feature = "vm_forwarding"))] object_forwarding::clear_forwarding_bits::(object); // If we are copying objects in mature space, we would need to mark the object as mature. if semantics.is_mature() && self.config.constraints.needs_log_bit { diff --git a/src/util/object_forwarding/mod.rs b/src/util/object_forwarding/mod.rs new file mode 100644 index 0000000000..ea250eeefc --- /dev/null +++ b/src/util/object_forwarding/mod.rs @@ -0,0 +1,200 @@ +use crate::util::copy::*; +use crate::util::ObjectReference; +use crate::vm::ObjectModel; +use crate::vm::VMBinding; +use std::marker::PhantomData; + +mod traditional; + +/// This type, together with `WonForwardingAttempt` and `LostForwardingAttempt`, represents an +/// attempt to forward an object, and provides methods for manipulating the forwarding state and +/// forwarding pointer of an object in an idiomatic way. +/// +/// A GC worker thread initiates object forwarding by calling `ForwardingAttempt::attempt(object)`. +/// It will try to atomically transition the forwarding state from `FORWARDING_NOT_TRIGGERED_YET` +/// to `BEING_FORWARDED`. +/// +/// - If the transition is successful (i.e. we "won" the race), the GC worker gains exclusive +/// right to further transition its forwarding state. +/// +/// - The GC worker can finish the forwarding by calling +/// `WonForwardingAttempt::forward_object`. It will actually copy the object and set the +/// forwarding bits and forwarding pointers. +/// +/// - The GC worker can abort the forwarding by calling `WonForwardingAttempt::revert`. It +/// will revert the forwarding state to the state before calling +/// `ForwardingAttempt::attempt`. +/// +/// - If the transition failed (i.e. we "lost" the race), it means another GC worker is +/// forwarding or has forwarded the object. +/// +/// - The current GC worker can call `LostForwardingAttempt::spin_and_get_forwarded_object` +/// to wait until the other GC worker finished forwarding, and read the forwarding pointer. +#[must_use] +pub enum ForwardingAttempt { + /// The old state before `Self::attempt` was `FORWARDING_NOT_TRIGGERED_YET`. + /// It means the current thread transitioned the forwarding state from + /// `FORWARDING_NOT_TRIGGERED_YET` to `BEING_FORWARDED`. + Won(WonForwardingAttempt), + /// The old state before `Self::attempt` was `BEING_FORWARDED` or `FORWARDED`. + /// It means another thread is forwarding or has already forwarded the object. + Lost(LostForwardingAttempt), +} + +/// Provide states and methods for a won forwarding attempt. +/// +/// See [`ForwardingAttempt`] +#[must_use] +pub struct WonForwardingAttempt { + /// The object to forward. This field holds the from-space address. + object: ObjectReference, + /// VM-specific temporary data used for reverting the forwarding states. + #[cfg(feature = "vm_forwarding")] + vm_data: >::VMForwardingDataType, + phantom_data: PhantomData, +} + +/// Provide states and methods for a lost forwarding attempt. +/// +/// See [`ForwardingAttempt`] +#[must_use] +pub struct LostForwardingAttempt { + /// The object to forward. This field holds the from-space address. + object: ObjectReference, + #[cfg(not(feature = "vm_forwarding"))] + old_state: u8, + phantom_data: PhantomData, +} + +impl ForwardingAttempt { + /// Attempt to forward an object by atomically transitioning the forwarding state of `object` + /// from `FORWARDING_NOT_TRIGGERED_YET` to `BEING_FORWARDED`. + pub fn attempt(object: ObjectReference) -> Self { + #[cfg(not(feature = "vm_forwarding"))] + { + let old_state = traditional::attempt_to_forward::(object); + if traditional::state_is_forwarded_or_being_forwarded(old_state) { + Self::Lost(LostForwardingAttempt { + object, + old_state, + phantom_data: PhantomData, + }) + } else { + Self::Won(WonForwardingAttempt { + object, + phantom_data: PhantomData, + }) + } + } + + #[cfg(feature = "vm_forwarding")] + { + match VM::VMObjectModel::attempt_to_forward(object) { + Some(vm_data) => Self::Won(WonForwardingAttempt { + object, + vm_data, + phantom_data: PhantomData, + }), + None => Self::Lost(LostForwardingAttempt { + object, + phantom_data: PhantomData, + }), + } + } + } +} + +impl WonForwardingAttempt { + /// Call this to forward the object. + /// + /// This method will also set the forwarding state to `FORWARDED` and store the forwarding + /// pointer at the appropriate location. + pub fn forward_object( + self, + semantics: CopySemantics, + copy_context: &mut GCWorkerCopyContext, + ) -> ObjectReference { + #[cfg(not(feature = "vm_forwarding"))] + { + let new_object = VM::VMObjectModel::copy(self.object, semantics, copy_context); + traditional::write_forwarding_bits_and_forwarding_pointer::( + self.object, + new_object, + ); + new_object + } + + #[cfg(feature = "vm_forwarding")] + { + let new_object = + VM::VMObjectModel::copy(self.object, semantics, copy_context, self.vm_data); + VM::VMObjectModel::write_forwarding_state_and_forwarding_pointer( + self.object, + new_object, + ); + new_object + } + } + + /// Call this to revert the forwarding state to the state before calling `attempt` + pub fn revert(self) { + #[cfg(not(feature = "vm_forwarding"))] + traditional::clear_forwarding_bits::(self.object); + + #[cfg(feature = "vm_forwarding")] + VM::VMObjectModel::revert_forwarding_state(self.object, self.vm_data); + } +} + +impl LostForwardingAttempt { + /// Spin-wait for the object's forwarding to become complete and then read the forwarding + /// pointer to the new object. If the forwarding state is reverted, this function will simply + /// return `self.object`. + pub fn spin_and_get_forwarded_object(self) -> ObjectReference { + // About the curly braces: The Rust compiler seems not to know that + // `not(feature = "vm_forwarding")` and `feature = "vm_forwarding"` are mutually exclusive. + // It will suggest adding semicolon (which is wrong) if we remove the curly braces. + #[cfg(not(feature = "vm_forwarding"))] + { + traditional::spin_and_get_forwarded_object::(self.object, self.old_state) + } + + #[cfg(feature = "vm_forwarding")] + { + VM::VMObjectModel::spin_and_get_forwarded_object(self.object) + } + } +} + +pub fn is_forwarded(object: ObjectReference) -> bool { + #[cfg(not(feature = "vm_forwarding"))] + { + traditional::is_forwarded::(object) + } + + #[cfg(feature = "vm_forwarding")] + { + VM::VMObjectModel::is_forwarded(object) + } +} + +/// Read the forwarding pointer of an object. +/// This function is called on forwarded/being_forwarded objects. +pub fn read_forwarding_pointer(object: ObjectReference) -> ObjectReference { + #[cfg(not(feature = "vm_forwarding"))] + { + traditional::read_forwarding_pointer::(object) + } + + #[cfg(feature = "vm_forwarding")] + { + VM::VMObjectModel::read_forwarding_pointer(object) + } +} + +/// Clear the forwarding bits. +/// Not needed when using VM-side forwarding implementation. +#[cfg(not(feature = "vm_forwarding"))] +pub fn clear_forwarding_bits(object: ObjectReference) { + traditional::clear_forwarding_bits::(object); +} diff --git a/src/util/object_forwarding.rs b/src/util/object_forwarding/traditional.rs similarity index 80% rename from src/util/object_forwarding.rs rename to src/util/object_forwarding/traditional.rs index 01b3fec447..e8391ebbb0 100644 --- a/src/util/object_forwarding.rs +++ b/src/util/object_forwarding/traditional.rs @@ -1,4 +1,18 @@ -use crate::util::copy::*; +//! This module contains the traditional implementation of object forwarding from JikesRVM. +//! +//! In this implementaion, +//! +//! - The forwarding states are encoded by the two-bit-per-object +//! `ObjectModel::LOCAL_FORWARDING_BITS_SPEC` metadata. +//! - The forwarding pointer is encoded by the one-word-per-object +//! `ObjectModel::LOCAL_FORWARDING_POINTER_SPEC` metadata. +//! +//! It also supports putting the forwarding bits and forwarding pointers into one single word to +//! conserve space. Simply specify both `ObjectModel::LOCAL_FORWARDING_BITS_SPEC` and +//! `ObjectModel::LOCAL_FORWARDING_POINTER_SPEC` as in-header, and let them overlap in the same +//! word, and the `write_forwarding_bits_and_forwarding_pointer` will handle this use case with +//! proper bit operations. + use crate::util::metadata::MetadataSpec; use crate::util::{constants, ObjectReference}; use crate::vm::ObjectModel; @@ -47,8 +61,9 @@ pub fn attempt_to_forward(object: ObjectReference) -> u8 { /// * `object`: the forwarded/being_forwarded object. /// * `forwarding_bits`: the last state of the forwarding bits before calling this function. /// -/// Returns a reference to the new object. -/// +/// Returns a reference to the new object. But if the GC algorithm decides that the object should +/// be pinned and should not move, it will revert the forwarding bits to +/// `FORWARDING_NOT_TRIGGERED_YET`. In that case, this function will simply return `object`. pub fn spin_and_get_forwarded_object( object: ObjectReference, forwarding_bits: u8, @@ -74,12 +89,10 @@ pub fn spin_and_get_forwarded_object( } } -pub fn forward_object( +pub fn write_forwarding_bits_and_forwarding_pointer( object: ObjectReference, - semantics: CopySemantics, - copy_context: &mut GCWorkerCopyContext, -) -> ObjectReference { - let new_object = VM::VMObjectModel::copy(object, semantics, copy_context); + new_object: ObjectReference, +) { if let Some(shift) = forwarding_bits_offset_in_forwarding_pointer::() { VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::( object, @@ -96,11 +109,10 @@ pub fn forward_object( Ordering::SeqCst, ); } - new_object } /// Return the forwarding bits for a given `ObjectReference`. -pub fn get_forwarding_status(object: ObjectReference) -> u8 { +fn get_forwarding_status(object: ObjectReference) -> u8 { VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.load_atomic::( object, None, @@ -116,7 +128,7 @@ fn is_being_forwarded(object: ObjectReference) -> bool { get_forwarding_status::(object) == BEING_FORWARDED } -pub fn is_forwarded_or_being_forwarded(object: ObjectReference) -> bool { +fn is_forwarded_or_being_forwarded(object: ObjectReference) -> bool { get_forwarding_status::(object) != FORWARDING_NOT_TRIGGERED_YET } @@ -124,7 +136,7 @@ pub fn state_is_forwarded_or_being_forwarded(forwarding_bits: u8) -> bool { forwarding_bits != FORWARDING_NOT_TRIGGERED_YET } -pub fn state_is_being_forwarded(forwarding_bits: u8) -> bool { +fn state_is_being_forwarded(forwarding_bits: u8) -> bool { forwarding_bits == BEING_FORWARDED } @@ -162,10 +174,7 @@ pub fn read_forwarding_pointer(object: ObjectReference) -> Object /// Write the forwarding pointer of an object. /// This function is called on being_forwarded objects. -pub fn write_forwarding_pointer( - object: ObjectReference, - new_object: ObjectReference, -) { +fn write_forwarding_pointer(object: ObjectReference, new_object: ObjectReference) { debug_assert!( is_being_forwarded::(object), "write_forwarding_pointer called for object {:?} that is not being forwarded! Forwarding state = 0x{:x}", @@ -190,7 +199,7 @@ pub fn write_forwarding_pointer( /// Otherwise, returns `Some(shift)`, where `shift` is the left shift needed on forwarding bits. /// #[cfg(target_endian = "little")] -pub(super) fn forwarding_bits_offset_in_forwarding_pointer() -> Option { +fn forwarding_bits_offset_in_forwarding_pointer() -> Option { use std::ops::Deref; // if both forwarding bits and forwarding pointer are in-header match ( @@ -210,6 +219,6 @@ pub(super) fn forwarding_bits_offset_in_forwarding_pointer() -> O } #[cfg(target_endian = "big")] -pub(super) fn forwarding_bits_offset_in_forwarding_pointer() -> Option { +fn forwarding_bits_offset_in_forwarding_pointer() -> Option { unimplemented!() } diff --git a/src/vm/object_model.rs b/src/vm/object_model.rs index e19415cfc1..d8843c3dde 100644 --- a/src/vm/object_model.rs +++ b/src/vm/object_model.rs @@ -64,6 +64,13 @@ pub trait ObjectModel { // Any side metadata offset calculation must consider these to prevent overlaps. A binding should start their // side metadata from GLOBAL_SIDE_METADATA_VM_BASE_ADDRESS or LOCAL_SIDE_METADATA_VM_BASE_ADDRESS. + /// The temporary VM-specific data generated by `attempt_to_forward` which is used when + /// finishing or reverting the forwarding attempt. The VM binding can customize the type as + /// it needs, and usually uses the data to hold the old value of type tag which can be + /// overwritten to represent forwarding states. + #[cfg(feature = "vm_forwarding")] + type VMForwardingDataType; + /// A global 1-bit metadata used by generational plans to track cross-generational pointers. It is generally /// located in side metadata. /// @@ -337,10 +344,13 @@ pub trait ObjectModel { /// * `from`: The address of the object to be copied. /// * `semantics`: The copy semantic to use. /// * `copy_context`: The `GCWorkerCopyContext` for the GC thread. + /// * `vm_data`: The VM-specific data from the `attempt_to_forward` call. This argument only + /// exists if the Cargo feature `"vm_forwarding"` is enabled. fn copy( from: ObjectReference, semantics: CopySemantics, copy_context: &mut GCWorkerCopyContext, + #[cfg(feature = "vm_forwarding")] vm_data: Self::VMForwardingDataType, ) -> ObjectReference; /// Copy an object. This is required @@ -472,6 +482,113 @@ pub trait ObjectModel { fn is_object_sane(_object: ObjectReference) -> bool { true } + + /// Initiate the attempt to forward an object. This function should atomically transition the + /// state of the `object` from "forwarding not triggered yet" to "being forwarded". + /// + /// This function is called when an evacuating GC attempts to forward an object. It should be + /// semantically equivalent to + /// `crate::util::object_forwarding::traditional::attempt_to_forward`, albeit this function + /// uses VM-specific encoding of forwarding states. + /// + /// Arguments: + /// * `object`: The object to forward. + /// + /// Returns `Some(value)` if this function successfully transitioned the state, and `value` can + /// be any VM-specific value that can be used to revert the state change. For VMs that + /// implement the forwarding states by overwriting the type tag in the header, this value can + /// be the original type tag before calling this function. + /// + /// Returns `None`` if the transition failed (i.e. another GC worker is forwarding or has + /// forwarded the object). + #[cfg(feature = "vm_forwarding")] + fn attempt_to_forward(_object: ObjectReference) -> Option; + + /// Change the the forwarding state of `object` to represent "forwarded" and write the + /// forwarding pointer to its proper location. + /// + /// This function is called after `object` has been copied to the new location `new_object`. It + /// should be semantically equivalent to + /// `crate::util::object_forwarding::traditional::write_forwarding_bits_and_forwarding_pointer`, + /// albeit this function uses VM-specific encoding of forwarding states. + /// + /// Argiments: + /// * `object`: The old address of the object. + /// * `new_object`: The new address of the object. + #[cfg(feature = "vm_forwarding")] + fn write_forwarding_state_and_forwarding_pointer( + object: ObjectReference, + new_object: ObjectReference, + ); + + /// Revert the forwarding state of `object` to the state before `attempt_to_forward` was + /// called. After this function is called, the state should represents "object forwarding is + /// not triggered yet". + /// + /// This function is called after `object` has been copied to the new location `new_object`. + /// + /// Note that mmtk-core's intrinsic implementation of this operation clears the forwarding bits + /// to `0b00` which represents "object forwarding is not triggered yet". If the VM binding + /// represents the forwarding states by overwriting the type tag, it should restore the type + /// tag to the value before `attempt_to_forward` is called. The VM binding can use the + /// `Some(value)` value returned from `attempt_to_forward` to remember the old type tag. + /// + /// Arguments: + /// * `object`: The object to restore state. + /// * `vm_data`: The `value` of the `Some(value)` returned from the `attempt_to_forward` call. + #[cfg(feature = "vm_forwarding")] + fn revert_forwarding_state(object: ObjectReference, vm_data: Self::VMForwardingDataType); + + /// Wait until the object is no longer in the "being forwarded state" and return the forwarding + /// pointer if the object is forwarded. + /// + /// The function is called after `attempt_to_forward` failed. The failing GC worker calls this + /// function to wait for another GC thread to finish forwarding the object or reverting the + /// forwarding state. It should be semantically equivalent to + /// `crate::util::object_forwarding::traditional::spin_and_get_forwarded_object`, albeit this + /// function uses VM-specific encoding of forwarding states. + /// + /// Arguments: + /// * `object`: The object to get forwarding pointer from. + /// + /// If the object is forwarded, return the forwarding pointer; if the state is reverted back to + /// the "object forwarding is not triggered yet" state, simply return `object`. + #[cfg(feature = "vm_forwarding")] + fn spin_and_get_forwarded_object(object: ObjectReference) -> ObjectReference; + + /// Returns true if `object` is already forwarded. + /// + /// This function may be called during tracing and weak reference processing. This test must + /// be atomic with respect to other GC workers which may attempt ot forward the same `object`. + /// + /// For example, during weak reference processing, in `Scanning::process_weak_refs`, the VM + /// binding may call `ObjectReference::is_reachable` to test if an object is reached, and call + /// `ObjectTracer::trace_object` to resurrect dead finalizable objects. For some spaces, such + /// as `CopySpace` and `ImmixSpace`, `is_reachable` may call `is_forwarded`, and `trace_object` + /// may forward the object. The implementer of this function must be aware that while one GC + /// worker is executing `is_forwarding`, another GC worker may be forwarding the same object. + /// The implementor of weak reference process must also be aware that even if + /// `is_forwarded(object)` returns `false`, the object may be concurrently forwarded by another + /// GC worker. The good news is, `trace_object(object)` is thread-safe. If two GC workers + /// call `trace_object` on the same `object`, one will forward it. The other will not forward + /// it, but will get its forwarded address. + /// + /// Argument: + /// * `object`: The object to test if it is forwarded. + #[cfg(feature = "vm_forwarding")] + fn is_forwarded(object: ObjectReference) -> bool; + + /// Read the forwarding pointer of `object` which is already forwarded. + /// + /// This function may be called during weak reference processing. Because the `object` must + /// have already been forwarded before calling this function, it is OK if this function is not + /// atomic because other GC workers will not revert the forwarding state. + /// + /// Argument: + /// * `object`: The forwarding pointer of this object will be read. This object must be + /// already forwarded before calling this function. + #[cfg(feature = "vm_forwarding")] + fn read_forwarding_pointer(object: ObjectReference) -> ObjectReference; } pub mod specs {