@@ -3,9 +3,15 @@ use std::collections::HashMap;
33use std:: hash:: { BuildHasherDefault , DefaultHasher } ;
44use std:: sync;
55
6+ use rustc_index:: bit_set:: DenseBitSet ;
7+
68static ALLOCATOR : sync:: Mutex < IsolatedAlloc > = sync:: Mutex :: new ( IsolatedAlloc :: empty ( ) ) ;
79
810pub struct IsolatedAlloc {
11+ /// A map of machine ID to allocator. If running in multi-seeded mode,
12+ /// each machine should have its own pool of memory that can be accessed
13+ /// separately. We use the normal `HashMap` type so that it's available
14+ /// in a `const` context.
915 #[ allow( rustc:: default_hash_types) ]
1016 allocators : HashMap < u64 , IsolatedAllocInner , BuildHasherDefault < DefaultHasher > > ,
1117 /// The host (not emulated) page size, or 0 if it has not yet been set.
@@ -24,14 +30,14 @@ pub struct IsolatedAllocInner {
2430 /// with their size stored as the second element of the vector.
2531 huge_ptrs : Vec < ( * mut u8 , usize ) > ,
2632 /// Metadata about which bytes have been allocated on each page. The length
27- /// of this vector must be the same as that of `page_ptrs`, and the length of
28- /// the boxed slice must be exactly `page_size / 8`.
33+ /// of this vector must be the same as that of `page_ptrs`, and the domain
34+ /// size of the bitset must be exactly `page_size / 8`.
2935 ///
30- /// Conceptually, each bit of the `u8` represents the allocation status of one
31- /// byte on the corresponding element of `page_ptrs`; in practice, we only allocate
32- /// in 8-byte chunks currently, so the `u8`s are only ever 0 (fully free) or
33- /// 255 (fully allocated) .
34- page_infos : Vec < Box < [ u8 ] > > ,
36+ /// Conceptually, each bit of the bitset represents the allocation status of
37+ /// one 8- byte chunk on the corresponding element of `page_ptrs`. Thus,
38+ /// indexing into it should be done with a value one-eighth of the corresponding
39+ /// offset on the matching `page_ptrs` element .
40+ page_infos : Vec < DenseBitSet < usize > > ,
3541}
3642
3743// SAFETY: We only point to heap-allocated data
@@ -42,6 +48,7 @@ impl IsolatedAlloc {
4248 /// allow this function to be `const`; it is updated to its real value on
4349 /// the first call to `alloc()` or `alloc_zeroed()`.
4450 const fn empty ( ) -> Self {
51+ // We need this to be `const`
4552 #[ allow( rustc:: default_hash_types) ]
4653 Self { allocators : HashMap :: with_hasher ( BuildHasherDefault :: new ( ) ) , page_size : 0 }
4754 }
@@ -108,14 +115,15 @@ impl IsolatedAllocInner {
108115 }
109116
110117 /// Expands the available memory pool by adding one page.
111- fn add_page ( & mut self , page_size : usize ) -> ( * mut u8 , & mut Box < [ u8 ] > ) {
118+ fn add_page ( & mut self , page_size : usize ) -> ( * mut u8 , & mut DenseBitSet < usize > ) {
112119 assert_ne ! ( page_size, 0 ) ;
113120
114121 let page_layout = unsafe { Layout :: from_size_align_unchecked ( page_size, page_size) } ;
115122 // We don't overwrite the bytes we hand out so make sure they're zeroed by default!
116123 let page_ptr = unsafe { alloc:: alloc ( page_layout) } ;
117- // `page_infos` has to be one-eighth of the pagesize per the field docs
118- self . page_infos . push ( vec ! [ 0u8 ; page_size / 8 ] . into_boxed_slice ( ) ) ;
124+ // `page_infos` has to have one-eighth as many bits as a page has bytes
125+ // (or one-64th as many bytes)
126+ self . page_infos . push ( DenseBitSet :: new_empty ( page_size / 8 ) ) ;
119127 self . page_ptrs . push ( page_ptr) ;
120128 ( page_ptr, self . page_infos . last_mut ( ) . unwrap ( ) )
121129 }
@@ -160,23 +168,28 @@ impl IsolatedAllocInner {
160168 page_size : usize ,
161169 layout : Layout ,
162170 page : * mut u8 ,
163- pinfo : & mut Box < [ u8 ] > ,
171+ pinfo : & mut DenseBitSet < usize > ,
164172 zeroed : bool ,
165173 ) -> Option < * mut u8 > {
166174 let ( size, align) = IsolatedAllocInner :: normalized_layout ( layout) ;
167175
176+ // Check every alignment-sized block and see if there exists a `size`
177+ // chunk of empty space i.e. forall idx . !pinfo.contains(idx / 8)
168178 for idx in ( 0 ..page_size) . step_by ( align) {
169179 let idx_pinfo = idx / 8 ;
170180 let size_pinfo = size / 8 ;
171- if pinfo. len ( ) < idx_pinfo + size_pinfo {
181+ // DenseBitSet::contains() panics if the index is out of bounds
182+ if pinfo. domain_size ( ) < idx_pinfo + size_pinfo {
172183 break ;
173184 }
174- if pinfo[ idx_pinfo..idx_pinfo + size_pinfo] . iter ( ) . all ( |v| * v == 0 ) {
175- pinfo[ idx_pinfo..idx_pinfo + size_pinfo] . fill ( 255 ) ;
185+ let pred = !( idx_pinfo..idx_pinfo + size_pinfo) . any ( |idx| pinfo. contains ( idx) ) ;
186+ if pred {
187+ pinfo. insert_range ( idx_pinfo..idx_pinfo + size_pinfo) ;
176188 unsafe {
177189 let ptr = page. add ( idx) ;
178190 if zeroed {
179- // Only write the bytes we were specifically asked to zero out
191+ // Only write the bytes we were specifically asked to
192+ // zero out, even if we allocated more
180193 ptr. write_bytes ( 0 , layout. size ( ) ) ;
181194 }
182195 return Some ( ptr) ;
@@ -223,14 +236,15 @@ impl IsolatedAllocInner {
223236 } ;
224237 let ptr_idx_pinfo = ptr_idx / 8 ;
225238 let size_pinfo = size / 8 ;
226- // Everything is always aligned to at least 8 bytes so this is ok
227- pinfo[ ptr_idx_pinfo..ptr_idx_pinfo + size_pinfo] . fill ( 0 ) ;
239+ for idx in ptr_idx_pinfo..ptr_idx_pinfo + size_pinfo {
240+ pinfo. remove ( idx) ;
241+ }
228242 }
229243
230244 let mut free = vec ! [ ] ;
231245 let page_layout = unsafe { Layout :: from_size_align_unchecked ( page_size, page_size) } ;
232246 for ( idx, pinfo) in self . page_infos . iter ( ) . enumerate ( ) {
233- if pinfo. iter ( ) . all ( |p| * p == 0 ) {
247+ if pinfo. is_empty ( ) {
234248 free. push ( idx) ;
235249 }
236250 }
0 commit comments