1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
use crate::util::Address;
use crate::util::ObjectReference;
use crate::vm::ObjectModel;
use crate::vm::VMBinding;
use crate::vm::VMLocalMarkBitSpec;
use std::sync::atomic::Ordering;
impl VMLocalMarkBitSpec {
/// Set the mark bit for the object to 1
pub fn mark<VM: VMBinding>(&self, object: ObjectReference, ordering: Ordering) {
self.store_atomic::<VM, u8>(object, 1, None, ordering);
}
/// Test if the mark bit for the object is set (1)
pub fn is_marked<VM: VMBinding>(&self, object: ObjectReference, ordering: Ordering) -> bool {
self.load_atomic::<VM, u8>(object, None, ordering) == 1
}
}
/// This provides an abstraction of the mark bit. It abstracts over the difference between side mark bits and in-header mark bits,
/// and provides efficient implementation for each case.
///
/// The key difference between side and in-header mark bit is what the mark state represents.
///
/// * Side mark bit
/// We always use 1 for the marked state. So we do not need to set mark bit for new objects (mark bit 0). In each GC, we do bulk zeroing
/// to reset the mark bit to 0 before tracing or after tracing. During tracing, we mark objects with the state 1 as usual.
/// * In-header mark bit
/// We flip the mark state in every GC. For example, if 1 means marked in the current GC, 1 will mean unmarked in the next GC.
/// With this approach, we do not need to reset mark bit for each object, as the value represents unmarked in the next GC.
/// However, with in-header mark bit, we have to set mark bit for newly allocated objects.
///
/// A policy could use this struct instead of the raw mark bit. It has to call all the methods prefixed with `on_`
/// such as `on_object_metadata_initialization()`, `on_global_prepare()`, `on_block_prepare()`, and `on_global_release()`.
// TODO: Currently only ImmortalSpace uses this struct. Any policy that needs mark bit can use this (immix, mark compact, mark sweep).
// We should do some refactoring for other policies as well.
pub struct MarkState {
/// This value represents the marked state. If the mark bit is this value, the object is considered as marked.
/// If the mark bit is on side, we always use 1 as the marked state. We do bulk zeroing to reset mark bits before GCs
/// If the mark bit is in header, we flip the marked state in every GC, so we do not need to reset the mark bit for live objects.
state: u8,
}
impl MarkState {
pub fn new() -> Self {
Self { state: 1 }
}
fn unmarked_state(&self) -> u8 {
self.state ^ 1
}
/// Check if the object is marked
pub fn is_marked<VM: VMBinding>(&self, object: ObjectReference) -> bool {
let state = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.load_atomic::<VM, u8>(
object,
None,
Ordering::SeqCst,
);
state == self.state
}
/// Attempt to mark an object. If the object is marked by this invocation, return true.
/// Otherwise return false -- the object was marked by others.
pub fn test_and_mark<VM: VMBinding>(&self, object: ObjectReference) -> bool {
loop {
let old_value = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.load_atomic::<VM, u8>(
object,
None,
Ordering::SeqCst,
);
if old_value == self.state {
return false;
}
if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
.compare_exchange_metadata::<VM, u8>(
object,
old_value,
self.state,
None,
Ordering::SeqCst,
Ordering::SeqCst,
)
.is_ok()
{
break;
}
}
true
}
/// This has to be called during object initialization.
pub fn on_object_metadata_initialization<VM: VMBinding>(&self, object: ObjectReference) {
// If it is in header, we have to set the mark bit for every newly allocated object
if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_in_header() {
VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.store_atomic::<VM, u8>(
object,
self.unmarked_state(),
None,
Ordering::SeqCst,
);
}
}
/// This has to be called in the global preparation of a space
pub fn on_global_prepare<VM: VMBinding>(&mut self) {}
/// This has to be called when a space resets its memory regions. This can be either called before the GC tracing, or
/// after a GC tracing (eagerly). This method will reset the mark bit. The policy should not use the mark bit before
/// doing another tracing.
pub fn on_block_reset<VM: VMBinding>(&self, start: Address, size: usize) {
if let crate::util::metadata::MetadataSpec::OnSide(side) =
*VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
{
side.bzero_metadata(start, size);
}
}
/// This has to be called in the global release of a space
pub fn on_global_release<VM: VMBinding>(&mut self) {
if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_in_header() {
// If it is in header, we flip it. In this case, we do not need to reset the bits for marked objects
self.state = self.unmarked_state()
}
}
}