]> git.lizzy.rs Git - rust.git/blob - src/libstd/alloc.rs
Auto merge of #74737 - smmalis37:astconv-factor, r=davidtwco
[rust.git] / src / libstd / alloc.rs
1 //! Memory allocation APIs
2 //!
3 //! In a given program, the standard library has one “global” memory allocator
4 //! that is used for example by `Box<T>` and `Vec<T>`.
5 //!
6 //! Currently the default global allocator is unspecified. Libraries, however,
7 //! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by
8 //! default.
9 //!
10 //! [`System`]: struct.System.html
11 //!
12 //! # The `#[global_allocator]` attribute
13 //!
14 //! This attribute allows configuring the choice of global allocator.
15 //! You can use this to implement a completely custom global allocator
16 //! to route all default allocation requests to a custom object.
17 //!
18 //! ```rust
19 //! use std::alloc::{GlobalAlloc, System, Layout};
20 //!
21 //! struct MyAllocator;
22 //!
23 //! unsafe impl GlobalAlloc for MyAllocator {
24 //!     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
25 //!         System.alloc(layout)
26 //!     }
27 //!
28 //!     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
29 //!         System.dealloc(ptr, layout)
30 //!     }
31 //! }
32 //!
33 //! #[global_allocator]
34 //! static GLOBAL: MyAllocator = MyAllocator;
35 //!
36 //! fn main() {
37 //!     // This `Vec` will allocate memory through `GLOBAL` above
38 //!     let mut v = Vec::new();
39 //!     v.push(1);
40 //! }
41 //! ```
42 //!
43 //! The attribute is used on a `static` item whose type implements the
44 //! [`GlobalAlloc`] trait. This type can be provided by an external library:
45 //!
46 //! [`GlobalAlloc`]: ../../core/alloc/trait.GlobalAlloc.html
47 //!
48 //! ```rust,ignore (demonstrates crates.io usage)
49 //! extern crate jemallocator;
50 //!
51 //! use jemallocator::Jemalloc;
52 //!
53 //! #[global_allocator]
54 //! static GLOBAL: Jemalloc = Jemalloc;
55 //!
56 //! fn main() {}
57 //! ```
58 //!
59 //! The `#[global_allocator]` can only be used once in a crate
60 //! or its recursive dependencies.
61
62 #![deny(unsafe_op_in_unsafe_fn)]
63 #![stable(feature = "alloc_module", since = "1.28.0")]
64
65 use core::intrinsics;
66 use core::ptr::NonNull;
67 use core::sync::atomic::{AtomicPtr, Ordering};
68 use core::{mem, ptr};
69
70 use crate::sys_common::util::dumb_print;
71
72 #[stable(feature = "alloc_module", since = "1.28.0")]
73 #[doc(inline)]
74 pub use alloc_crate::alloc::*;
75
76 /// The default memory allocator provided by the operating system.
77 ///
78 /// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
79 /// plus related functions.
80 ///
81 /// This type implements the `GlobalAlloc` trait and Rust programs by default
82 /// work as if they had this definition:
83 ///
84 /// ```rust
85 /// use std::alloc::System;
86 ///
87 /// #[global_allocator]
88 /// static A: System = System;
89 ///
90 /// fn main() {
91 ///     let a = Box::new(4); // Allocates from the system allocator.
92 ///     println!("{}", a);
93 /// }
94 /// ```
95 ///
96 /// You can also define your own wrapper around `System` if you'd like, such as
97 /// keeping track of the number of all bytes allocated:
98 ///
99 /// ```rust
100 /// use std::alloc::{System, GlobalAlloc, Layout};
101 /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
102 ///
103 /// struct Counter;
104 ///
105 /// static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
106 ///
107 /// unsafe impl GlobalAlloc for Counter {
108 ///     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
109 ///         let ret = System.alloc(layout);
110 ///         if !ret.is_null() {
111 ///             ALLOCATED.fetch_add(layout.size(), SeqCst);
112 ///         }
113 ///         return ret
114 ///     }
115 ///
116 ///     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
117 ///         System.dealloc(ptr, layout);
118 ///         ALLOCATED.fetch_sub(layout.size(), SeqCst);
119 ///     }
120 /// }
121 ///
122 /// #[global_allocator]
123 /// static A: Counter = Counter;
124 ///
125 /// fn main() {
126 ///     println!("allocated bytes before main: {}", ALLOCATED.load(SeqCst));
127 /// }
128 /// ```
129 ///
130 /// It can also be used directly to allocate memory independently of whatever
131 /// global allocator has been selected for a Rust program. For example if a Rust
132 /// program opts in to using jemalloc as the global allocator, `System` will
133 /// still allocate memory using `malloc` and `HeapAlloc`.
134 #[stable(feature = "alloc_system_type", since = "1.28.0")]
135 #[derive(Debug, Default, Copy, Clone)]
136 pub struct System;
137
138 // The AllocRef impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl,
139 // which is in `std::sys::*::alloc`.
140 #[unstable(feature = "allocator_api", issue = "32838")]
141 unsafe impl AllocRef for System {
142     #[inline]
143     fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
144         unsafe {
145             let size = layout.size();
146             if size == 0 {
147                 Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
148             } else {
149                 let raw_ptr = match init {
150                     AllocInit::Uninitialized => GlobalAlloc::alloc(self, layout),
151                     AllocInit::Zeroed => GlobalAlloc::alloc_zeroed(self, layout),
152                 };
153                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
154                 Ok(MemoryBlock { ptr, size })
155             }
156         }
157     }
158
159     #[inline]
160     unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
161         if layout.size() != 0 {
162             // SAFETY: The safety guarantees are explained in the documentation
163             // for the `GlobalAlloc` trait and its `dealloc` method.
164             unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) }
165         }
166     }
167
168     #[inline]
169     unsafe fn grow(
170         &mut self,
171         ptr: NonNull<u8>,
172         layout: Layout,
173         new_size: usize,
174         placement: ReallocPlacement,
175         init: AllocInit,
176     ) -> Result<MemoryBlock, AllocErr> {
177         let size = layout.size();
178         debug_assert!(
179             new_size >= size,
180             "`new_size` must be greater than or equal to `memory.size()`"
181         );
182
183         if size == new_size {
184             return Ok(MemoryBlock { ptr, size });
185         }
186
187         match placement {
188             ReallocPlacement::InPlace => Err(AllocErr),
189             ReallocPlacement::MayMove if layout.size() == 0 => {
190                 let new_layout =
191                     // SAFETY: The new size and layout alignement guarantees
192                     // are transfered to the caller (they come from parameters).
193                     //
194                     // See the preconditions for `Layout::from_size_align` to
195                     // see what must be checked.
196                     unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
197                 self.alloc(new_layout, init)
198             }
199             ReallocPlacement::MayMove => {
200                 // SAFETY:
201                 //
202                 // The safety guarantees are explained in the documentation
203                 // for the `GlobalAlloc` trait and its `dealloc` method.
204                 //
205                 // `realloc` probably checks for `new_size > size` or something
206                 // similar.
207                 //
208                 // For the guarantees about `init_offset`, see its documentation:
209                 // `ptr` is assumed valid (and checked for non-NUL) and
210                 // `memory.size` is set to `new_size` so the offset being `size`
211                 // is valid.
212                 let memory = unsafe {
213                     intrinsics::assume(new_size > size);
214                     let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
215                     let memory =
216                         MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size };
217                     init.init_offset(memory, size);
218                     memory
219                 };
220                 Ok(memory)
221             }
222         }
223     }
224
225     #[inline]
226     unsafe fn shrink(
227         &mut self,
228         ptr: NonNull<u8>,
229         layout: Layout,
230         new_size: usize,
231         placement: ReallocPlacement,
232     ) -> Result<MemoryBlock, AllocErr> {
233         let size = layout.size();
234         debug_assert!(
235             new_size <= size,
236             "`new_size` must be smaller than or equal to `memory.size()`"
237         );
238
239         if size == new_size {
240             return Ok(MemoryBlock { ptr, size });
241         }
242
243         match placement {
244             ReallocPlacement::InPlace => Err(AllocErr),
245             ReallocPlacement::MayMove if new_size == 0 => {
246                 // SAFETY: see `GlobalAlloc::dealloc` for the guarantees that
247                 // must be respected. `ptr` and `layout` are parameters and so
248                 // those guarantees must be checked by the caller.
249                 unsafe { self.dealloc(ptr, layout) };
250                 Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
251             }
252             ReallocPlacement::MayMove => {
253                 // SAFETY:
254                 //
255                 // See `GlobalAlloc::realloc` for more informations about the
256                 // guarantees expected by this method. `ptr`, `layout` and
257                 // `new_size` are parameters and the responsability for their
258                 // correctness is left to the caller.
259                 //
260                 // `realloc` probably checks for `new_size < size` or something
261                 // similar.
262                 let memory = unsafe {
263                     intrinsics::assume(new_size < size);
264                     let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
265                     MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }
266                 };
267                 Ok(memory)
268             }
269         }
270     }
271 }
272 static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
273
274 /// Registers a custom allocation error hook, replacing any that was previously registered.
275 ///
276 /// The allocation error hook is invoked when an infallible memory allocation fails, before
277 /// the runtime aborts. The default hook prints a message to standard error,
278 /// but this behavior can be customized with the [`set_alloc_error_hook`] and
279 /// [`take_alloc_error_hook`] functions.
280 ///
281 /// The hook is provided with a `Layout` struct which contains information
282 /// about the allocation that failed.
283 ///
284 /// The allocation error hook is a global resource.
285 ///
286 /// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html
287 /// [`take_alloc_error_hook`]: fn.take_alloc_error_hook.html
288 #[unstable(feature = "alloc_error_hook", issue = "51245")]
289 pub fn set_alloc_error_hook(hook: fn(Layout)) {
290     HOOK.store(hook as *mut (), Ordering::SeqCst);
291 }
292
293 /// Unregisters the current allocation error hook, returning it.
294 ///
295 /// *See also the function [`set_alloc_error_hook`].*
296 ///
297 /// If no custom hook is registered, the default hook will be returned.
298 ///
299 /// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html
300 #[unstable(feature = "alloc_error_hook", issue = "51245")]
301 pub fn take_alloc_error_hook() -> fn(Layout) {
302     let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
303     if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }
304 }
305
306 fn default_alloc_error_hook(layout: Layout) {
307     dumb_print(format_args!("memory allocation of {} bytes failed", layout.size()));
308 }
309
310 #[cfg(not(test))]
311 #[doc(hidden)]
312 #[alloc_error_handler]
313 #[unstable(feature = "alloc_internals", issue = "none")]
314 pub fn rust_oom(layout: Layout) -> ! {
315     let hook = HOOK.load(Ordering::SeqCst);
316     let hook: fn(Layout) =
317         if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } };
318     hook(layout);
319     crate::process::abort()
320 }
321
322 #[cfg(not(test))]
323 #[doc(hidden)]
324 #[allow(unused_attributes)]
325 #[unstable(feature = "alloc_internals", issue = "none")]
326 pub mod __default_lib_allocator {
327     use super::{GlobalAlloc, Layout, System};
328     // These magic symbol names are used as a fallback for implementing the
329     // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is
330     // no `#[global_allocator]` attribute.
331
332     // for symbol names src/librustc_ast/expand/allocator.rs
333     // for signatures src/librustc_allocator/lib.rs
334
335     // linkage directives are provided as part of the current compiler allocator
336     // ABI
337
338     #[rustc_std_internal_symbol]
339     pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
340         // SAFETY: see the guarantees expected by `Layout::from_size_align` and
341         // `GlobalAlloc::alloc`.
342         unsafe {
343             let layout = Layout::from_size_align_unchecked(size, align);
344             System.alloc(layout)
345         }
346     }
347
348     #[rustc_std_internal_symbol]
349     pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) {
350         // SAFETY: see the guarantees expected by `Layout::from_size_align` and
351         // `GlobalAlloc::dealloc`.
352         unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) }
353     }
354
355     #[rustc_std_internal_symbol]
356     pub unsafe extern "C" fn __rdl_realloc(
357         ptr: *mut u8,
358         old_size: usize,
359         align: usize,
360         new_size: usize,
361     ) -> *mut u8 {
362         // SAFETY: see the guarantees expected by `Layout::from_size_align` and
363         // `GlobalAlloc::realloc`.
364         unsafe {
365             let old_layout = Layout::from_size_align_unchecked(old_size, align);
366             System.realloc(ptr, old_layout, new_size)
367         }
368     }
369
370     #[rustc_std_internal_symbol]
371     pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
372         // SAFETY: see the guarantees expected by `Layout::from_size_align` and
373         // `GlobalAlloc::alloc_zeroed`.
374         unsafe {
375             let layout = Layout::from_size_align_unchecked(size, align);
376             System.alloc_zeroed(layout)
377         }
378     }
379 }