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}