Skip to main content

core/alloc/
global.rs

1use super::{AllocError, GlobalAllocator};
2use crate::alloc::Layout;
3use crate::hint::assert_unchecked;
4use crate::ptr::NonNull;
5use crate::{cmp, ptr};
6
7/// A memory allocator that can be registered as the standard library’s default
8/// through the `#[global_allocator]` attribute.
9///
10/// Some of the methods require that a memory block be *currently
11/// allocated* via an allocator. This means that:
12///
13/// * the starting address for that memory block was previously
14///   returned by a previous call to an allocation method
15///   such as `alloc`, and
16///
17/// * the memory block has not been subsequently deallocated, where
18///   blocks are deallocated either by being passed to a deallocation
19///   method such as `dealloc` or by being
20///   passed to a reallocation method that returns a non-null pointer.
21///
22///
23/// # Example
24///
25/// ```standalone_crate
26/// use std::alloc::{GlobalAlloc, Layout};
27/// use std::cell::UnsafeCell;
28/// use std::ptr::null_mut;
29/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
30///
31/// const ARENA_SIZE: usize = 128 * 1024;
32/// const MAX_SUPPORTED_ALIGN: usize = 4096;
33/// #[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN
34/// struct SimpleAllocator {
35///     arena: UnsafeCell<[u8; ARENA_SIZE]>,
36///     remaining: AtomicUsize, // we allocate from the top, counting down
37/// }
38///
39/// #[global_allocator]
40/// static ALLOCATOR: SimpleAllocator = SimpleAllocator {
41///     arena: UnsafeCell::new([0x55; ARENA_SIZE]),
42///     remaining: AtomicUsize::new(ARENA_SIZE),
43/// };
44///
45/// unsafe impl Sync for SimpleAllocator {}
46///
47/// unsafe impl GlobalAlloc for SimpleAllocator {
48///     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
49///         let size = layout.size();
50///         let align = layout.align();
51///
52///         // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2.
53///         // So we can safely use a mask to ensure alignment without worrying about UB.
54///         let align_mask_to_round_down = !(align - 1);
55///
56///         if align > MAX_SUPPORTED_ALIGN {
57///             return null_mut();
58///         }
59///
60///         let mut allocated = 0;
61///         if self
62///             .remaining
63///             .try_update(Relaxed, Relaxed, |mut remaining| {
64///                 if size > remaining {
65///                     return None;
66///                 }
67///                 remaining -= size;
68///                 remaining &= align_mask_to_round_down;
69///                 allocated = remaining;
70///                 Some(remaining)
71///             })
72///             .is_err()
73///         {
74///             return null_mut();
75///         };
76///         unsafe { self.arena.get().cast::<u8>().add(allocated) }
77///     }
78///     unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
79/// }
80///
81/// fn main() {
82///     let _s = format!("allocating a string!");
83///     let currently = ALLOCATOR.remaining.load(Relaxed);
84///     println!("allocated so far: {}", ARENA_SIZE - currently);
85/// }
86/// ```
87///
88/// # Safety
89///
90/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and
91/// implementors must ensure that they adhere to these contracts:
92///
93/// * It's undefined behavior if global allocators unwind. This restriction may
94///   be lifted in the future, but currently a panic from any of these
95///   functions may lead to memory unsafety.
96///
97/// * `Layout` queries and calculations in general must be correct. Callers of
98///   this trait are allowed to rely on the contracts defined on each method,
99///   and implementors must ensure such contracts remain true.
100///
101/// * You must not rely on allocations actually happening, even if there are explicit
102///   heap allocations in the source. The optimizer may detect unused allocations that it can either
103///   eliminate entirely or move to the stack and thus never invoke the allocator. The
104///   optimizer may further assume that allocation is infallible, so code that used to fail due
105///   to allocator failures may now suddenly work because the optimizer worked around the
106///   need for an allocation. More concretely, the following code example is unsound, irrespective
107///   of whether your custom allocator allows counting how many allocations have happened.
108///
109///   ```rust,ignore (unsound and has placeholders)
110///   drop(Box::new(42));
111///   let number_of_heap_allocs = /* call private allocator API */;
112///   unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); }
113///   ```
114///
115///   Note that the optimizations mentioned above are not the only
116///   optimization that can be applied. You may generally not rely on heap allocations
117///   happening if they can be removed without changing program behavior.
118///   Whether allocations happen or not is not part of the program behavior, even if it
119///   could be detected via an allocator that tracks allocations by printing or otherwise
120///   having side effects.
121///
122/// # Re-entrance
123///
124/// When implementing a global allocator, one has to be careful not to create an infinitely recursive
125/// implementation by accident, as many constructs in the Rust standard library may allocate in
126/// their implementation. For example, on some platforms, [`std::sync::Mutex`] may allocate, so using
127/// it is highly problematic in a global allocator.
128///
129/// For this reason, one should generally stick to library features available through
130/// [`core`], and avoid using [`std`] in a global allocator. A few features from [`std`] are
131/// guaranteed to not use `#[global_allocator]` to allocate:
132///
133///  - [`std::thread_local`],
134///  - [`std::thread::current`],
135///  - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and
136/// [`Clone`] implementation.
137///
138/// [`std`]: ../../std/index.html
139/// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html
140/// [`std::thread_local`]: ../../std/macro.thread_local.html
141/// [`std::thread::current`]: ../../std/thread/fn.current.html
142/// [`std::thread::park`]: ../../std/thread/fn.park.html
143/// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html
144/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark
145
146#[stable(feature = "global_alloc", since = "1.28.0")]
147pub unsafe trait GlobalAlloc {
148    /// Allocates memory as described by the given `layout`.
149    ///
150    /// Returns a pointer to newly-allocated memory,
151    /// or null to indicate allocation failure.
152    ///
153    /// # Safety
154    ///
155    /// `layout` must have non-zero size. Attempting to allocate for a zero-sized `layout` will
156    /// result in undefined behavior.
157    ///
158    /// (Extension subtraits might provide more specific bounds on
159    /// behavior, e.g., guarantee a sentinel address or a null pointer
160    /// in response to a zero-size allocation request.)
161    ///
162    /// The allocated block of memory may or may not be initialized.
163    ///
164    /// # Errors
165    ///
166    /// Returning a null pointer indicates that either memory is exhausted
167    /// or `layout` does not meet this allocator's size or alignment constraints.
168    ///
169    /// Implementations are encouraged to return null on memory
170    /// exhaustion rather than aborting, but this is not
171    /// a strict requirement. (Specifically: it is *legal* to
172    /// implement this trait atop an underlying native allocation
173    /// library that aborts on memory exhaustion.)
174    ///
175    /// Clients wishing to abort computation in response to an
176    /// allocation error are encouraged to call the [`handle_alloc_error`] function,
177    /// rather than directly invoking `panic!` or similar.
178    ///
179    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
180    #[stable(feature = "global_alloc", since = "1.28.0")]
181    unsafe fn alloc(&self, layout: Layout) -> *mut u8;
182
183    /// Deallocates the block of memory at the given `ptr` pointer with the given `layout`.
184    ///
185    /// # Safety
186    ///
187    /// The caller must ensure:
188    ///
189    /// * `ptr` is a block of memory currently allocated via this allocator and,
190    ///
191    /// * `layout` is the same layout that was used to allocate that block of
192    ///   memory.
193    ///
194    /// Otherwise the behavior is undefined.
195    #[stable(feature = "global_alloc", since = "1.28.0")]
196    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
197
198    /// Behaves like `alloc`, but also ensures that the contents
199    /// are set to zero before being returned.
200    ///
201    /// # Safety
202    ///
203    /// The caller has to ensure that `layout` has non-zero size. Like `alloc`
204    /// zero sized `layout` will result in undefined behavior.
205    /// However the allocated block of memory is guaranteed to be initialized.
206    ///
207    /// # Errors
208    ///
209    /// Returning a null pointer indicates that either memory is exhausted
210    /// or `layout` does not meet allocator's size or alignment constraints,
211    /// just as in `alloc`.
212    ///
213    /// Clients wishing to abort computation in response to an
214    /// allocation error are encouraged to call the [`handle_alloc_error`] function,
215    /// rather than directly invoking `panic!` or similar.
216    ///
217    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
218    #[stable(feature = "global_alloc", since = "1.28.0")]
219    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
220        let size = layout.size();
221        // SAFETY: the safety contract for `alloc` must be upheld by the caller.
222        let ptr = unsafe { self.alloc(layout) };
223        if !ptr.is_null() {
224            // SAFETY: as allocation succeeded, the region from `ptr`
225            // of size `size` is guaranteed to be valid for writes.
226            unsafe { ptr::write_bytes(ptr, 0, size) };
227        }
228        ptr
229    }
230
231    /// Shrinks or grows a block of memory to the given `new_size` in bytes.
232    /// The block is described by the given `ptr` pointer and `layout`.
233    ///
234    /// If this returns a non-null pointer, then ownership of the memory block
235    /// referenced by `ptr` has been transferred to this allocator.
236    /// Any access to the old `ptr` is Undefined Behavior, even if the
237    /// allocation remained in-place. The newly returned pointer is the only valid pointer
238    /// for accessing this memory now.
239    ///
240    /// The new memory block is allocated with `layout`,
241    /// but with the `size` updated to `new_size` in bytes.
242    /// This new layout must be used when deallocating the new memory block with `dealloc`.
243    /// The range `0..min(layout.size(), new_size)` of the new memory block is
244    /// guaranteed to have the same values as the original block.
245    ///
246    /// If this method returns null, then ownership of the memory
247    /// block has not been transferred to this allocator, and the
248    /// contents of the memory block are unaltered.
249    ///
250    /// # Safety
251    ///
252    /// The caller must ensure that:
253    ///
254    /// * `ptr` is allocated via this allocator,
255    ///
256    /// * `layout` is the same layout that was used
257    ///   to allocate that block of memory,
258    ///
259    /// * `new_size` is greater than zero.
260    ///
261    /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
262    ///   does not overflow `isize` (i.e., the rounded value must be less than or
263    ///   equal to `isize::MAX`).
264    ///
265    /// If these are not followed, the behavior is undefined.
266    ///
267    /// (Extension subtraits might provide more specific bounds on
268    /// behavior, e.g., guarantee a sentinel address or a null pointer
269    /// in response to a zero-size allocation request.)
270    ///
271    /// # Errors
272    ///
273    /// Returns null if the new layout does not meet the size
274    /// and alignment constraints of the allocator, or if reallocation
275    /// otherwise fails.
276    ///
277    /// Implementations are encouraged to return null on memory
278    /// exhaustion rather than panicking or aborting, but this is not
279    /// a strict requirement. (Specifically: it is *legal* to
280    /// implement this trait atop an underlying native allocation
281    /// library that aborts on memory exhaustion.)
282    ///
283    /// Clients wishing to abort computation in response to a
284    /// reallocation error are encouraged to call the [`handle_alloc_error`] function,
285    /// rather than directly invoking `panic!` or similar.
286    ///
287    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
288    #[stable(feature = "global_alloc", since = "1.28.0")]
289    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
290        let alignment = layout.alignment();
291        // SAFETY: the caller must ensure that the `new_size` does not overflow
292        // when rounded up to the next multiple of `alignment`.
293        let new_layout = unsafe { Layout::from_size_alignment_unchecked(new_size, alignment) };
294        // SAFETY: the caller must ensure that `new_layout` is greater than zero.
295        let new_ptr = unsafe { self.alloc(new_layout) };
296        if !new_ptr.is_null() {
297            // SAFETY: the previously allocated block cannot overlap the newly allocated block.
298            // The safety contract for `dealloc` must be upheld by the caller.
299            unsafe {
300                ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
301                self.dealloc(ptr, layout);
302            }
303        }
304        new_ptr
305    }
306}
307
308/// Allows all [`GlobalAllocator`]s to be used with the legacy [`GlobalAlloc`] interface.
309#[stable(feature = "global_alloc", since = "1.28.0")]
310unsafe impl<A> GlobalAlloc for A
311where
312    A: GlobalAllocator + ?Sized,
313{
314    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
315        // SAFETY: guaranteed by the caller.
316        // This might lead to the removal of zero-size checks inside the
317        // `Allocator` implementation.
318        unsafe { assert_unchecked(layout.size() != 0) };
319        match self.allocate(layout) {
320            Ok(ptr) => ptr.cast().as_ptr(),
321            Err(AllocError) => ptr::null_mut(),
322        }
323    }
324
325    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
326        // SAFETY: guaranteed by the caller.
327        unsafe { assert_unchecked(layout.size() != 0) };
328        // SAFETY: only non-null pointers can be currently allocated.
329        let ptr = unsafe { NonNull::new_unchecked(ptr) };
330        // SAFETY: guaranteed by caller.
331        unsafe { self.deallocate(ptr, layout) };
332    }
333
334    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
335        // SAFETY: guaranteed by the caller.
336        unsafe { assert_unchecked(layout.size() != 0) };
337        match self.allocate_zeroed(layout) {
338            Ok(ptr) => ptr.cast().as_ptr(),
339            Err(AllocError) => ptr::null_mut(),
340        }
341    }
342
343    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
344        // SAFETY: guaranteed by the caller.
345        unsafe { assert_unchecked(layout.size() != 0) };
346        // SAFETY: guaranteed by the caller.
347        unsafe { assert_unchecked(new_size != 0) };
348
349        // SAFETY: only non-null pointers can be currently allocated.
350        let ptr = unsafe { NonNull::new_unchecked(ptr) };
351        let alignment = layout.alignment();
352        // SAFETY: the caller must ensure that the `new_size` does not overflow
353        // when rounded up to the next multiple of `alignment`.
354        let new_layout = unsafe { Layout::from_size_alignment_unchecked(new_size, alignment) };
355
356        // SAFETY:
357        // Two preconditions are guaranteed by the caller:
358        // * `ptr` is currently allocated with this allocator.
359        // * `layout` fits the block of memory.
360        // The size precondition is upheld by selecting between `grow` and `shrink`
361        // based on the size.
362        let ptr = unsafe {
363            if new_size >= layout.size() {
364                self.grow(ptr, layout, new_layout)
365            } else {
366                self.shrink(ptr, layout, new_layout)
367            }
368        };
369
370        match ptr {
371            Ok(ptr) => ptr.cast().as_ptr(),
372            Err(AllocError) => ptr::null_mut(),
373        }
374    }
375}