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