Skip to content

Support for object pinning #703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ global_alloc_bit = []
# conservative garbage collection support
is_mmtk_object = ["global_alloc_bit"]

# Enable object pinning, in particular, enable pinning/unpinning, and its metadata
object_pinning = []

# The following two features are useful for using Immix for VMs that do not support moving GC.

# Disable defragmentation for ImmixSpace. This makes Immix a non-moving plan.
Expand Down
44 changes: 44 additions & 0 deletions src/memory_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,50 @@ pub fn add_finalizer<VM: VMBinding>(
mmtk.finalizable_processor.lock().unwrap().add(object);
}

/// Pin an object. MMTk will make sure that the object does not move
/// during GC. Note that action cannot happen in some plans, eg, semispace.
/// It returns true if the pinning operation has been performed, i.e.,
/// the object status changed from non-pinned to pinned
///
/// Arguments:
/// * `object`: The object to be pinned
#[cfg(feature = "object_pinning")]
pub fn pin_object<VM: VMBinding>(object: ObjectReference) -> bool {
use crate::mmtk::SFT_MAP;
use crate::policy::sft_map::SFTMap;
SFT_MAP
.get_checked(object.to_address::<VM>())
.pin_object(object)
}

/// Unpin an object.
/// Returns true if the unpinning operation has been performed, i.e.,
/// the object status changed from pinned to non-pinned
///
/// Arguments:
/// * `object`: The object to be pinned
#[cfg(feature = "object_pinning")]
pub fn unpin_object<VM: VMBinding>(object: ObjectReference) -> bool {
use crate::mmtk::SFT_MAP;
use crate::policy::sft_map::SFTMap;
SFT_MAP
.get_checked(object.to_address::<VM>())
.unpin_object(object)
}

/// Check whether an object is currently pinned
///
/// Arguments:
/// * `object`: The object to be checked
#[cfg(feature = "object_pinning")]
pub fn is_pinned<VM: VMBinding>(object: ObjectReference) -> bool {
use crate::mmtk::SFT_MAP;
use crate::policy::sft_map::SFTMap;
SFT_MAP
.get_checked(object.to_address::<VM>())
.is_object_pinned(object)
}

/// Get an object that is ready for finalization. After each GC, if any registered object is not
/// alive, this call will return one of the objects. MMTk will retain the liveness of those objects
/// until they are popped through this call. Once an object is popped, it is the responsibility of
Expand Down
15 changes: 15 additions & 0 deletions src/policy/copyspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ impl<VM: VMBinding> SFT for CopySpace<VM> {
!self.is_from_space() || object_forwarding::is_forwarded::<VM>(object)
}

#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
panic!("Cannot pin/unpin objects of CopySpace.")
}

#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
panic!("Cannot pin/unpin objects of CopySpace.")
}

#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
false
}

fn is_movable(&self) -> bool {
true
}
Expand Down
30 changes: 25 additions & 5 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,22 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
self.is_marked(object, self.mark_state) || ForwardingWord::is_forwarded::<VM>(object)
}
}
#[cfg(feature = "object_pinning")]
fn pin_object(&self, object: ObjectReference) -> bool {
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.pin_object::<VM>(object)
}
#[cfg(feature = "object_pinning")]
fn unpin_object(&self, object: ObjectReference) -> bool {
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.unpin_object::<VM>(object)
}
#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, object: ObjectReference) -> bool {
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.is_object_pinned::<VM>(object)
}
fn is_movable(&self) -> bool {
super::DEFRAG
}

