Skip to content

Commit 6e91fc3

Browse files
committed
Unique object enqueuing option
Added a constant `VMBinding::UNIQUE_OBJECT_ENQUEUING`. When set to true, MMTk will guarantee that each object is enqueued at most once in each GC. This can be useful for VMs that piggyback on object scanning to visit objects during GC. Implementation-wise, the mark bit is set atomically when `VMBinding::UNIQUE_OBJECT_ENQUEUING` is true. This PR only affects the native MarkSweep space. Other spaces already do this atomically. Fixes: #1254
1 parent ec74535 commit 6e91fc3

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

src/policy/marksweepspace/native_ms/global.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,56 @@ impl<VM: VMBinding> MarkSweepSpace<VM> {
329329
}
330330
}
331331

332+
/// Mark an object non-atomically. If multiple GC worker threads attempt to mark the same
333+
/// object, more than one of them may return `true`.
334+
fn attempt_mark_non_atomic(&self, object: ObjectReference) -> bool {
335+
if !VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_marked::<VM>(object, Ordering::SeqCst) {
336+
VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.mark::<VM>(object, Ordering::SeqCst);
337+
true
338+
} else {
339+
false
340+
}
341+
}
342+
343+
/// Mark an object atomically.
344+
fn attempt_mark_atomic(&self, object: ObjectReference) -> bool {
345+
loop {
346+
let old_value = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.load_atomic::<VM, u8>(
347+
object,
348+
None,
349+
Ordering::SeqCst,
350+
);
351+
if old_value == 1u8 {
352+
return false;
353+
}
354+
355+
if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
356+
.compare_exchange_metadata::<VM, u8>(
357+
object,
358+
old_value,
359+
1u8,
360+
None,
361+
Ordering::SeqCst,
362+
Ordering::SeqCst,
363+
)
364+
.is_ok()
365+
{
366+
break;
367+
}
368+
}
369+
true
370+
}
371+
372+
/// Mark an object. Return `true` if the object is newly marked. Return `false` if the object
373+
/// was already marked.
374+
fn attempt_mark(&self, object: ObjectReference) -> bool {
375+
if VM::UNIQUE_OBJECT_ENQUEUING {
376+
self.attempt_mark_atomic(object)
377+
} else {
378+
self.attempt_mark_non_atomic(object)
379+
}
380+
}
381+
332382
fn trace_object<Q: ObjectQueue>(
333383
&self,
334384
queue: &mut Q,

src/vm/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,19 @@ where
7979
/// Note that MMTk does not attempt to do anything to align the cursor to this value, but
8080
/// it merely asserts with this constant.
8181
const ALLOC_END_ALIGNMENT: usize = 1;
82+
83+
/// When set to `true`, all plans will guarantee that during each GC, each live object is
84+
/// enqueued at most once, and therefore scanned (by either [`Scanning::scan_object`] or
85+
/// [`Scanning::scan_object_and_trace_edges`]) at most once.
86+
///
87+
/// When set to `false`, MMTk may enqueue an object multiple times due to optimizations, such as
88+
/// using non-atomic operatios to mark objects. Consequently, an object may be scanned multiple
89+
/// times during a GC.
90+
///
91+
/// The default value is `false` because duplicated object-enqueuing is benign for most VMs, and
92+
/// related optimizations, such as non-atomic marking, can improve GC speed. VM bindings can
93+
/// override this if they need. For example, some VMs piggyback on object-scanning to visit
94+
/// objects during a GC, but may have data race if multiple GC workers visit the same object at
95+
/// the same time. Such VMs can set this constant to `true` to workaround this problem.
96+
const UNIQUE_OBJECT_ENQUEUING: bool = false;
8297
}

0 commit comments

Comments
 (0)