global.rs - source
Description: Source of the Rust file `library/core/src/alloc/global.rs`.

#
core/alloc/
global.rs

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