#[cfg(feature = "sanity")]
fn is_sane(&self) -> bool {
true
Expand Down Expand Up @@ -164,6 +177,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
MetadataSpec::OnSide(Block::MARK_TABLE),
MetadataSpec::OnSide(ChunkMap::ALLOC_TABLE),
*VM::VMObjectModel::LOCAL_MARK_BIT_SPEC,
#[cfg(feature = "object_pinning")]
*VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC,
]
} else {
vec![
Expand All @@ -172,6 +187,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
MetadataSpec::OnSide(Block::MARK_TABLE),
MetadataSpec::OnSide(ChunkMap::ALLOC_TABLE),
*VM::VMObjectModel::LOCAL_MARK_BIT_SPEC,
#[cfg(feature = "object_pinning")]
*VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC,
]
})
}
Expand Down Expand Up @@ -483,7 +500,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
{
if new_object == object {
debug_assert!(
self.is_marked(object, self.mark_state) || self.defrag.space_exhausted() || Self::is_pinned(object),
self.is_marked(object, self.mark_state) || self.defrag.space_exhausted() || self.is_pinned(object),
"Forwarded object is the same as original object {} even though it should have been copied",
object,
);
Expand All @@ -502,7 +519,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
// We won the forwarding race but the object is already marked so we clear the
// forwarding status and return the unmoved object
debug_assert!(
self.defrag.space_exhausted() || Self::is_pinned(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,
);
Expand All @@ -511,7 +528,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
} 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) || self.defrag.space_exhausted() {
let new_object = if self.is_pinned(object) || self.defrag.space_exhausted() {
self.attempt_mark(object, self.mark_state);
ForwardingWord::clear_forwarding_bits::<VM>(object);
Block::containing::<VM>(object).set_state(BlockState::Marked);
Expand Down Expand Up @@ -582,8 +599,11 @@ impl<VM: VMBinding> ImmixSpace<VM> {

/// Check if an object is pinned.
#[inline(always)]
fn is_pinned(_object: ObjectReference) -> bool {
// TODO(wenyuzhao): Object pinning not supported yet.
fn is_pinned(&self, _object: ObjectReference) -> bool {
#[cfg(feature = "object_pinning")]
return self.is_object_pinned(_object);

#[cfg(not(feature = "object_pinning"))]
false
}

Expand Down
12 changes: 12 additions & 0 deletions src/policy/immortalspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ impl<VM: VMBinding> SFT for ImmortalSpace<VM> {
);
old_value == self.mark_state
}
#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
false
}
#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
false
}
#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
true
}
fn is_movable(&self) -> bool {
false
}
Expand Down
12 changes: 12 additions & 0 deletions src/policy/largeobjectspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ impl<VM: VMBinding> SFT for LargeObjectSpace<VM> {
fn is_live(&self, object: ObjectReference) -> bool {
self.test_mark_bit(object, self.mark_state)
}
#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
false
}
#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
false
}
#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
true
}
fn is_movable(&self) -> bool {
false
}
Expand Down
12 changes: 12 additions & 0 deletions src/policy/lockfreeimmortalspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ impl<VM: VMBinding> SFT for LockFreeImmortalSpace<VM> {
fn is_live(&self, _object: ObjectReference) -> bool {
unimplemented!()
}
#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
false
}
#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
false
}
#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
true
}
fn is_movable(&self) -> bool {
unimplemented!()
}
Expand Down
15 changes: 15 additions & 0 deletions src/policy/markcompactspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ impl<VM: VMBinding> SFT for MarkCompactSpace<VM> {
Self::is_marked(object)
}

#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
panic!("Cannot pin/unpin objects of MarkCompactSpace.")
}

#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
panic!("Cannot pin/unpin objects of MarkCompactSpace.")
}

#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
false
}

fn is_movable(&self) -> bool {
true
}
Expand Down
15 changes: 15 additions & 0 deletions src/policy/marksweepspace/malloc_ms/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ impl<VM: VMBinding> SFT for MallocSpace<VM> {
is_marked::<VM>(object, Ordering::SeqCst)
}

#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
false
}

#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
false
}

#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
false
}

fn is_movable(&self) -> bool {
false
}
Expand Down
15 changes: 15 additions & 0 deletions src/policy/marksweepspace/native_ms/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ impl<VM: VMBinding> SFT for MarkSweepSpace<VM> {
VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_marked::<VM>(object, Ordering::SeqCst)
}

#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
false
}

#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
false
}

#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
false
}

fn is_movable(&self) -> bool {
false
}
Expand Down
26 changes: 26 additions & 0 deletions src/policy/sft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ pub trait SFT {
self.is_live(object)
}

