mmtk/util/metadata/
mark_bit.rs

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