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///         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#[stable(feature = "global_alloc", since = "1.28.0")]
119pub unsafe trait GlobalAlloc {
120    /// Allocates memory as described by the given `layout`.
121    ///
122    /// Returns a pointer to newly-allocated memory,
123    /// or null to indicate allocation failure.
124    ///
125    /// # Safety
126    ///
127    /// `layout` must have non-zero size. Attempting to allocate for a zero-sized `layout` may
128    /// result in undefined behavior.
129    ///
130    /// (Extension subtraits might provide more specific bounds on
131    /// behavior, e.g., guarantee a sentinel address or a null pointer
132    /// in response to a zero-size allocation request.)
133    ///
134    /// The allocated block of memory may or may not be initialized.
135    ///
136    /// # Errors
137    ///
138    /// Returning a null pointer indicates that either memory is exhausted
139    /// or `layout` does not meet this allocator's size or alignment constraints.
140    ///
141    /// Implementations are encouraged to return null on memory
142    /// exhaustion rather than aborting, but this is not
143    /// a strict requirement. (Specifically: it is *legal* to
144    /// implement this trait atop an underlying native allocation
145    /// library that aborts on memory exhaustion.)
146    ///
147    /// Clients wishing to abort computation in response to an
148    /// allocation error are encouraged to call the [`handle_alloc_error`] function,
149    /// rather than directly invoking `panic!` or similar.
150    ///
151    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
152    #[stable(feature = "global_alloc", since = "1.28.0")]
153    unsafe fn alloc(&self, layout: Layout) -> *mut u8;
154
155    /// Deallocates the block of memory at the given `ptr` pointer with the given `layout`.
156    ///
157    /// # Safety
158    ///
159    /// The caller must ensure:
160    ///
161    /// * `ptr` is a block of memory currently allocated via this allocator and,
162    ///
163    /// * `layout` is the same layout that was used to allocate that block of
164    ///   memory.
165    ///
166    /// Otherwise undefined behavior can result.
167    #[stable(feature = "global_alloc", since = "1.28.0")]
168    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
169
170    /// Behaves like `alloc`, but also ensures that the contents
171    /// are set to zero before being returned.
172    ///
173    /// # Safety
174    ///
175    /// The caller has to ensure that `layout` has non-zero size. Like `alloc`
176    /// zero sized `layout` can result in undefined behavior.
177    /// However the allocated block of memory is guaranteed to be initialized.
178    ///
179    /// # Errors
180    ///
181    /// Returning a null pointer indicates that either memory is exhausted
182    /// or `layout` does not meet allocator's size or alignment constraints,
183    /// just as in `alloc`.
184    ///
185    /// Clients wishing to abort computation in response to an
186    /// allocation error are encouraged to call the [`handle_alloc_error`] function,
187    /// rather than directly invoking `panic!` or similar.
188    ///
189    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
190    #[stable(feature = "global_alloc", since = "1.28.0")]
191    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
192        let size = layout.size();
193        // SAFETY: the safety contract for `alloc` must be upheld by the caller.
194        let ptr = unsafe { self.alloc(layout) };
195        if !ptr.is_null() {
196            // SAFETY: as allocation succeeded, the region from `ptr`
197            // of size `size` is guaranteed to be valid for writes.
198            unsafe { ptr::write_bytes(ptr, 0, size) };
199        }
200        ptr
201    }
202
203    /// Shrinks or grows a block of memory to the given `new_size` in bytes.
204    /// The block is described by the given `ptr` pointer and `layout`.
205    ///
206    /// If this returns a non-null pointer, then ownership of the memory block
207    /// referenced by `ptr` has been transferred to this allocator.
208    /// Any access to the old `ptr` is Undefined Behavior, even if the
209    /// allocation remained in-place. The newly returned pointer is the only valid pointer
210    /// for accessing this memory now.
211    ///
212    /// The new memory block is allocated with `layout`,
213    /// but with the `size` updated to `new_size` in bytes.
214    /// This new layout must be used when deallocating the new memory block with `dealloc`.
215    /// The range `0..min(layout.size(), new_size)` of the new memory block is
216    /// guaranteed to have the same values as the original block.
217    ///
218    /// If this method returns null, then ownership of the memory
219    /// block has not been transferred to this allocator, and the
220    /// contents of the memory block are unaltered.
221    ///
222    /// # Safety
223    ///
224    /// The caller must ensure that:
225    ///
226    /// * `ptr` is allocated via this allocator,
227    ///
228    /// * `layout` is the same layout that was used
229    ///   to allocate that block of memory,
230    ///
231    /// * `new_size` is greater than zero.
232    ///
233    /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
234    ///   does not overflow `isize` (i.e., the rounded value must be less than or
235    ///   equal to `isize::MAX`).
236    ///
237    /// If these are not followed, undefined behavior can result.
238    ///
239    /// (Extension subtraits might provide more specific bounds on
240    /// behavior, e.g., guarantee a sentinel address or a null pointer
241    /// in response to a zero-size allocation request.)
242    ///
243    /// # Errors
244    ///
245    /// Returns null if the new layout does not meet the size
246    /// and alignment constraints of the allocator, or if reallocation
247    /// otherwise fails.
248    ///
249    /// Implementations are encouraged to return null on memory
250    /// exhaustion rather than panicking or aborting, but this is not
251    /// a strict requirement. (Specifically: it is *legal* to
252    /// implement this trait atop an underlying native allocation
253    /// library that aborts on memory exhaustion.)
254    ///
255    /// Clients wishing to abort computation in response to a
256    /// reallocation error are encouraged to call the [`handle_alloc_error`] function,
257    /// rather than directly invoking `panic!` or similar.
258    ///
259    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
260    #[stable(feature = "global_alloc", since = "1.28.0")]
261    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
262        // SAFETY: the caller must ensure that the `new_size` does not overflow.
263        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
264        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
265        // SAFETY: the caller must ensure that `new_layout` is greater than zero.
266        let new_ptr = unsafe { self.alloc(new_layout) };
267        if !new_ptr.is_null() {
268            // SAFETY: the previously allocated block cannot overlap the newly allocated block.
269            // The safety contract for `dealloc` must be upheld by the caller.
270            unsafe {
271                ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
272                self.dealloc(ptr, layout);
273            }
274        }
275        new_ptr
276    }
277}