// Functions for pinning/unpining and checking if an object is pinned
// For non moving policies, all the objects are considered as forever pinned,
// thus attempting to pin or unpin them will not succeed and will always return false.
// For policies where moving is compusory, pin/unpin is impossible and will panic (is_object_pinned will return false).
// For policies that support pinning (eg. Immix), pin/unpin will return a boolean indicating that the
// pinning/unpinning action has been performed by the function, and is_object_pinned will return whether the object
// is currently pinned.
#[cfg(feature = "object_pinning")]
fn pin_object(&self, object: ObjectReference) -> bool;
#[cfg(feature = "object_pinning")]
fn unpin_object(&self, object: ObjectReference) -> bool;
#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, object: ObjectReference) -> bool;

/// Is the object movable, determined by the policy? E.g. the policy is non-moving,
/// or the object is pinned.
fn is_movable(&self) -> bool;
Expand Down Expand Up @@ -116,6 +130,18 @@ impl SFT for EmptySpaceSFT {
warn!("Object in empty space!");
false
}
#[cfg(feature = "object_pinning")]
fn pin_object(&self, _object: ObjectReference) -> bool {
panic!("Cannot pin/unpin objects of EmptySpace.")
}
#[cfg(feature = "object_pinning")]
fn unpin_object(&self, _object: ObjectReference) -> bool {
panic!("Cannot pin/unpin objects of EmptySpace.")
}
#[cfg(feature = "object_pinning")]
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
false
}
fn is_movable(&self) -> bool {
/*
* FIXME steveb I think this should panic (ie the function should not
Expand Down
1 change: 1 addition & 0 deletions src/util/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,6 @@ pub use metadata_val_traits::*;

pub(crate) mod log_bit;
pub(crate) mod mark_bit;
pub(crate) mod pin_bit;

pub use global::*;
41 changes: 41 additions & 0 deletions src/util/metadata/pin_bit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::util::ObjectReference;
use crate::vm::VMBinding;
use crate::vm::VMLocalPinningBitSpec;
use std::sync::atomic::Ordering;

impl VMLocalPinningBitSpec {
/// Pin the object
pub fn pin_object<VM: VMBinding>(&self, object: ObjectReference) -> bool {
let res = self.compare_exchange_metadata::<VM, u8>(
object,
0,
1,
None,
Ordering::SeqCst,
Ordering::SeqCst,
);

res.is_ok()
}

pub fn unpin_object<VM: VMBinding>(&self, object: ObjectReference) -> bool {
let res = self.compare_exchange_metadata::<VM, u8>(
object,
1,
0,
None,
Ordering::SeqCst,
Ordering::SeqCst,
);

res.is_ok()
}

pub fn is_object_pinned<VM: VMBinding>(&self, object: ObjectReference) -> bool {
if unsafe { self.load::<VM, u8>(object, None) == 1 } {
return true;
}

false
}
}
5 changes: 5 additions & 0 deletions src/vm/object_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ pub trait ObjectModel<VM: VMBinding> {
const LOCAL_FORWARDING_BITS_SPEC: VMLocalForwardingBitsSpec;
/// The metadata specification for the mark bit, used by most plans that need to mark live objects. 1 bit.
const LOCAL_MARK_BIT_SPEC: VMLocalMarkBitSpec;
#[cfg(feature = "object_pinning")]
/// The metadata specification for the pinning bit, used by most plans that need to pin objects. 1 bit.
const LOCAL_PINNING_BIT_SPEC: VMLocalPinningBitSpec;
/// The metadata specification for the mark-and-nursery bits, used by most plans that has large object allocation. 2 bits.
const LOCAL_LOS_MARK_NURSERY_SPEC: VMLocalLOSMarkNurserySpec;

Expand Down Expand Up @@ -532,6 +535,8 @@ pub mod specs {
define_vm_metadata_spec!(VMLocalForwardingBitsSpec, false, 1, LOG_MIN_OBJECT_SIZE);
// Mark bit: 1 bit per object, local
define_vm_metadata_spec!(VMLocalMarkBitSpec, false, 0, LOG_MIN_OBJECT_SIZE);
// Pinning bit: 1 bit per object, local
define_vm_metadata_spec!(VMLocalPinningBitSpec, false, 0, LOG_MIN_OBJECT_SIZE);
// Mark&nursery bits for LOS: 2 bit per page, local
define_vm_metadata_spec!(VMLocalLOSMarkNurserySpec, false, 1, LOG_BYTES_IN_PAGE);
}