@@ -44,20 +44,68 @@ impl Chunk {
4444 }
4545}
4646
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+ }
5594}
5695
5796/// A byte-map to record all the allocated chunks.
5897/// 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.
60101pub 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.
61109 chunk_range : Mutex < Range < Chunk > > ,
62110}
63111
@@ -66,22 +114,40 @@ impl ChunkMap {
66114 pub const ALLOC_TABLE : SideMetadataSpec =
67115 crate :: util:: metadata:: side_metadata:: spec_defs:: CHUNK_MARK ;
68116
69- pub fn new ( ) -> Self {
117+ pub fn new ( space_index : usize ) -> Self {
70118 Self {
119+ space_index,
71120 chunk_range : Mutex :: new ( Chunk :: ZERO ..Chunk :: ZERO ) ,
72121 }
73122 }
74123
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+ } ;
77131 // Do nothing if the chunk is already in the expected state.
78- if self . get ( chunk) == state {
132+ if self . get_internal ( chunk) == state {
79133 return ;
80134 }
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+ }
81147 // 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 ) } ;
83149 // If this is a newly allcoated chunk, then expand the chunk range.
84- if state == ChunkState :: Allocated {
150+ if allocated {
85151 debug_assert ! ( !chunk. start( ) . is_zero( ) ) ;
86152 let mut range = self . chunk_range . lock ( ) ;
87153 if range. start == Chunk :: ZERO {
@@ -96,20 +162,23 @@ impl ChunkMap {
96162 }
97163 }
98164
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 {
101173 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)
107175 }
108176
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 > + ' _ {
111179 let chunk_range = self . chunk_range . lock ( ) ;
112180 RegionIterator :: < Chunk > :: new ( chunk_range. start , chunk_range. end )
181+ . filter ( |c| self . get ( * c) . is_some ( ) )
113182 }
114183
115184 /// Helper function to create per-chunk processing work packets for each allocated chunks.
@@ -118,18 +187,9 @@ impl ChunkMap {
118187 func : impl Fn ( Chunk ) -> Box < dyn GCWork < VM > > ,
119188 ) -> Vec < Box < dyn GCWork < VM > > > {
120189 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 ( ) {
125191 work_packets. push ( func ( chunk) ) ;
126192 }
127193 work_packets
128194 }
129195}
130-
131- impl Default for ChunkMap {
132- fn default ( ) -> Self {
133- Self :: new ( )
134- }
135- }
0 commit comments