@@ -44,29 +44,35 @@ impl Chunk {
44
44
}
45
45
}
46
46
47
- /// Chunk allocation state
47
+ /// The allocation state for a chunk in the chunk map. It includes whether each chunk is allocated or free, and the space the chunk belongs to.
48
48
/// Highest bit: 0 = free, 1 = allocated
49
49
/// Lower 4 bits: Space index (0-15)
50
50
#[ repr( transparent) ]
51
51
#[ derive( PartialEq , Clone , Copy ) ]
52
52
pub struct ChunkState ( u8 ) ;
53
53
54
54
impl ChunkState {
55
+ /// Create a new ChunkState that represents being allocated in the given space
55
56
pub fn allocated ( space_index : usize ) -> ChunkState {
56
57
debug_assert ! ( space_index < crate :: util:: heap:: layout:: heap_parameters:: MAX_SPACES ) ;
57
58
let mut encode = space_index as u8 ;
58
59
encode |= 0x80 ;
59
60
ChunkState ( encode)
60
61
}
61
- pub fn free ( ) -> ChunkState {
62
- ChunkState ( 0 )
62
+ /// Create a new ChunkState that represents being free in the given space
63
+ pub fn free ( space_index : usize ) -> ChunkState {
64
+ debug_assert ! ( space_index < crate :: util:: heap:: layout:: heap_parameters:: MAX_SPACES ) ;
65
+ ChunkState ( space_index as u8 )
63
66
}
67
+ /// Is the chunk free?
64
68
pub fn is_free ( & self ) -> bool {
65
69
self . 0 & 0x80 == 0
66
70
}
71
+ /// Is the chunk allocated?
67
72
pub fn is_allocated ( & self ) -> bool {
68
73
!self . is_free ( )
69
74
}
75
+ /// Get the space index of the chunk
70
76
pub fn get_space_index ( & self ) -> usize {
71
77
debug_assert ! ( self . is_allocated( ) ) ;
72
78
let index = ( self . 0 & 0x0F ) as usize ;
@@ -78,17 +84,26 @@ impl ChunkState {
78
84
impl std:: fmt:: Debug for ChunkState {
79
85
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
80
86
if self . is_free ( ) {
81
- write ! ( f, "Free" )
87
+ write ! ( f, "Free({})" , self . get_space_index ( ) )
82
88
} else {
83
- write ! ( f, "Allocated in space {} " , self . get_space_index( ) )
89
+ write ! ( f, "Allocated({}) " , self . get_space_index( ) )
84
90
}
85
91
}
86
92
}
87
93
88
94
/// A byte-map to record all the allocated chunks.
89
95
/// A plan can use this to maintain records for the chunks that they used, and the states of the chunks.
90
- /// Any plan that uses the chunk map should include the `ALLOC_TABLE` spec in their local sidemetadata specs
96
+ /// Any plan that uses the chunk map should include the `ALLOC_TABLE` spec in their local sidemetadata specs.
97
+ ///
98
+ /// A chunk map is created for a space (identified by the space index), and will only update or list chunks for that space.
91
99
pub struct ChunkMap {
100
+ /// The space that uses this chunk map.
101
+ space_index : usize ,
102
+ /// The range of chunks that are used by the space. The range only records the lowest chunk and the highest chunk.
103
+ /// All the chunks that are used for the space are within the range, but not necessarily that all the chunks in the range
104
+ /// are used for the space. Spaces may be discontiguous, thus the range may include chunks that do not belong to the space.
105
+ /// We need to use the space index in the chunk map and the space index encoded with the chunk state to know if
106
+ /// the chunk belongs to the current space.
92
107
chunk_range : Mutex < Range < Chunk > > ,
93
108
}
94
109
@@ -97,24 +112,35 @@ impl ChunkMap {
97
112
pub const ALLOC_TABLE : SideMetadataSpec =
98
113
crate :: util:: metadata:: side_metadata:: spec_defs:: CHUNK_MARK ;
99
114
100
- pub fn new ( ) -> Self {
115
+ pub fn new ( space_index : usize ) -> Self {
101
116
Self {
117
+ space_index,
102
118
chunk_range : Mutex :: new ( Chunk :: ZERO ..Chunk :: ZERO ) ,
103
119
}
104
120
}
105
121
106
- /// Set chunk state
107
- pub fn set ( & self , chunk : Chunk , state : ChunkState ) {
122
+ /// Set a chunk as allocated, or as free.
123
+ pub fn set_allocated ( & self , chunk : Chunk , allocated : bool ) {
124
+ let state = if allocated {
125
+ ChunkState :: allocated ( self . space_index )
126
+ } else {
127
+ ChunkState :: free ( self . space_index )
128
+ } ;
108
129
// Do nothing if the chunk is already in the expected state.
109
- if self . get ( chunk) == state {
130
+ if self . get_any ( chunk) == state {
110
131
return ;
111
132
}
112
133
#[ cfg( debug_assertions) ]
113
134
{
114
- let old_state = self . get ( chunk) ;
115
- if state. is_allocated ( ) {
116
- assert ! ( old_state. is_free( ) || old_state. get_space_index( ) == state. get_space_index( ) , "Chunk {:?}: old state {:?}, new state {:?}. Cannot set to new state." , chunk, old_state, state) ;
117
- }
135
+ let old_state = self . get_any ( chunk) ;
136
+ // If a chunk is free, any space may use it. If a chunk is not free, only the current space may update its state.
137
+ assert ! (
138
+ old_state. is_free( ) || old_state. get_space_index( ) == state. get_space_index( ) ,
139
+ "Chunk {:?}: old state {:?}, new state {:?}. Cannot set to new state." ,
140
+ chunk,
141
+ old_state,
142
+ state
143
+ ) ;
118
144
}
119
145
// Update alloc byte
120
146
unsafe { Self :: ALLOC_TABLE . store :: < u8 > ( chunk. start ( ) , state. 0 ) } ;
@@ -134,16 +160,30 @@ impl ChunkMap {
134
160
}
135
161
}
136
162
137
- /// Get chunk state
138
- pub fn get ( & self , chunk : Chunk ) -> ChunkState {
163
+ /// Get chunk state. Return None if the chunk does not belong to the space.
164
+ pub fn get ( & self , chunk : Chunk ) -> Option < ChunkState > {
165
+ let state = self . get_any ( chunk) ;
166
+ ( state. get_space_index ( ) == self . space_index ) . then_some ( state)
167
+ }
168
+
169
+ /// Get chunk state, regardless of the space. This should always be private.
170
+ fn get_any ( & self , chunk : Chunk ) -> ChunkState {
139
171
let byte = unsafe { Self :: ALLOC_TABLE . load :: < u8 > ( chunk. start ( ) ) } ;
140
172
ChunkState ( byte)
141
173
}
142
174
143
175
/// A range of all chunks in the heap.
144
- pub fn all_chunks ( & self ) -> RegionIterator < Chunk > {
176
+ pub fn all_chunks ( & self ) -> impl Iterator < Item = Chunk > + use < ' _ > {
177
+ let chunk_range = self . chunk_range . lock ( ) ;
178
+ RegionIterator :: < Chunk > :: new ( chunk_range. start , chunk_range. end )
179
+ . filter ( |c| self . get ( * c) . is_some ( ) )
180
+ }
181
+
182
+ /// A range of all chunks in the heap.
183
+ pub fn all_allocated_chunks ( & self ) -> impl Iterator < Item = Chunk > + use < ' _ > {
145
184
let chunk_range = self . chunk_range . lock ( ) ;
146
185
RegionIterator :: < Chunk > :: new ( chunk_range. start , chunk_range. end )
186
+ . filter ( |c| self . get ( * c) . is_some_and ( |state| state. is_allocated ( ) ) )
147
187
}
148
188
149
189
/// Helper function to create per-chunk processing work packets for each allocated chunks.
@@ -152,18 +192,9 @@ impl ChunkMap {
152
192
func : impl Fn ( Chunk ) -> Box < dyn GCWork < VM > > ,
153
193
) -> Vec < Box < dyn GCWork < VM > > > {
154
194
let mut work_packets: Vec < Box < dyn GCWork < VM > > > = vec ! [ ] ;
155
- for chunk in self
156
- . all_chunks ( )
157
- . filter ( |c| self . get ( * c) . is_allocated ( ) )
158
- {
195
+ for chunk in self . all_allocated_chunks ( ) {
159
196
work_packets. push ( func ( chunk) ) ;
160
197
}
161
198
work_packets
162
199
}
163
200
}
164
-
165
- impl Default for ChunkMap {
166
- fn default ( ) -> Self {
167
- Self :: new ( )
168
- }
169
- }
0 commit comments