]> git.lizzy.rs Git - rust.git/blob - library/core/src/alloc/global.rs
Auto merge of #102755 - pcc:data-local-tmp, r=Mark-Simulacrum
[rust.git] / library / core / src / alloc / global.rs
1 use crate::alloc::Layout;
2 use crate::cmp;
3 use 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 ///
23 /// ```
24 /// use std::alloc::{GlobalAlloc, Layout};
25 /// use std::cell::UnsafeCell;
26 /// use std::ptr::null_mut;
27 /// use std::sync::atomic::{
28 ///     AtomicUsize,
29 ///     Ordering::{Acquire, SeqCst},
30 /// };
31 ///
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
38 /// }
39 ///
40 /// #[global_allocator]
41 /// static ALLOCATOR: SimpleAllocator = SimpleAllocator {
42 ///     arena: UnsafeCell::new([0x55; ARENA_SIZE]),
43 ///     remaining: AtomicUsize::new(ARENA_SIZE),
44 /// };
45 ///
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().cast::<u8>().add(allocated)
78 ///     }
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);
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.
101 ///
102 /// * You must not rely on allocations actually happening, even if there are explicit
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.
122 #[stable(feature = "global_alloc", since = "1.28.0")]
123 pub 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
170     ///   to allocate that block of memory.
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();
196         // SAFETY: the safety contract for `alloc` must be upheld by the caller.
197         let ptr = unsafe { self.alloc(layout) };
198         if !ptr.is_null() {
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) };
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, and should be
212     /// considered unusable. The new memory block is allocated with `layout`,
213     /// but with the `size` updated to `new_size`. This new layout should be
214     /// used when deallocating the new memory block with `dealloc`. The range
215     /// `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     /// This function is unsafe because undefined behavior can result
225     /// if the caller does not ensure all of the following:
226     ///
227     /// * `ptr` must be currently allocated via this allocator,
228     ///
229     /// * `layout` must be the same layout that was used
230     ///   to allocate that block of memory,
231     ///
232     /// * `new_size` must be greater than zero.
233     ///
234     /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
235     ///   must not overflow (i.e., the rounded value must be less than `usize::MAX`).
236     ///
237     /// (Extension subtraits might provide more specific bounds on
238     /// behavior, e.g., guarantee a sentinel address or a null pointer
239     /// in response to a zero-size allocation request.)
240     ///
241     /// # Errors
242     ///
243     /// Returns null if the new layout does not meet the size
244     /// and alignment constraints of the allocator, or if reallocation
245     /// otherwise fails.
246     ///
247     /// Implementations are encouraged to return null on memory
248     /// exhaustion rather than panicking or aborting, but this is not
249     /// a strict requirement. (Specifically: it is *legal* to
250     /// implement this trait atop an underlying native allocation
251     /// library that aborts on memory exhaustion.)
252     ///
253     /// Clients wishing to abort computation in response to a
254     /// reallocation error are encouraged to call the [`handle_alloc_error`] function,
255     /// rather than directly invoking `panic!` or similar.
256     ///
257     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
258     #[stable(feature = "global_alloc", since = "1.28.0")]
259     unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
260         // SAFETY: the caller must ensure that the `new_size` does not overflow.
261         // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
262         let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
263         // SAFETY: the caller must ensure that `new_layout` is greater than zero.
264         let new_ptr = unsafe { self.alloc(new_layout) };
265         if !new_ptr.is_null() {
266             // SAFETY: the previously allocated block cannot overlap the newly allocated block.
267             // The safety contract for `dealloc` must be upheld by the caller.
268             unsafe {
269                 ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
270                 self.dealloc(ptr, layout);
271             }
272         }
273         new_ptr
274     }
275 }