]> git.proxmox.com Git - rustc.git/blame - library/core/src/alloc/global.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / library / core / src / alloc / global.rs
CommitLineData
ba9703b0
XL
1use crate::alloc::Layout;
2use crate::cmp;
3use crate::ptr;
4
5/// A memory allocator that can be registered as the standard library’s default
6/// through the `#[global_allocator]` attribute.
7///
8/// Some of the methods require that a memory block be *currently
9/// allocated* via an allocator. This means that:
10///
11/// * the starting address for that memory block was previously
12/// returned by a previous call to an allocation method
13/// such as `alloc`, and
14///
15/// * the memory block has not been subsequently deallocated, where
16/// blocks are deallocated either by being passed to a deallocation
17/// method such as `dealloc` or by being
18/// passed to a reallocation method that returns a non-null pointer.
19///
20///
21/// # Example
22///
136023e0
XL
23/// ```
24/// use std::alloc::{GlobalAlloc, Layout};
25/// use std::cell::UnsafeCell;
ba9703b0 26/// use std::ptr::null_mut;
136023e0
XL
27/// use std::sync::atomic::{
28/// AtomicUsize,
29/// Ordering::{Acquire, SeqCst},
30/// };
ba9703b0 31///
136023e0
XL
32/// const ARENA_SIZE: usize = 128 * 1024;
33/// const MAX_SUPPORTED_ALIGN: usize = 4096;
34/// #[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN
35/// struct SimpleAllocator {
36/// arena: UnsafeCell<[u8; ARENA_SIZE]>,
37/// remaining: AtomicUsize, // we allocate from the top, counting down
ba9703b0
XL
38/// }
39///
40/// #[global_allocator]
136023e0
XL
41/// static ALLOCATOR: SimpleAllocator = SimpleAllocator {
42/// arena: UnsafeCell::new([0x55; ARENA_SIZE]),
43/// remaining: AtomicUsize::new(ARENA_SIZE),
44/// };
ba9703b0 45///
136023e0
XL
46/// unsafe impl Sync for SimpleAllocator {}
47///
48/// unsafe impl GlobalAlloc for SimpleAllocator {
49/// unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
50/// let size = layout.size();
51/// let align = layout.align();
52///
53/// // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2.
54/// // So we can safely use a mask to ensure alignment without worrying about UB.
55/// let align_mask_to_round_down = !(align - 1);
56///
57/// if align > MAX_SUPPORTED_ALIGN {
58/// return null_mut();
59/// }
60///
61/// let mut allocated = 0;
62/// if self
63/// .remaining
64/// .fetch_update(SeqCst, SeqCst, |mut remaining| {
65/// if size > remaining {
66/// return None;
67/// }
68/// remaining -= size;
69/// remaining &= align_mask_to_round_down;
70/// allocated = remaining;
71/// Some(remaining)
72/// })
73/// .is_err()
74/// {
75/// return null_mut();
76/// };
77/// (self.arena.get() as *mut u8).add(allocated)
ba9703b0 78/// }
136023e0
XL
79/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
80/// }
81///
82/// fn main() {
83/// let _s = format!("allocating a string!");
84/// let currently = ALLOCATOR.remaining.load(Acquire);
85/// println!("allocated so far: {}", ARENA_SIZE - currently);
ba9703b0
XL
86/// }
87/// ```
88///
89/// # Safety
90///
91/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and
92/// implementors must ensure that they adhere to these contracts:
93///
94/// * It's undefined behavior if global allocators unwind. This restriction may
95/// be lifted in the future, but currently a panic from any of these
96/// functions may lead to memory unsafety.
97///
98/// * `Layout` queries and calculations in general must be correct. Callers of
99/// this trait are allowed to rely on the contracts defined on each method,
100/// and implementors must ensure such contracts remain true.
5869c6ff 101///
94222f64 102/// * You must not rely on allocations actually happening, even if there are explicit
5869c6ff
XL
103/// heap allocations in the source. The optimizer may detect unused allocations that it can either
104/// eliminate entirely or move to the stack and thus never invoke the allocator. The
105/// optimizer may further assume that allocation is infallible, so code that used to fail due
106/// to allocator failures may now suddenly work because the optimizer worked around the
107/// need for an allocation. More concretely, the following code example is unsound, irrespective
108/// of whether your custom allocator allows counting how many allocations have happened.
109///
110/// ```rust,ignore (unsound and has placeholders)
111/// drop(Box::new(42));
112/// let number_of_heap_allocs = /* call private allocator API */;
113/// unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); }
114/// ```
115///
116/// Note that the optimizations mentioned above are not the only
117/// optimization that can be applied. You may generally not rely on heap allocations
118/// happening if they can be removed without changing program behavior.
119/// Whether allocations happen or not is not part of the program behavior, even if it
120/// could be detected via an allocator that tracks allocations by printing or otherwise
121/// having side effects.
ba9703b0
XL
122#[stable(feature = "global_alloc", since = "1.28.0")]
123pub unsafe trait GlobalAlloc {
124 /// Allocate memory as described by the given `layout`.
125 ///
126 /// Returns a pointer to newly-allocated memory,
127 /// or null to indicate allocation failure.
128 ///
129 /// # Safety
130 ///
131 /// This function is unsafe because undefined behavior can result
132 /// if the caller does not ensure that `layout` has non-zero size.
133 ///
134 /// (Extension subtraits might provide more specific bounds on
135 /// behavior, e.g., guarantee a sentinel address or a null pointer
136 /// in response to a zero-size allocation request.)
137 ///
138 /// The allocated block of memory may or may not be initialized.
139 ///
140 /// # Errors
141 ///
142 /// Returning a null pointer indicates that either memory is exhausted
143 /// or `layout` does not meet this allocator's size or alignment constraints.
144 ///
145 /// Implementations are encouraged to return null on memory
146 /// exhaustion rather than aborting, but this is not
147 /// a strict requirement. (Specifically: it is *legal* to
148 /// implement this trait atop an underlying native allocation
149 /// library that aborts on memory exhaustion.)
150 ///
151 /// Clients wishing to abort computation in response to an
152 /// allocation error are encouraged to call the [`handle_alloc_error`] function,
153 /// rather than directly invoking `panic!` or similar.
154 ///
155 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
156 #[stable(feature = "global_alloc", since = "1.28.0")]
157 unsafe fn alloc(&self, layout: Layout) -> *mut u8;
158
159 /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`.
160 ///
161 /// # Safety
162 ///
163 /// This function is unsafe because undefined behavior can result
164 /// if the caller does not ensure all of the following:
165 ///
166 /// * `ptr` must denote a block of memory currently allocated via
167 /// this allocator,
168 ///
169 /// * `layout` must be the same layout that was used
6a06907d 170 /// to allocate that block of memory.
ba9703b0
XL
171 #[stable(feature = "global_alloc", since = "1.28.0")]
172 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
173
174 /// Behaves like `alloc`, but also ensures that the contents
175 /// are set to zero before being returned.
176 ///
177 /// # Safety
178 ///
179 /// This function is unsafe for the same reasons that `alloc` is.
180 /// However the allocated block of memory is guaranteed to be initialized.
181 ///
182 /// # Errors
183 ///
184 /// Returning a null pointer indicates that either memory is exhausted
185 /// or `layout` does not meet allocator's size or alignment constraints,
186 /// just as in `alloc`.
187 ///
188 /// Clients wishing to abort computation in response to an
189 /// allocation error are encouraged to call the [`handle_alloc_error`] function,
190 /// rather than directly invoking `panic!` or similar.
191 ///
192 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
193 #[stable(feature = "global_alloc", since = "1.28.0")]
194 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
195 let size = layout.size();
f035d41b
XL
196 // SAFETY: the safety contract for `alloc` must be upheld by the caller.
197 let ptr = unsafe { self.alloc(layout) };
ba9703b0 198 if !ptr.is_null() {
f035d41b
XL
199 // SAFETY: as allocation succeeded, the region from `ptr`
200 // of size `size` is guaranteed to be valid for writes.
201 unsafe { ptr::write_bytes(ptr, 0, size) };
ba9703b0
XL
202 }
203 ptr
204 }
205
206 /// Shrink or grow a block of memory to the given `new_size`.
207 /// The block is described by the given `ptr` pointer and `layout`.
208 ///
209 /// If this returns a non-null pointer, then ownership of the memory block
210 /// referenced by `ptr` has been transferred to this allocator.
211 /// The memory may or may not have been deallocated,
212 /// and should be considered unusable (unless of course it was
213 /// transferred back to the caller again via the return value of
214 /// this method). The new memory block is allocated with `layout`, but
6a06907d
XL
215 /// with the `size` updated to `new_size`. This new layout should be
216 /// used when deallocating the new memory block with `dealloc`. The range
217 /// `0..min(layout.size(), new_size)` of the new memory block is
218 /// guaranteed to have the same values as the original block.
ba9703b0
XL
219 ///
220 /// If this method returns null, then ownership of the memory
221 /// block has not been transferred to this allocator, and the
222 /// contents of the memory block are unaltered.
223 ///
224 /// # Safety
225 ///
226 /// This function is unsafe because undefined behavior can result
227 /// if the caller does not ensure all of the following:
228 ///
229 /// * `ptr` must be currently allocated via this allocator,
230 ///
231 /// * `layout` must be the same layout that was used
232 /// to allocate that block of memory,
233 ///
234 /// * `new_size` must be greater than zero.
235 ///
236 /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
237 /// must not overflow (i.e., the rounded value must be less than `usize::MAX`).
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 {
f035d41b
XL
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) };
ba9703b0 267 if !new_ptr.is_null() {
f035d41b
XL
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 }
ba9703b0
XL
274 }
275 new_ptr
276 }
277}