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