Skip to content

Commit 3dbdd7a

Browse files
authored
Don't panic if forwarding word is not FORWARDED and add debug assertions (#580)
* Don't panic if forwarding word is not FORWARDED and add debug assertions Some policies (such as Immix) can leave objects inplace and can reset the forwarding word while tracing (such as when Immix is out of space in its copy allocators). In such a case, we simply want to return the current object instead of attempting to read the forwarding pointer. This commit removes our faulty assumption and assertion and adds further debug assertions for a sanity check. Closes #579
1 parent ce82a7d commit 3dbdd7a

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed

src/policy/immix/immixspace.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,11 +391,44 @@ impl<VM: VMBinding> ImmixSpace<VM> {
391391
debug_assert!(!super::BLOCK_ONLY);
392392
let forwarding_status = ForwardingWord::attempt_to_forward::<VM>(object);
393393
if ForwardingWord::state_is_forwarded_or_being_forwarded(forwarding_status) {
394-
ForwardingWord::spin_and_get_forwarded_object::<VM>(object, forwarding_status)
394+
// We lost the forwarding race as some other thread has set the forwarding word; wait
395+
// until the object has been forwarded by the winner. Note that the object may not
396+
// necessarily get forwarded since Immix opportunistically moves objects.
397+
#[allow(clippy::let_and_return)]
398+
let new_object =
399+
ForwardingWord::spin_and_get_forwarded_object::<VM>(object, forwarding_status);
400+
#[cfg(debug_assertions)]
401+
{
402+
if new_object == object {
403+
debug_assert!(
404+
self.is_marked(object, self.mark_state) || self.defrag.space_exhausted() || Self::is_pinned(object),
405+
"Forwarded object is the same as original object {} even though it should have been copied",
406+
object,
407+
);
408+
} else {
409+
// new_object != object
410+
debug_assert!(
411+
!Block::containing::<VM>(new_object).is_defrag_source(),
412+
"Block {:?} containing forwarded object {} should not be a defragmentation source",
413+
Block::containing::<VM>(new_object),
414+
new_object,
415+
);
416+
}
417+
}
418+
new_object
395419
} else if self.is_marked(object, self.mark_state) {
420+
// We won the forwarding race but the object is already marked so we clear the
421+
// forwarding status and return the unmoved object
422+
debug_assert!(
423+
self.defrag.space_exhausted() || Self::is_pinned(object),
424+
"Forwarded object is the same as original object {} even though it should have been copied",
425+
object,
426+
);
396427
ForwardingWord::clear_forwarding_bits::<VM>(object);
397428
object
398429
} else {
430+
// We won the forwarding race; actually forward and copy the object if it is not pinned
431+
// and we have sufficient space in our copy allocator
399432
let new_object = if Self::is_pinned(object) || self.defrag.space_exhausted() {
400433
self.attempt_mark(object, self.mark_state);
401434
ForwardingWord::clear_forwarding_bits::<VM>(object);

src/util/object_forwarding.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,16 @@ pub fn spin_and_get_forwarded_object<VM: VMBinding>(
6363
if forwarding_bits == FORWARDED {
6464
read_forwarding_pointer::<VM>(object)
6565
} else {
66-
panic!(
67-
"Invalid forwarding state 0x{:x} 0x{:x} for object {}",
66+
// For some policies (such as Immix), we can have interleaving such that one thread clears
67+
// the forwarding word while another thread was stuck spinning in the above loop.
68+
// See: https://github.com/mmtk/mmtk-core/issues/579
69+
debug_assert!(
70+
forwarding_bits == FORWARDING_NOT_TRIGGERED_YET,
71+
"Invalid/Corrupted forwarding word {:x} for object {}",
6872
forwarding_bits,
69-
read_forwarding_pointer::<VM>(object),
70-
object
71-
)
73+
object,
74+
);
75+
object
7276
}
7377
}
7478

0 commit comments

Comments
 (0)