Skip to content

Commit 6fe9bbc

Browse files
authored
Support for object pinning (#703)
This is a draft PR to support object pinning. I have added three methods to the SFT: `pin_object`, `unpin_object`, and `is_object_pinned`. In policies where pinning should not be supported I've created a panic message. For policies where objects do not move, all methods simply return false and do nothing. The PR also adds a `PinningBitSpec` to the object model, and check to the `trace_object_with_opportunistic_copy` function before attempting to forward objects or clear the forwarding status.
1 parent 5a7617d commit 6fe9bbc

File tree

14 files changed

+241
-5
lines changed

14 files changed

+241
-5
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ global_alloc_bit = []
8585
# conservative garbage collection support
8686
is_mmtk_object = ["global_alloc_bit"]
8787

88+
# Enable object pinning, in particular, enable pinning/unpinning, and its metadata
89+
object_pinning = []
90+
8891
# The following two features are useful for using Immix for VMs that do not support moving GC.
8992

9093
# Disable defragmentation for ImmixSpace. This makes Immix a non-moving plan.

src/memory_manager.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,50 @@ pub fn add_finalizer<VM: VMBinding>(
761761
mmtk.finalizable_processor.lock().unwrap().add(object);
762762
}
763763

764+
/// Pin an object. MMTk will make sure that the object does not move
765+
/// during GC. Note that action cannot happen in some plans, eg, semispace.
766+
/// It returns true if the pinning operation has been performed, i.e.,
767+
/// the object status changed from non-pinned to pinned
768+
///
769+
/// Arguments:
770+
/// * `object`: The object to be pinned
771+
#[cfg(feature = "object_pinning")]
772+
pub fn pin_object<VM: VMBinding>(object: ObjectReference) -> bool {
773+
use crate::mmtk::SFT_MAP;
774+
use crate::policy::sft_map::SFTMap;
775+
SFT_MAP
776+
.get_checked(object.to_address::<VM>())
777+
.pin_object(object)
778+
}
779+
780+
/// Unpin an object.
781+
/// Returns true if the unpinning operation has been performed, i.e.,
782+
/// the object status changed from pinned to non-pinned
783+
///
784+
/// Arguments:
785+
/// * `object`: The object to be pinned
786+
#[cfg(feature = "object_pinning")]
787+
pub fn unpin_object<VM: VMBinding>(object: ObjectReference) -> bool {
788+
use crate::mmtk::SFT_MAP;
789+
use crate::policy::sft_map::SFTMap;
790+
SFT_MAP
791+
.get_checked(object.to_address::<VM>())
792+
.unpin_object(object)
793+
}
794+
795+
/// Check whether an object is currently pinned
796+
///
797+
/// Arguments:
798+
/// * `object`: The object to be checked
799+
#[cfg(feature = "object_pinning")]
800+
pub fn is_pinned<VM: VMBinding>(object: ObjectReference) -> bool {
801+
use crate::mmtk::SFT_MAP;
802+
use crate::policy::sft_map::SFTMap;
803+
SFT_MAP
804+
.get_checked(object.to_address::<VM>())
805+
.is_object_pinned(object)
806+
}
807+
764808
/// Get an object that is ready for finalization. After each GC, if any registered object is not
765809
/// alive, this call will return one of the objects. MMTk will retain the liveness of those objects
766810
/// until they are popped through this call. Once an object is popped, it is the responsibility of

src/policy/copyspace.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ impl<VM: VMBinding> SFT for CopySpace<VM> {
3636
!self.is_from_space() || object_forwarding::is_forwarded::<VM>(object)
3737
}
3838

39+
#[cfg(feature = "object_pinning")]
40+
fn pin_object(&self, _object: ObjectReference) -> bool {
41+
panic!("Cannot pin/unpin objects of CopySpace.")
42+
}
43+
44+
#[cfg(feature = "object_pinning")]
45+
fn unpin_object(&self, _object: ObjectReference) -> bool {
46+
panic!("Cannot pin/unpin objects of CopySpace.")
47+
}
48+
49+
#[cfg(feature = "object_pinning")]
50+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
51+
false
52+
}
53+
3954
fn is_movable(&self) -> bool {
4055
true
4156
}

