Skip to content

Commit 68bf1b6

Browse files
authored
Unique object enqueuing option (mmtk#1255)
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: mmtk#1254
1 parent ec74535 commit 68bf1b6

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

src/policy/marksweepspace/native_ms/global.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::util::heap::chunk_map::*;
2929
use crate::util::linear_scan::Region;
3030
use crate::util::VMThread;
3131
use crate::vm::ObjectModel;
32+
use crate::vm::Scanning;
3233
use std::sync::Mutex;
3334

3435
/// The result for `MarkSweepSpace.acquire_block()`. `MarkSweepSpace` will attempt
@@ -329,6 +330,58 @@ impl<VM: VMBinding> MarkSweepSpace<VM> {
329330
}
330331
}
331332

333+
/// Mark an object non-atomically. If multiple GC worker threads attempt to mark the same
334+
/// object, more than one of them may return `true`.
335+
fn attempt_mark_non_atomic(&self, object: ObjectReference) -> bool {
336+
if !VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_marked::<VM>(object, Ordering::SeqCst) {
337+
VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.mark::<VM>(object, Ordering::SeqCst);
338+
true
339+
} else {
340+
false
341+
}
342+
}
343+
344+
/// Mark an object atomically.
345+
fn attempt_mark_atomic(&self, object: ObjectReference) -> bool {
346+
let mark_state = 1u8;
347+
348+
loop {
349+
let old_value = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.load_atomic::<VM, u8>(
350+
object,
351+
None,
352+
Ordering::SeqCst,
353+
);
354+
if old_value == mark_state {
355+
return false;
356+
}
357+
358+
if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
359+
.compare_exchange_metadata::<VM, u8>(
360+
object,
361+
old_value,
362+
mark_state,
363+
None,
364+
Ordering::SeqCst,
365+
Ordering::SeqCst,
366+
)
367+
.is_ok()
368+
{
369+
break;
370+
}
371+
}
372+
true
373+
}
374+
375+
/// Mark an object. Return `true` if the object is newly marked. Return `false` if the object
376+
/// was already marked.
377+
fn attempt_mark(&self, object: ObjectReference) -> bool {
378+
if VM::VMScanning::UNIQUE_OBJECT_ENQUEUING {
379+
self.attempt_mark_atomic(object)
380+
} else {
381+
self.attempt_mark_non_atomic(object)
382+
}
383+
}
384+
332385
fn trace_object<Q: ObjectQueue>(
333386
&self,
334387
queue: &mut Q,
@@ -339,8 +392,7 @@ impl<VM: VMBinding> MarkSweepSpace<VM> {
339392
"Cannot mark an object {} that was not alloced by free list allocator.",
340393
object,
341394
);
342-
if !VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_marked::<VM>(object, Ordering::SeqCst) {
343-
VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.mark::<VM>(object, Ordering::SeqCst);
395+
if self.attempt_mark(object) {
344396
let block = Block::containing(object);
345397
block.set_state(BlockState::Marked);
346398
queue.enqueue(object);

src/vm/scanning.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,21 @@ pub trait RootsWorkFactory<SL: Slot>: Clone + Send + 'static {
140140

141141
/// VM-specific methods for scanning roots/objects.
142142
pub trait Scanning<VM: VMBinding> {
143+
/// When set to `true`, all plans will guarantee that during each GC, each live object is
144+
/// enqueued at most once, and therefore scanned (by either [`Scanning::scan_object`] or
145+
/// [`Scanning::scan_object_and_trace_edges`]) at most once.
146+
///
147+
/// When set to `false`, MMTk may enqueue an object multiple times due to optimizations, such as
148+
/// using non-atomic operatios to mark objects. Consequently, an object may be scanned multiple
149+
/// times during a GC.
150+
///
151+
/// The default value is `false` because duplicated object-enqueuing is benign for most VMs, and
152+
/// related optimizations, such as non-atomic marking, can improve GC speed. VM bindings can
153+
/// override this if they need. For example, some VMs piggyback on object-scanning to visit
154+
/// objects during a GC, but may have data race if multiple GC workers visit the same object at
155+
/// the same time. Such VMs can set this constant to `true` to workaround this problem.
156+
const UNIQUE_OBJECT_ENQUEUING: bool = false;
157+
143158
/// Return true if the given object supports slot enqueuing.
144159
///
145160
/// - If this returns true, MMTk core will call `scan_object` on the object.

0 commit comments

Comments
 (0)