mmtk/util/alloc/
allocator.rs

1use crate::global_state::GlobalState;
2use crate::util::address::Address;
3#[cfg(feature = "analysis")]
4use crate::util::analysis::AnalysisManager;
5use crate::util::heap::gc_trigger::GCTrigger;
6use crate::util::options::Options;
7use crate::MMTK;
8
9use std::cell::RefCell;
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::sync::Arc;
12
13use crate::policy::space::Space;
14use crate::util::opaque_pointer::*;
15use crate::vm::VMBinding;
16use crate::vm::{ActivePlan, Collection};
17use downcast_rs::Downcast;
18
19#[repr(C)]
20#[derive(Debug)]
21/// A list of errors that MMTk can encounter during allocation.
22pub enum AllocationError {
23    /// The specified heap size is too small for the given program to continue.
24    HeapOutOfMemory,
25    /// The OS is unable to mmap or acquire more memory. Critical error. MMTk expects the VM to
26    /// abort if such an error is thrown.
27    MmapOutOfMemory,
28}
29
30/// Allow specifying different behaviors with [`Allocator::alloc_with_options`].
31#[repr(C)]
32#[derive(Copy, Clone, PartialEq, Eq, Debug)]
33pub struct AllocationOptions {
34    /// Whether over-committing is allowed at this allocation site.  Over-committing means the
35    /// allocation is allowed to go beyond the current heap size.  But it is not guaranteed to
36    /// succeed.
37    ///
38    /// **The default is `false`**.
39    ///
40    /// Note that regardless of the value of `allow_overcommit`, the allocation may trigger GC if
41    /// the GC trigger considers it needed.
42    pub allow_overcommit: bool,
43
44    /// Whether the allocation is at a safepoint.
45    ///
46    /// **The default is `true`**.
47    ///
48    /// If `true`, the allocation is allowed to block for GC.
49    ///
50    /// If `false`, the allocation will immediately return a null address if the allocation cannot
51    /// be satisfied without a GC.
52    pub at_safepoint: bool,
53
54    /// Whether the allocation is allowed to call [`Collection::out_of_memory`].
55    ///
56    /// **The default is `true`**.
57    ///
58    /// If `true`, the allocation will call [`Collection::out_of_memory`] when out of memory and
59    /// return null.
60    ///
61    /// If `fasle`, the allocation will return null immediately when out of memory.
62    pub allow_oom_call: bool,
63}
64
65/// The default value for `AllocationOptions` has the same semantics as calling [`Allocator::alloc`]
66/// directly.
67impl Default for AllocationOptions {
68    fn default() -> Self {
69        Self {
70            allow_overcommit: false,
71            at_safepoint: true,
72            allow_oom_call: true,
73        }
74    }
75}
76
77impl AllocationOptions {
78    pub(crate) fn is_default(&self) -> bool {
79        *self == AllocationOptions::default()
80    }
81}
82
83/// A wrapper for [`AllocatorContext`] to hold a [`AllocationOptions`] that can be modified by the
84/// same mutator thread.
85///
86/// All [`Allocator`] instances in `Allocators` share one `AllocationOptions` instance, and it will
87/// only be accessed by the mutator (via `Mutator::allocators`) or the GC worker (via
88/// `GCWorker::copy`) that owns it.  Rust doesn't like multiple mutable references pointing to a
89/// shared data structure.  We cannot use [`atomic::Atomic`] because `AllocationOptions` has
90/// multiple fields. We wrap it in a `RefCell` to make it internally mutable.
91///
92/// Note: The allocation option is called every time [`Allocator::alloc_with_options`] is called.
93/// Because API functions should only be called on allocation slow paths, we believe that `RefCell`
94/// should be good enough for performance.  If this is too slow, we may consider `UnsafeCell`.  If
95/// that's still too slow, we should consider changing the API to make the allocation options a
96/// persistent per-mutator value, and allow the VM binding set its value via a new API function.
97struct AllocationOptionsHolder {
98    alloc_options: RefCell<AllocationOptions>,
99}
100
101/// Strictly speaking, `AllocationOptionsHolder` isn't `Sync`.  Two threads cannot set or clear the
102/// same `AllocationOptionsHolder` at the same time.  However, both `Mutator` and `GCWorker` are
103/// `Send`, and both of which own `Allocators` and require its field `Arc<AllocationContext>` to be
104/// `Send`, which requires `AllocationContext` to be `Sync`, which requires
105/// `AllocationOptionsHolder` to be `Sync`.  (Note that `Arc<T>` can be cloned and given to another
106/// thread, and Rust expects `T` to be `Sync`, too.  But we never share `AllocationContext` between
107/// threads, but only between multiple `Allocator` instances within the same `Allocators` instance.
108/// Rust can't figure this out.)
109unsafe impl Sync for AllocationOptionsHolder {}
110
111impl AllocationOptionsHolder {
112    pub fn new(alloc_options: AllocationOptions) -> Self {
113        Self {
114            alloc_options: RefCell::new(alloc_options),
115        }
116    }
117    pub fn set_alloc_options(&self, options: AllocationOptions) {
118        let mut alloc_options = self.alloc_options.borrow_mut();
119        *alloc_options = options;
120    }
121
122    pub fn clear_alloc_options(&self) {
123        let mut alloc_options = self.alloc_options.borrow_mut();
124        *alloc_options = AllocationOptions::default();
125    }
126
127    pub fn get_alloc_options(&self) -> AllocationOptions {
128        let alloc_options = self.alloc_options.borrow();
129        *alloc_options
130    }
131}
132
133pub fn align_allocation_no_fill<VM: VMBinding>(
134    region: Address,
135    alignment: usize,
136    offset: usize,
137) -> Address {
138    align_allocation_inner::<VM>(region, alignment, offset, VM::MIN_ALIGNMENT, false)
139}
140
141pub fn align_allocation<VM: VMBinding>(
142    region: Address,
143    alignment: usize,
144    offset: usize,
145) -> Address {
146    align_allocation_inner::<VM>(region, alignment, offset, VM::MIN_ALIGNMENT, true)
147}
148
149pub fn align_allocation_inner<VM: VMBinding>(
150    region: Address,
151    alignment: usize,
152    offset: usize,
153    known_alignment: usize,
154    fillalignmentgap: bool,
155) -> Address {
156    debug_assert!(known_alignment >= VM::MIN_ALIGNMENT);
157    // Make sure MIN_ALIGNMENT is reasonable.
158    #[allow(clippy::assertions_on_constants)]
159    {
160        // TODO: This is a static assertion that VM::MIN_ALIGNMENT must be at least 4.
161        // This assertion has existed since JikesRVM MMTk.
162        // We are keeping it here because some implementation details of the allocator may rely on this assertion.
163        // Some GC algorithms may require a stricter minimum alignment, and that can override the value.
164        // We should refactor the VM binding API and the internal interface
165        // to reconcile the requirements from the VM and the GC algorithms.
166        debug_assert!(VM::MIN_ALIGNMENT >= std::mem::size_of::<i32>());
167    }
168    debug_assert!(!(fillalignmentgap && region.is_zero()));
169    debug_assert!(alignment <= VM::MAX_ALIGNMENT);
170    debug_assert!(region.is_aligned_to(VM::ALLOC_END_ALIGNMENT));
171    debug_assert!((alignment & (VM::MIN_ALIGNMENT - 1)) == 0);
172    debug_assert!((offset & (VM::MIN_ALIGNMENT - 1)) == 0);
173
174    // No alignment ever required.
175    if alignment <= known_alignment || VM::MAX_ALIGNMENT <= VM::MIN_ALIGNMENT {
176        return region;
177    }
178
179    // May require an alignment
180    let mask = (alignment - 1) as isize; // fromIntSignExtend
181    let neg_off: isize = -(offset as isize); // fromIntSignExtend
182    let delta = neg_off.wrapping_sub_unsigned(region.as_usize()) & mask; // Use wrapping_sub to avoid overflow
183
184    if fillalignmentgap && (VM::ALIGNMENT_VALUE != 0) {
185        fill_alignment_gap::<VM>(region, region + delta);
186    }
187
188    region + delta
189}
190
191/// Fill the specified region with the alignment value.
192pub fn fill_alignment_gap<VM: VMBinding>(start: Address, end: Address) {
193    if VM::ALIGNMENT_VALUE != 0 {
194        let start_ptr = start.to_mut_ptr::<u8>();
195        unsafe {
196            std::ptr::write_bytes(start_ptr, VM::ALIGNMENT_VALUE, end - start);
197        }
198    }
199}
200
201pub fn get_maximum_aligned_size<VM: VMBinding>(size: usize, alignment: usize) -> usize {
202    get_maximum_aligned_size_inner::<VM>(size, alignment, VM::MIN_ALIGNMENT)
203}
204
205pub fn get_maximum_aligned_size_inner<VM: VMBinding>(
206    size: usize,
207    alignment: usize,
208    known_alignment: usize,
209) -> usize {
210    trace!(
211        "size={}, alignment={}, known_alignment={}, MIN_ALIGNMENT={}",
212        size,
213        alignment,
214        known_alignment,
215        VM::MIN_ALIGNMENT
216    );
217    debug_assert!(size == size & !(known_alignment - 1));
218    debug_assert!(known_alignment >= VM::MIN_ALIGNMENT);
219
220    if VM::MAX_ALIGNMENT <= VM::MIN_ALIGNMENT || alignment <= known_alignment {
221        size
222    } else {
223        size + alignment - known_alignment
224    }
225}
226
227#[cfg(debug_assertions)]
228pub(crate) fn assert_allocation_args<VM: VMBinding>(size: usize, align: usize, offset: usize) {
229    use crate::util::constants::*;
230    // MMTk has assumptions about minimal object size.
231    // We need to make sure that all allocations comply with the min object size.
232    // Ideally, we check the allocation size, and if it is smaller, we transparently allocate the min
233    // object size (the VM does not need to know this). However, for the VM bindings we support at the moment,
234    // their object sizes are all larger than MMTk's min object size, so we simply put an assertion here.
235    // If you plan to use MMTk with a VM with its object size smaller than MMTk's min object size, you should
236    // meet the min object size in the fastpath.
237    debug_assert!(size >= MIN_OBJECT_SIZE);
238    // Assert alignment
239    debug_assert!(align >= VM::MIN_ALIGNMENT);
240    debug_assert!(align <= VM::MAX_ALIGNMENT);
241    // Assert offset
242    debug_assert!(VM::USE_ALLOCATION_OFFSET || offset == 0);
243}
244
245/// The context an allocator needs to access in order to perform allocation.
246///
247/// **Note:** An `AllocatorContext` is a thread-local struct, however, it is
248/// used as `Arc<AllocatorContext>` inside all allocator implementations since
249/// we need the entire struct to be `Send`.
250///
251/// See doc comment on `impl Sync` for `AllocationOptionsHolder` above.
252/// See here for more information: <https://github.com/mmtk/mmtk-core/issues/1474>
253pub struct AllocatorContext<VM: VMBinding> {
254    alloc_options: AllocationOptionsHolder,
255    pub state: Arc<GlobalState>,
256    /// Have we thrown an OOM already?
257    /// This value is only set and reset if [`Collection::out_of_memory`] returns.
258    pub thrown_oom: AtomicBool,
259    pub options: Arc<Options>,
260    pub gc_trigger: Arc<GCTrigger<VM>>,
261    #[cfg(feature = "analysis")]
262    pub analysis_manager: Arc<AnalysisManager<VM>>,
263}
264
265impl<VM: VMBinding> AllocatorContext<VM> {
266    pub fn new(mmtk: &MMTK<VM>) -> Self {
267        Self {
268            alloc_options: AllocationOptionsHolder::new(AllocationOptions::default()),
269            state: mmtk.state.clone(),
270            thrown_oom: AtomicBool::new(false),
271            options: mmtk.options.clone(),
272            gc_trigger: mmtk.gc_trigger.clone(),
273            #[cfg(feature = "analysis")]
274            analysis_manager: mmtk.analysis_manager.clone(),
275        }
276    }
277
278    pub fn set_alloc_options(&self, options: AllocationOptions) {
279        self.alloc_options.set_alloc_options(options);
280    }
281
282    pub fn clear_alloc_options(&self) {
283        self.alloc_options.clear_alloc_options();
284    }
285
286    pub fn get_alloc_options(&self) -> AllocationOptions {
287        self.alloc_options.get_alloc_options()
288    }
289}
290
291fn reset_oom_state<VM: VMBinding, A: Allocator<VM> + ?Sized>(allocator: &A) {
292    let context = allocator.get_context();
293    // Relaxed store is fine since this is a thread-local boolean.
294    context.thrown_oom.store(false, Ordering::Relaxed);
295}
296
297/// A trait which implements allocation routines. Every allocator needs to implements this trait.
298pub trait Allocator<VM: VMBinding>: Downcast {
299    /// Return the [`VMThread`] associated with this allocator instance.
300    fn get_tls(&self) -> VMThread;
301
302    /// Return the [`Space`](src/policy/space/Space) instance associated with this allocator instance.
303    fn get_space(&self) -> &'static dyn Space<VM>;
304
305    /// Return the context for the allocator.
306    fn get_context(&self) -> &AllocatorContext<VM>;
307
308    /// Return if this allocator can do thread local allocation. If an allocator does not do thread
309    /// local allocation, each allocation will go to slowpath and will have a check for GC polls.
310    fn does_thread_local_allocation(&self) -> bool;
311
312    /// Return at which granularity the allocator acquires memory from the global space and use
313    /// them as thread local buffer. For example, the [`BumpAllocator`](crate::util::alloc::BumpAllocator) acquires memory at 32KB
314    /// blocks. Depending on the actual size for the current object, they always acquire memory of
315    /// N*32KB (N>=1). Thus the [`BumpAllocator`](crate::util::alloc::BumpAllocator) returns 32KB for this method.  Only allocators
316    /// that do thread local allocation need to implement this method.
317    fn get_thread_local_buffer_granularity(&self) -> usize {
318        assert!(self.does_thread_local_allocation(), "An allocator that does not thread local allocation does not have a buffer granularity.");
319        unimplemented!()
320    }
321
322    /// Check if the requested `size` is an obvious out-of-memory case (requested allocation size is larger than the heap size).
323    /// If it is, call `Collection::out_of_memory`.  Return true if the allocation request is an obvious OOM case, and false otherwise.
324    fn handle_obvious_oom_request(&self, tls: VMThread, size: usize) -> bool {
325        if self.get_context().gc_trigger.will_oom_on_alloc(size) {
326            if self
327                .get_context()
328                .alloc_options
329                .get_alloc_options()
330                .allow_oom_call
331            {
332                self.out_of_memory(tls);
333            }
334            return true;
335        }
336        false
337    }
338
339    /// Wrapper around [`Collection::out_of_memory`]. Used to set up relevant state and signal out
340    /// of memory errors.
341    fn out_of_memory(&self, tls: VMThread) {
342        VM::VMCollection::out_of_memory(tls, AllocationError::HeapOutOfMemory);
343        // Relaxed store is fine since this is a thread-local boolean.
344        self.get_context().thrown_oom.store(true, Ordering::Relaxed);
345    }
346
347    /// An allocation attempt. The implementation of this function depends on the allocator used.
348    /// If an allocator supports thread local allocations, then the allocation will be serviced
349    /// from its TLAB, otherwise it will default to using the slowpath, i.e. [`alloc_slow`](Allocator::alloc_slow).
350    ///
351    /// If the heap is full, we trigger a GC and attempt to free up
352    /// more memory, and re-attempt the allocation.
353    ///
354    /// Note that in the case where the VM is out of memory, we invoke
355    /// [`Collection::out_of_memory`] to inform the binding and then return a null pointer back to
356    /// it. We have no assumptions on whether the VM will continue executing or abort immediately.
357    /// If the VM continues execution, the function will return a null address.
358    ///
359    /// An allocator needs to make sure the object reference for the returned address is in the same
360    /// chunk as the returned address (so the side metadata and the SFT for an object reference is valid).
361    /// See [`crate::util::alloc::object_ref_guard`](util/alloc/object_ref_guard).
362    ///
363    /// Arguments:
364    /// * `size`: the allocation size in bytes.
365    /// * `align`: the required alignment in bytes.
366    /// * `offset` the required offset in bytes.
367    fn alloc(&mut self, size: usize, align: usize, offset: usize) -> Address;
368
369    /// An allocation attempt. The allocation options may specify different behaviors for this allocation request.
370    ///
371    /// Arguments:
372    /// * `size`: the allocation size in bytes.
373    /// * `align`: the required alignment in bytes.
374    /// * `offset` the required offset in bytes.
375    /// * `options`: the allocation options to change the default allocation behavior for this request.
376    fn alloc_with_options(
377        &mut self,
378        size: usize,
379        align: usize,
380        offset: usize,
381        alloc_options: AllocationOptions,
382    ) -> Address {
383        self.get_context().set_alloc_options(alloc_options);
384        let ret = self.alloc(size, align, offset);
385        self.get_context().clear_alloc_options();
386        ret
387    }
388
389    /// Slowpath allocation attempt. This function is explicitly not inlined for performance
390    /// considerations.
391    ///
392    /// Arguments:
393    /// * `size`: the allocation size in bytes.
394    /// * `align`: the required alignment in bytes.
395    /// * `offset` the required offset in bytes.
396    #[inline(never)]
397    fn alloc_slow(&mut self, size: usize, align: usize, offset: usize) -> Address {
398        self.alloc_slow_inline(size, align, offset)
399    }
400
401    /// Slowpath allocation attempt. Mostly the same as [`Allocator::alloc_slow`], except that the allocation options
402    /// may specify different behaviors for this allocation request.
403    ///
404    /// This function is not used internally. It is mostly for the bindings.
405    /// [`Allocator::alloc_with_options`] still calls the normal [`Allocator::alloc_slow`].
406    ///
407    /// Arguments:
408    /// * `size`: the allocation size in bytes.
409    /// * `align`: the required alignment in bytes.
410    /// * `offset` the required offset in bytes.
411    fn alloc_slow_with_options(
412        &mut self,
413        size: usize,
414        align: usize,
415        offset: usize,
416        alloc_options: AllocationOptions,
417    ) -> Address {
418        // The function is not used internally. We won't set no_gc_on_fail redundantly.
419        self.get_context().set_alloc_options(alloc_options);
420        let ret = self.alloc_slow(size, align, offset);
421        self.get_context().clear_alloc_options();
422        ret
423    }
424
425    /// Slowpath allocation attempt. This function executes the actual slowpath allocation.  A
426    /// slowpath allocation in MMTk attempts to allocate the object using the per-allocator
427    /// definition of [`alloc_slow_once`](Allocator::alloc_slow_once). This function also accounts for increasing the
428    /// allocation bytes in order to support stress testing. In case precise stress testing is
429    /// being used, the [`alloc_slow_once_precise_stress`](Allocator::alloc_slow_once_precise_stress) function is used instead.
430    ///
431    /// Note that in the case where the VM is out of memory, we invoke
432    /// [`Collection::out_of_memory`] with a [`AllocationError::HeapOutOfMemory`] error to inform
433    /// the binding and then return a null pointer back to it. We have no assumptions on whether
434    /// the VM will continue executing or abort immediately on a
435    /// [`AllocationError::HeapOutOfMemory`] error.
436    ///
437    /// Arguments:
438    /// * `size`: the allocation size in bytes.
439    /// * `align`: the required alignment in bytes.
440    /// * `offset` the required offset in bytes.
441    fn alloc_slow_inline(&mut self, size: usize, align: usize, offset: usize) -> Address {
442        let tls = self.get_tls();
443        let is_mutator = VM::VMActivePlan::is_mutator(tls);
444        let stress_test = self.get_context().options.is_stress_test_gc_enabled();
445        assert!(!self.get_context().thrown_oom.load(Ordering::Relaxed), "We should not enter alloc_slow_inline if we have already thrown OOM for this allocation request.");
446
447        // Information about the previous collection.
448        let mut emergency_collection = false;
449        let mut previous_result_zero = false;
450
451        loop {
452            // Try to allocate using the slow path
453            let result = if is_mutator && stress_test && *self.get_context().options.precise_stress
454            {
455                // If we are doing precise stress GC, we invoke the special allow_slow_once call.
456                // alloc_slow_once_precise_stress() should make sure that every allocation goes
457                // to the slowpath (here) so we can check the allocation bytes and decide
458                // if we need to do a stress GC.
459
460                // If we should do a stress GC now, we tell the alloc_slow_once_precise_stress()
461                // so they would avoid try any thread local allocation, and directly call
462                // global acquire and do a poll.
463                let need_poll = is_mutator && self.get_context().gc_trigger.should_do_stress_gc();
464                self.alloc_slow_once_precise_stress(size, align, offset, need_poll)
465            } else {
466                // If we are not doing precise stress GC, just call the normal alloc_slow_once().
467                // Normal stress test only checks for stress GC in the slowpath.
468                self.alloc_slow_once_traced(size, align, offset)
469            };
470
471            if !is_mutator {
472                debug_assert!(!result.is_zero());
473                debug_assert!(!self.get_context().thrown_oom.load(Ordering::Relaxed));
474                return result;
475            }
476
477            if !result.is_zero() {
478                // Report allocation success to assist OutOfMemory handling.
479                if !self
480                    .get_context()
481                    .state
482                    .allocation_success
483                    .load(Ordering::Relaxed)
484                {
485                    self.get_context()
486                        .state
487                        .allocation_success
488                        .store(true, Ordering::SeqCst);
489                }
490                debug_assert!(!self.get_context().thrown_oom.load(Ordering::Relaxed));
491
492                // Only update the allocation bytes if we haven't failed a previous allocation in this loop
493                if stress_test && self.get_context().state.is_initialized() && !previous_result_zero
494                {
495                    let allocated_size = if *self.get_context().options.precise_stress
496                        || !self.does_thread_local_allocation()
497                    {
498                        // For precise stress test, or for allocators that do not have thread local buffer,
499                        // we know exactly how many bytes we allocate.
500                        size
501                    } else {
502                        // For normal stress test, we count the entire thread local buffer size as allocated.
503                        crate::util::conversions::raw_align_up(
504                            size,
505                            self.get_thread_local_buffer_granularity(),
506                        )
507                    };
508                    let _allocation_bytes = self
509                        .get_context()
510                        .state
511                        .increase_allocation_bytes_by(allocated_size);
512
513                    // This is the allocation hook for the analysis trait. If you want to call
514                    // an analysis counter specific allocation hook, then here is the place to do so
515                    #[cfg(feature = "analysis")]
516                    if _allocation_bytes > *self.get_context().options.analysis_factor {
517                        trace!(
518                            "Analysis: allocation_bytes = {} more than analysis_factor = {}",
519                            _allocation_bytes,
520                            *self.get_context().options.analysis_factor
521                        );
522
523                        self.get_context()
524                            .analysis_manager
525                            .alloc_hook(size, align, offset);
526                    }
527                }
528
529                return result;
530            }
531
532            // From here on, we handle the case that alloc_once failed.
533            assert!(result.is_zero());
534
535            if !self.get_context().get_alloc_options().at_safepoint {
536                // If the allocation is not at safepoint, it will not be able to block for GC.  But
537                // the code beyond this point tests OOM conditions and, if not OOM, try to allocate
538                // again.  Since we didn't block for GC, the allocation will fail again if we try
539                // again. So we return null immediately.
540                reset_oom_state(self);
541                return Address::ZERO;
542            }
543
544            // If we have already thrown an OOM for this allocation then return a zero.
545            // Relaxed load and store is fine given this is a thread-local boolean.
546            if self.get_context().thrown_oom.load(Ordering::Relaxed) {
547                // Need to reset the thrown_oom state since we're giving up on this allocation,
548                // that is to say, the thrown_oom state is *per* allocation request
549                reset_oom_state(self);
550                return Address::ZERO;
551            }
552
553            // It is possible to have cases where a thread is blocked for another GC (non emergency)
554            // immediately after being blocked for a GC (emergency) (e.g. in stress test), that is saying
555            // the thread does not leave this loop between the two GCs. The local var 'emergency_collection'
556            // was set to true after the first GC. But when we execute this check below, we just finished
557            // the second GC, which is not emergency. In such case, we will give a false OOM.
558            // We cannot just rely on the local var. Instead, we get the emergency collection value again,
559            // and check both.
560            if emergency_collection && self.get_context().state.is_emergency_collection() {
561                trace!("Emergency collection");
562                // Report allocation success to assist OutOfMemory handling.
563                // This seems odd, but we must allow each OOM to run its course (and maybe give us back memory)
564                let fail_with_oom = !self
565                    .get_context()
566                    .state
567                    .allocation_success
568                    .swap(true, Ordering::SeqCst);
569                trace!("fail with oom={}", fail_with_oom);
570                if fail_with_oom {
571                    // Note that we throw a `HeapOutOfMemory` error here and return a null ptr back to the VM
572                    trace!("Throw HeapOutOfMemory!");
573                    // Undo the swap above *before* the callback: `Collection::out_of_memory`
574                    // may not return (a binding may unwind out of it instead).
575                    self.get_context()
576                        .state
577                        .allocation_success
578                        .store(false, Ordering::SeqCst);
579                    // Tell the binding about the OOM. The binding may or may not return from this call.
580                    // TODO: This is subject to change in the future. See https://github.com/mmtk/mmtk-core/issues/1475.
581                    self.out_of_memory(tls);
582                    // `thrown_oom` is only set after `out_of_memory` returns, so this reset can
583                    // safely stay after the call.
584                    reset_oom_state(self);
585                    return result;
586                }
587            }
588
589            /* This is in case a GC occurs, and our mutator context is stale.
590             * In some VMs the scheduler can change the affinity between the
591             * current thread and the mutator context. This is possible for
592             * VMs that dynamically multiplex Java threads onto multiple mutator
593             * contexts. */
594            // FIXME: No good way to do this
595            //current = unsafe {
596            //    VMActivePlan::mutator(tls).get_allocator_from_space(space)
597            //};
598
599            // Record whether last collection was an Emergency collection. If so, we make one more
600            // attempt to allocate before we signal an OOM.
601            emergency_collection = self.get_context().state.is_emergency_collection();
602            trace!("Got emergency collection as {}", emergency_collection);
603            previous_result_zero = true;
604        }
605    }
606
607    /// Single slow path allocation attempt. This is called by [`alloc_slow_inline`](Allocator::alloc_slow_inline). The
608    /// implementation of this function depends on the allocator used. Generally, if an allocator
609    /// supports thread local allocations, it will try to allocate more TLAB space here. If it
610    /// doesn't, then (generally) the allocator simply allocates enough space for the current
611    /// object.
612    ///
613    /// Arguments:
614    /// * `size`: the allocation size in bytes.
615    /// * `align`: the required alignment in bytes.
616    /// * `offset` the required offset in bytes.
617    fn alloc_slow_once(&mut self, size: usize, align: usize, offset: usize) -> Address;
618
619    /// A wrapper method for [`alloc_slow_once`](Allocator::alloc_slow_once) to insert USDT tracepoints.
620    ///
621    /// Arguments:
622    /// * `size`: the allocation size in bytes.
623    /// * `align`: the required alignment in bytes.
624    /// * `offset` the required offset in bytes.
625    fn alloc_slow_once_traced(&mut self, size: usize, align: usize, offset: usize) -> Address {
626        probe!(mmtk, alloc_slow_once_start);
627        // probe! expands to an empty block on unsupported platforms
628        #[allow(clippy::let_and_return)]
629        let ret = self.alloc_slow_once(size, align, offset);
630        probe!(mmtk, alloc_slow_once_end);
631        ret
632    }
633
634    /// Single slowpath allocation attempt for stress test. When the stress factor is set (e.g. to
635    /// N), we would expect for every N bytes allocated, we will trigger a stress GC.  However, for
636    /// allocators that do thread local allocation, they may allocate from their thread local
637    /// buffer which does not have a GC poll check, and they may even allocate with the JIT
638    /// generated allocation fastpath which is unaware of stress test GC. For both cases, we are
639    /// not able to guarantee a stress GC is triggered every N bytes. To solve this, when the
640    /// stress factor is set, we will call this method instead of the normal alloc_slow_once(). We
641    /// expect the implementation of this slow allocation will trick the fastpath so every
642    /// allocation will fail in the fastpath, jump to the slow path and eventually call this method
643    /// again for the actual allocation.
644    ///
645    /// The actual implementation about how to trick the fastpath may vary. For example, our bump
646    /// pointer allocator will set the thread local buffer limit to the buffer size instead of the
647    /// buffer end address. In this case, every fastpath check (cursor + size < limit) will fail,
648    /// and jump to this slowpath. In the slowpath, we still allocate from the thread local buffer,
649    /// and recompute the limit (remaining buffer size).
650    ///
651    /// If an allocator does not do thread local allocation (which returns false for
652    /// does_thread_local_allocation()), it does not need to override this method. The default
653    /// implementation will simply call allow_slow_once() and it will work fine for allocators that
654    /// do not have thread local allocation.
655    ///
656    /// Arguments:
657    /// * `size`: the allocation size in bytes.
658    /// * `align`: the required alignment in bytes.
659    /// * `offset` the required offset in bytes.
660    /// * `need_poll`: if this is true, the implementation must poll for a GC, rather than
661    ///   attempting to allocate from the local buffer.
662    fn alloc_slow_once_precise_stress(
663        &mut self,
664        size: usize,
665        align: usize,
666        offset: usize,
667        need_poll: bool,
668    ) -> Address {
669        // If an allocator does thread local allocation but does not override this method to
670        // provide a correct implementation, we will log a warning.
671        if self.does_thread_local_allocation() && need_poll {
672            warn!("{} does not support stress GC (An allocator that does thread local allocation needs to implement allow_slow_once_stress_test()).", std::any::type_name::<Self>());
673        }
674        self.alloc_slow_once_traced(size, align, offset)
675    }
676
677    /// The [`crate::plan::Mutator`] that includes this allocator is going to be destroyed. Some allocators
678    /// may need to save/transfer its thread local data to the space.
679    fn on_mutator_destroy(&mut self) {
680        // By default, do nothing
681    }
682}
683
684impl_downcast!(Allocator<VM> where VM: VMBinding);