src/policy/immix/immixspace.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,22 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
6363
self.is_marked(object, self.mark_state) || ForwardingWord::is_forwarded::<VM>(object)
6464
}
6565
}
66+
#[cfg(feature = "object_pinning")]
67+
fn pin_object(&self, object: ObjectReference) -> bool {
68+
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.pin_object::<VM>(object)
69+
}
70+
#[cfg(feature = "object_pinning")]
71+
fn unpin_object(&self, object: ObjectReference) -> bool {
72+
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.unpin_object::<VM>(object)
73+
}
74+
#[cfg(feature = "object_pinning")]
75+
fn is_object_pinned(&self, object: ObjectReference) -> bool {
76+
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.is_object_pinned::<VM>(object)
77+
}
6678
fn is_movable(&self) -> bool {
6779
super::DEFRAG
6880
}
81+
6982
#[cfg(feature = "sanity")]
7083
fn is_sane(&self) -> bool {
7184
true
@@ -164,6 +177,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
164177
MetadataSpec::OnSide(Block::MARK_TABLE),
165178
MetadataSpec::OnSide(ChunkMap::ALLOC_TABLE),
166179
*VM::VMObjectModel::LOCAL_MARK_BIT_SPEC,
180+
#[cfg(feature = "object_pinning")]
181+
*VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC,
167182
]
168183
} else {
169184
vec![
@@ -172,6 +187,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
172187
MetadataSpec::OnSide(Block::MARK_TABLE),
173188
MetadataSpec::OnSide(ChunkMap::ALLOC_TABLE),
174189
*VM::VMObjectModel::LOCAL_MARK_BIT_SPEC,
190+
#[cfg(feature = "object_pinning")]
191+
*VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC,
175192
]
176193
})
177194
}
@@ -483,7 +500,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
483500
{
484501
if new_object == object {
485502
debug_assert!(
486-
self.is_marked(object, self.mark_state) || self.defrag.space_exhausted() || Self::is_pinned(object),
503+
self.is_marked(object, self.mark_state) || self.defrag.space_exhausted() || self.is_pinned(object),
487504
"Forwarded object is the same as original object {} even though it should have been copied",
488505
object,
489506
);
@@ -502,7 +519,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
502519
// We won the forwarding race but the object is already marked so we clear the
503520
// forwarding status and return the unmoved object
504521
debug_assert!(
505-
self.defrag.space_exhausted() || Self::is_pinned(object),
522+
self.defrag.space_exhausted() || self.is_pinned(object),
506523
"Forwarded object is the same as original object {} even though it should have been copied",
507524
object,
508525
);
@@ -511,7 +528,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
511528
} else {
512529
// We won the forwarding race; actually forward and copy the object if it is not pinned
513530
// and we have sufficient space in our copy allocator
514-
let new_object = if Self::is_pinned(object) || self.defrag.space_exhausted() {
531+
let new_object = if self.is_pinned(object) || self.defrag.space_exhausted() {
515532
self.attempt_mark(object, self.mark_state);
516533
ForwardingWord::clear_forwarding_bits::<VM>(object);
517534
Block::containing::<VM>(object).set_state(BlockState::Marked);
@@ -582,8 +599,11 @@ impl<VM: VMBinding> ImmixSpace<VM> {
582599

583600
/// Check if an object is pinned.
584601
#[inline(always)]
585-
fn is_pinned(_object: ObjectReference) -> bool {
586-
// TODO(wenyuzhao): Object pinning not supported yet.
602+
fn is_pinned(&self, _object: ObjectReference) -> bool {
603+
#[cfg(feature = "object_pinning")]
604+
return self.is_object_pinned(_object);
605+
606+
#[cfg(not(feature = "object_pinning"))]
587607
false
588608
}
589609

src/policy/immortalspace.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ impl<VM: VMBinding> SFT for ImmortalSpace<VM> {
4545
);
4646
old_value == self.mark_state
4747
}
48+
#[cfg(feature = "object_pinning")]
49+
fn pin_object(&self, _object: ObjectReference) -> bool {
50+
false
51+
}
52+
#[cfg(feature = "object_pinning")]
53+
fn unpin_object(&self, _object: ObjectReference) -> bool {
54+
false
55+
}
56+
#[cfg(feature = "object_pinning")]
57+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
58+
true
59+
}
4860
fn is_movable(&self) -> bool {
4961
false
5062
}

src/policy/largeobjectspace.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ impl<VM: VMBinding> SFT for LargeObjectSpace<VM> {
4343
fn is_live(&self, object: ObjectReference) -> bool {
4444
self.test_mark_bit(object, self.mark_state)
4545
}
46+
#[cfg(feature = "object_pinning")]
47+
fn pin_object(&self, _object: ObjectReference) -> bool {
48+
false
49+
}
50+
#[cfg(feature = "object_pinning")]
51+
fn unpin_object(&self, _object: ObjectReference) -> bool {
52+
false
53+
}
54+
#[cfg(feature = "object_pinning")]
55+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
56+
true
57+
}
4658
fn is_movable(&self) -> bool {
4759
false
4860
}

src/policy/lockfreeimmortalspace.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ impl<VM: VMBinding> SFT for LockFreeImmortalSpace<VM> {
5050
fn is_live(&self, _object: ObjectReference) -> bool {
5151
unimplemented!()
5252
}
53+
#[cfg(feature = "object_pinning")]
54+
fn pin_object(&self, _object: ObjectReference) -> bool {
55+
false
56+
}
57+
#[cfg(feature = "object_pinning")]
58+
fn unpin_object(&self, _object: ObjectReference) -> bool {
59+
false
60+
}
61+
#[cfg(feature = "object_pinning")]
62+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
63+
true
64+
}
5365
fn is_movable(&self) -> bool {
5466
unimplemented!()
5567
}

src/policy/markcompactspace.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ impl<VM: VMBinding> SFT for MarkCompactSpace<VM> {
5252
Self::is_marked(object)
5353
}
5454

55+
#[cfg(feature = "object_pinning")]
56+
fn pin_object(&self, _object: ObjectReference) -> bool {
57+
panic!("Cannot pin/unpin objects of MarkCompactSpace.")
58+
}
59+
60+
#[cfg(feature = "object_pinning")]
61+
fn unpin_object(&self, _object: ObjectReference) -> bool {
62+
panic!("Cannot pin/unpin objects of MarkCompactSpace.")
63+
}
64+
65+
#[cfg(feature = "object_pinning")]
66+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
67+
false
68+
}
69+
5570
fn is_movable(&self) -> bool {
5671
true
5772
}

src/policy/marksweepspace/malloc_ms/global.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,21 @@ impl<VM: VMBinding> SFT for MallocSpace<VM> {
7171
is_marked::<VM>(object, Ordering::SeqCst)
7272
}
7373

74+
#[cfg(feature = "object_pinning")]
75+
fn pin_object(&self, _object: ObjectReference) -> bool {
76+
false
77+
}
78+
79+
#[cfg(feature = "object_pinning")]
80+
fn unpin_object(&self, _object: ObjectReference) -> bool {
81+
false
82+
}
83+
84+
#[cfg(feature = "object_pinning")]
85+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
86+
false
87+
}
88+
7489
fn is_movable(&self) -> bool {
7590
false
7691
}

src/policy/marksweepspace/native_ms/global.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,21 @@ impl<VM: VMBinding> SFT for MarkSweepSpace<VM> {
8989
VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_marked::<VM>(object, Ordering::SeqCst)
9090
}
9191

92+
#[cfg(feature = "object_pinning")]
93+
fn pin_object(&self, _object: ObjectReference) -> bool {
94+
false
95+
}
96+
97+
#[cfg(feature = "object_pinning")]
98+
fn unpin_object(&self, _object: ObjectReference) -> bool {
99+
false
100+
}
101+
102+
#[cfg(feature = "object_pinning")]
103+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
104+
false
105+
}
106+
92107
fn is_movable(&self) -> bool {
93108
false
94109
}

src/policy/sft.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ pub trait SFT {
4242
self.is_live(object)
4343
}
4444

45+
// Functions for pinning/unpining and checking if an object is pinned
46+
// For non moving policies, all the objects are considered as forever pinned,
47+
// thus attempting to pin or unpin them will not succeed and will always return false.
48+
// For policies where moving is compusory, pin/unpin is impossible and will panic (is_object_pinned will return false).
49+
// For policies that support pinning (eg. Immix), pin/unpin will return a boolean indicating that the
50+
// pinning/unpinning action has been performed by the function, and is_object_pinned will return whether the object
51+
// is currently pinned.
52+
#[cfg(feature = "object_pinning")]
53+
fn pin_object(&self, object: ObjectReference) -> bool;
54+
#[cfg(feature = "object_pinning")]
55+
fn unpin_object(&self, object: ObjectReference) -> bool;
56+
#[cfg(feature = "object_pinning")]
57+
fn is_object_pinned(&self, object: ObjectReference) -> bool;
58+
4559
/// Is the object movable, determined by the policy? E.g. the policy is non-moving,
4660
/// or the object is pinned.
4761
fn is_movable(&self) -> bool;
@@ -116,6 +130,18 @@ impl SFT for EmptySpaceSFT {
116130
warn!("Object in empty space!");
117131
false
118132
}
133+
#[cfg(feature = "object_pinning")]
134+
fn pin_object(&self, _object: ObjectReference) -> bool {
135+
panic!("Cannot pin/unpin objects of EmptySpace.")
136+
}
137+
#[cfg(feature = "object_pinning")]
138+
fn unpin_object(&self, _object: ObjectReference) -> bool {
139+
panic!("Cannot pin/unpin objects of EmptySpace.")
140+
}
141+
#[cfg(feature = "object_pinning")]
142+
fn is_object_pinned(&self, _object: ObjectReference) -> bool {
143+
false
144+
}
119145
fn is_movable(&self) -> bool {
120146
/*
121147
* FIXME steveb I think this should panic (ie the function should not

src/util/metadata/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,6 @@ pub use metadata_val_traits::*;
226226

227227
pub(crate) mod log_bit;
228228
pub(crate) mod mark_bit;
229+
pub(crate) mod pin_bit;
229230

230231
pub use global::*;

src/util/metadata/pin_bit.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use crate::util::ObjectReference;
2+
use crate::vm::VMBinding;
3+
use crate::vm::VMLocalPinningBitSpec;
4+
use std::sync::atomic::Ordering;
5+
6+
impl VMLocalPinningBitSpec {
7+
/// Pin the object
8+
pub fn pin_object<VM: VMBinding>(&self, object: ObjectReference) -> bool {
9+
let res = self.compare_exchange_metadata::<VM, u8>(
10+
object,
11+
0,
12+
1,
13+
None,
14+
Ordering::SeqCst,
15+
Ordering::SeqCst,
16+
);
17+
18+
res.is_ok()
19+
}
20+
21+
pub fn unpin_object<VM: VMBinding>(&self, object: ObjectReference) -> bool {
22+
let res = self.compare_exchange_metadata::<VM, u8>(
23+
object,
24+
1,
25+
0,
26+
None,
27+
Ordering::SeqCst,
28+
Ordering::SeqCst,
29+
);
30+
31+
res.is_ok()
32+
}
33+
34+
pub fn is_object_pinned<VM: VMBinding>(&self, object: ObjectReference) -> bool {
35+
if unsafe { self.load::<VM, u8>(object, None) == 1 } {
36+
return true;
37+
}
38+
39+
false
40+
}
41+
}

src/vm/object_model.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ pub trait ObjectModel<VM: VMBinding> {
8181
const LOCAL_FORWARDING_BITS_SPEC: VMLocalForwardingBitsSpec;
8282
/// The metadata specification for the mark bit, used by most plans that need to mark live objects. 1 bit.
8383
const LOCAL_MARK_BIT_SPEC: VMLocalMarkBitSpec;
84+
#[cfg(feature = "object_pinning")]
85+
/// The metadata specification for the pinning bit, used by most plans that need to pin objects. 1 bit.
86+
const LOCAL_PINNING_BIT_SPEC: VMLocalPinningBitSpec;
8487
/// The metadata specification for the mark-and-nursery bits, used by most plans that has large object allocation. 2 bits.
8588
const LOCAL_LOS_MARK_NURSERY_SPEC: VMLocalLOSMarkNurserySpec;
8689

@@ -532,6 +535,8 @@ pub mod specs {
532535
define_vm_metadata_spec!(VMLocalForwardingBitsSpec, false, 1, LOG_MIN_OBJECT_SIZE);
533536
// Mark bit: 1 bit per object, local
534537
define_vm_metadata_spec!(VMLocalMarkBitSpec, false, 0, LOG_MIN_OBJECT_SIZE);
538+
// Pinning bit: 1 bit per object, local
539+
define_vm_metadata_spec!(VMLocalPinningBitSpec, false, 0, LOG_MIN_OBJECT_SIZE);
535540
// Mark&nursery bits for LOS: 2 bit per page, local
536541
define_vm_metadata_spec!(VMLocalLOSMarkNurserySpec, false, 1, LOG_BYTES_IN_PAGE);
537542
}

0 commit comments

Comments
 (0)