1use std::sync::Arc;
4
5use super::allocator::AllocatorContext;
6use crate::policy::marksweepspace::native_ms::*;
7use crate::util::alloc::allocator;
8use crate::util::alloc::Allocator;
9use crate::util::linear_scan::Region;
10use crate::util::Address;
11use crate::util::VMThread;
12use crate::vm::VMBinding;
13
14#[repr(C)]
16pub struct FreeListAllocator<VM: VMBinding> {
17 pub tls: VMThread,
19 space: &'static MarkSweepSpace<VM>,
20 context: Arc<AllocatorContext<VM>>,
21 pub available_blocks: BlockLists,
23 pub available_blocks_stress: BlockLists,
29 pub unswept_blocks: BlockLists,
31 pub consumed_blocks: BlockLists,
33}
34
35impl<VM: VMBinding> Allocator<VM> for FreeListAllocator<VM> {
36 fn get_tls(&self) -> VMThread {
37 self.tls
38 }
39
40 fn get_space(&self) -> &'static dyn crate::policy::space::Space<VM> {
41 self.space
42 }
43
44 fn get_context(&self) -> &AllocatorContext<VM> {
45 &self.context
46 }
47
48 fn alloc(&mut self, size: usize, align: usize, offset: usize) -> Address {
50 debug_assert!(
51 size <= MAX_BIN_SIZE,
52 "Alloc request for {} bytes is too big.",
53 size
54 );
55 debug_assert!(align <= VM::MAX_ALIGNMENT);
56 debug_assert!(align >= VM::MIN_ALIGNMENT);
57
58 if let Some(block) = self.find_free_block_local(size, align) {
59 let cell = self.block_alloc(block);
60 if !cell.is_zero() {
61 debug_assert!(
63 !(*self.context.options.precise_stress
64 && self.context.options.is_stress_test_gc_enabled())
65 );
66
67 let res = allocator::align_allocation::<VM>(cell, align, offset);
68 #[cfg(debug_assertions)]
70 {
71 let cell_size = block.load_block_cell_size();
72 debug_assert!(
73 res + size <= cell + cell_size,
74 "Allocating (size = {}, align = {}, offset = {}) to the cell {} of size {}, but the end of the allocation region {} is beyond the cell end {}",
75 size, align, offset, cell, cell_size, res + size, cell + cell_size
76 );
77 }
78 return res;
79 }
80 }
81
82 self.alloc_slow(size, align, offset)
83 }
84
85 fn alloc_slow_once(&mut self, size: usize, align: usize, offset: usize) -> Address {
86 if let Some(block) = self.acquire_global_block(size, align, false) {
88 let addr = self.block_alloc(block);
89 allocator::align_allocation::<VM>(addr, align, offset)
90 } else {
91 Address::ZERO
92 }
93 }
94
95 fn does_thread_local_allocation(&self) -> bool {
96 true
97 }
98
99 fn get_thread_local_buffer_granularity(&self) -> usize {
100 Block::BYTES
101 }
102
103 fn alloc_slow_once_precise_stress(
104 &mut self,
105 size: usize,
106 align: usize,
107 offset: usize,
108 need_poll: bool,
109 ) -> Address {
110 trace!("allow slow precise stress s={}", size);
111 if need_poll {
112 self.acquire_global_block(0, 0, true);
113 }
114
115 if let Some(block) = self.find_free_block_stress(size, align) {
117 let cell = self.block_alloc(block);
118 allocator::align_allocation::<VM>(cell, align, offset)
119 } else {
120 Address::ZERO
121 }
122 }
123
124 fn on_mutator_destroy(&mut self) {
125 let mut global = self.space.get_abandoned_block_lists().lock().unwrap();
126 self.abandon_blocks(&mut global);
127 }
128}
129
130impl<VM: VMBinding> FreeListAllocator<VM> {
131 pub(crate) fn new(
133 tls: VMThread,
134 space: &'static MarkSweepSpace<VM>,
135 context: Arc<AllocatorContext<VM>>,
136 ) -> Self {
137 FreeListAllocator {
138 tls,
139 space,
140 context,
141 available_blocks: new_empty_block_lists(),
142 available_blocks_stress: new_empty_block_lists(),
143 unswept_blocks: new_empty_block_lists(),
144 consumed_blocks: new_empty_block_lists(),
145 }
146 }
147
148 fn block_alloc(&mut self, block: Block) -> Address {
150 let cell = block.load_free_list();
151 if cell.is_zero() {
152 return cell; }
154 let next_cell = unsafe { cell.load::<Address>() };
155 unsafe { cell.store::<Address>(Address::ZERO) };
157 debug_assert!(
158 next_cell.is_zero() || block.includes_address(next_cell),
159 "next_cell {} is not in {:?}",
160 next_cell,
161 block
162 );
163 block.store_free_list(next_cell);
164
165 let cell_size = block.load_block_cell_size();
168 crate::util::memory::zero(cell, cell_size);
169
170 #[cfg(debug_assertions)]
173 {
174 let mut cursor = cell;
175 while cursor < cell + cell_size {
176 debug_assert_eq!(unsafe { cursor.load::<usize>() }, 0);
177 cursor += crate::util::constants::BYTES_IN_ADDRESS;
178 }
179 }
180
181 cell
182 }
183
184 fn find_free_block_stress(&mut self, size: usize, align: usize) -> Option<Block> {
186 Self::find_free_block_with(
187 &mut self.available_blocks_stress,
188 &mut self.consumed_blocks,
189 size,
190 align,
191 )
192 .or_else(|| self.recycle_local_blocks(size, align, true))
193 .or_else(|| self.acquire_global_block(size, align, true))
194 }
195
196 fn find_free_block_local(&mut self, size: usize, align: usize) -> Option<Block> {
198 Self::find_free_block_with(
199 &mut self.available_blocks,
200 &mut self.consumed_blocks,
201 size,
202 align,
203 )
204 .or_else(|| self.recycle_local_blocks(size, align, false))
205 }
206
207 fn find_free_block_with(
212 available_blocks: &mut BlockLists,
213 consumed_blocks: &mut BlockLists,
214 size: usize,
215 align: usize,
216 ) -> Option<Block> {
217 let bin = mi_bin::<VM>(size, align);
218 debug_assert!(bin <= MAX_BIN);
219
220 let available = &mut available_blocks[bin];
221 debug_assert!(available.size >= size);
222
223 if !available.is_empty() {
224 let mut cursor = available.first;
225
226 while let Some(block) = cursor {
227 if block.has_free_cells() {
228 return Some(block);
229 }
230 available.pop();
231 consumed_blocks.get_mut(bin).unwrap().push(block);
232
233 cursor = available.first;
234 }
235 }
236
237 debug_assert!(available_blocks[bin].is_empty());
238 None
239 }
240
241 fn add_to_available_blocks(&mut self, bin: usize, block: Block, stress: bool) {
244 if stress {
245 debug_assert!(*self.context.options.precise_stress);
246 self.available_blocks_stress[bin].push(block);
247 } else {
248 self.available_blocks[bin].push(block);
249 }
250 }
251
252 fn recycle_local_blocks(
254 &mut self,
255 size: usize,
256 align: usize,
257 _stress_test: bool,
258 ) -> Option<Block> {
259 if cfg!(feature = "eager_sweeping") {
260 None
262 } else {
263 loop {
265 let bin = mi_bin::<VM>(size, align);
266 debug_assert!(self.available_blocks[bin].is_empty()); let block = self.unswept_blocks.get_mut(bin).unwrap().pop()?;
269
270 block.sweep::<VM>();
271 if block.has_free_cells() {
272 self.add_to_available_blocks(
274 bin,
275 block,
276 self.context.options.is_stress_test_gc_enabled(),
277 );
278 return Some(block);
279 } else {
280 self.consumed_blocks.get_mut(bin).unwrap().push(block);
282 }
283 }
284 }
285 }
286
287 fn acquire_global_block(
289 &mut self,
290 size: usize,
291 align: usize,
292 stress_test: bool,
293 ) -> Option<Block> {
294 let bin = mi_bin::<VM>(size, align);
295 loop {
296 match self.space.acquire_block(self.tls, size, align, self.get_context().get_alloc_options()) {
297 crate::policy::marksweepspace::native_ms::BlockAcquireResult::Exhausted => {
298 debug!("Acquire global block: None");
299 return None;
301 }
302
303 crate::policy::marksweepspace::native_ms::BlockAcquireResult::Fresh(block) => {
304 debug!("Acquire global block: Fresh {:?}", block);
305 self.add_to_available_blocks(bin, block, stress_test);
306 self.init_block(block, self.available_blocks[bin].size);
307
308 return Some(block);
309 }
310
311 crate::policy::marksweepspace::native_ms::BlockAcquireResult::AbandonedAvailable(block) => {
312 debug!("Acquire global block: AbandonedAvailable {:?}", block);
313 block.store_tls(self.tls);
314 if block.has_free_cells() {
315 self.add_to_available_blocks(bin, block, stress_test);
316 return Some(block);
317 } else {
318 self.consumed_blocks[bin].push(block);
319 }
320 }
321
322 crate::policy::marksweepspace::native_ms::BlockAcquireResult::AbandonedUnswept(block) => {
323 debug!("Acquire global block: AbandonedUnswep {:?}", block);
324 block.store_tls(self.tls);
325 block.sweep::<VM>();
326 if block.has_free_cells() {
327 self.add_to_available_blocks(bin, block, stress_test);
328 return Some(block);
329 } else {
330 self.consumed_blocks[bin].push(block);
331 }
332 }
333 }
334 }
335 }
336
337 fn init_block(&self, block: Block, cell_size: usize) {
338 debug_assert_ne!(cell_size, 0);
339 self.space.record_new_block(block);
340
341 let block_end = block.start() + Block::BYTES;
343 let mut old_cell = unsafe { Address::zero() };
344 let mut new_cell = block.start();
345
346 let final_cell = loop {
347 unsafe {
348 new_cell.store::<Address>(old_cell);
349 }
350 old_cell = new_cell;
351 new_cell += cell_size;
352 if new_cell + cell_size > block_end {
353 break old_cell;
354 };
355 };
356
357 block.store_free_list(final_cell);
358 block.store_block_cell_size(cell_size);
359 #[cfg(feature = "malloc_native_mimalloc")]
360 {
361 block.store_local_free_list(Address::ZERO);
362 block.store_thread_free_list(Address::ZERO);
363 }
364
365 self.store_block_tls(block);
366 }
367
368 #[cfg(feature = "malloc_native_mimalloc")]
369 fn free(&self, addr: Address) {
370 assert!(!addr.is_zero(), "Attempted to free zero address.");
371
372 use crate::util::ObjectReference;
373 let block = Block::from_unaligned_address(addr);
374 let block_tls = block.load_tls();
375
376 if self.tls == block_tls {
377 let local_free = block.load_local_free_list();
379 unsafe {
380 addr.store(local_free);
381 }
382 block.store_local_free_list(addr);
383 } else {
384 unreachable!(
386 "tlss don't match freeing from block {}, my tls = {:?}, block tls = {:?}",
387 block.start(),
388 self.tls,
389 block.load_tls()
390 );
391
392 }
402
403 crate::util::metadata::vo_bit::unset_vo_bit(unsafe {
407 ObjectReference::from_raw_address_unchecked(addr)
408 })
409 }
410
411 fn store_block_tls(&self, block: Block) {
412 block.store_tls(self.tls);
413 }
414
415 pub(crate) fn prepare(&mut self) {}
416
417 pub(crate) fn release(&mut self) {
418 for bin in 0..MI_BIN_FULL {
419 let unswept = self.unswept_blocks.get_mut(bin).unwrap();
420
421 #[cfg(feature = "eager_sweeping")]
423 debug_assert!(unswept.is_empty());
424
425 let mut sweep_later = |list: &mut BlockList| {
426 list.release_blocks(self.space);
427
428 if cfg!(not(feature = "eager_sweeping")) {
434 unswept.append(list);
435 }
436 };
437
438 sweep_later(&mut self.available_blocks[bin]);
439 sweep_later(&mut self.available_blocks_stress[bin]);
440 sweep_later(&mut self.consumed_blocks[bin]);
441 }
442
443 {
446 let mut global = self.space.get_abandoned_block_lists_in_gc().lock().unwrap();
447 self.abandon_blocks(&mut global);
448 }
449
450 self.space.release_packet_done();
451 }
452
453 fn abandon_blocks(&mut self, global: &mut AbandonedBlockLists) {
454 for i in 0..MI_BIN_FULL {
455 let available = self.available_blocks.get_mut(i).unwrap();
456 if !available.is_empty() {
457 global.available[i].append(available);
458 }
459
460 let available_stress = self.available_blocks_stress.get_mut(i).unwrap();
461 if !available_stress.is_empty() {
462 global.available[i].append(available_stress);
463 }
464
465 let consumed = self.consumed_blocks.get_mut(i).unwrap();
466 if !consumed.is_empty() {
467 global.consumed[i].append(consumed);
468 }
469
470 let unswept = self.unswept_blocks.get_mut(i).unwrap();
471 if !unswept.is_empty() {
472 global.unswept[i].append(unswept);
473 }
474 }
475 }
476}