Skip to content

Commit 328deb6

Browse files
authored
Fix a race between forwarding bits and VO bits. (#1214)
The current code sets the forwarding bits before setting the VO bit when copying an object. If another GC worker is attempting to forward the same object, it may observe the forwarding bits being `FORWARDED` but the VO bit is not set. This violates the semantics of VO bits because VO bits should be set for both from-space and to-space copies. This will affect VM bindings that assert slots always refer to a valid object when scanning objects and may update the same slot multiple times for some reasons. This revision provides a mechanism to ensure that all necessary metadata are set before setting forwarding bits to `FORWARDED`. Currently it affects the VO bits and the mark bits (which are used to update the VO bits in Immix-based plans). It may be used for other metadata introduced in the future.
1 parent 58b3b35 commit 328deb6

File tree

3 files changed

+31
-12
lines changed

3 files changed

+31
-12
lines changed

src/policy/copyspace.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,12 @@ impl<VM: VMBinding> CopySpace<VM> {
252252
object,
253253
semantics.unwrap(),
254254
worker.get_copy_context_mut(),
255+
|_new_object| {
256+
#[cfg(feature = "vo_bit")]
257+
crate::util::metadata::vo_bit::set_vo_bit(_new_object);
258+
},
255259
);
256260

257-
#[cfg(feature = "vo_bit")]
258-
crate::util::metadata::vo_bit::set_vo_bit(new_object);
259-
260261
trace!("Forwarding pointer");
261262
queue.enqueue(new_object);
262263
trace!("Copied [{:?} -> {:?}]", object, new_object);

src/policy/immix/immixspace.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -660,15 +660,15 @@ impl<VM: VMBinding> ImmixSpace<VM> {
660660
// We are forwarding objects. When the copy allocator allocates the block, it should
661661
// mark the block. So we do not need to explicitly mark it here.
662662

663-
// Clippy complains if the "vo_bit" feature is not enabled.
664-
#[allow(clippy::let_and_return)]
665-
let new_object =
666-
object_forwarding::forward_object::<VM>(object, semantics, copy_context);
667-
668-
#[cfg(feature = "vo_bit")]
669-
vo_bit::helper::on_object_forwarded::<VM>(new_object);
670-
671-
new_object
663+
object_forwarding::forward_object::<VM>(
664+
object,
665+
semantics,
666+
copy_context,
667+
|_new_object| {
668+
#[cfg(feature = "vo_bit")]
669+
vo_bit::helper::on_object_forwarded::<VM>(_new_object);
670+
},
671+
)
672672
};
673673
debug_assert_eq!(
674674
Block::containing(new_object).get_state(),

src/util/object_forwarding.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,30 @@ pub fn spin_and_get_forwarded_object<VM: VMBinding>(
7474
}
7575
}
7676

77+
/// Copy an object and set the forwarding state.
78+
///
79+
/// The caller can use `on_after_forwarding` to set extra metadata (including VO bits, mark bits,
80+
/// etc.) after the object is copied, but before the forwarding state is changed to `FORWARDED`. The
81+
/// atomic memory operation that sets the forwarding bits to `FORWARDED` has the `SeqCst` order. It
82+
/// will guarantee that if another GC worker thread that attempts to forward the same object sees
83+
/// the forwarding bits being `FORWARDED`, it is guaranteed to see those extra metadata set.
84+
///
85+
/// Arguments:
86+
///
87+
/// * `object`: The object to copy.
88+
/// * `semantics`: The copy semantics.
89+
/// * `copy_context`: A reference ot the `CopyContext` instance of the current GC worker.
90+
/// * `on_after_forwarding`: A callback function that is called after `object` is copied, but
91+
/// before the forwarding bits are set. Its argument is a reference to the new copy of
92+
/// `object`.
7793
pub fn forward_object<VM: VMBinding>(
7894
object: ObjectReference,
7995
semantics: CopySemantics,
8096
copy_context: &mut GCWorkerCopyContext<VM>,
97+
on_after_forwarding: impl FnOnce(ObjectReference),
8198
) -> ObjectReference {
8299
let new_object = VM::VMObjectModel::copy(object, semantics, copy_context);
100+
on_after_forwarding(new_object);
83101
if let Some(shift) = forwarding_bits_offset_in_forwarding_pointer::<VM>() {
84102
VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>(
85103
object,

0 commit comments

Comments
 (0)