malloc_best_effort/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4//! Best effort memory allocator for Rust
5//!
6//! Selects suitable allocator depending on target OS and architecture.
7//!
8//! ## Usage
9//! * Put to your `src/main.rs`:
10//! ```rust,ignore
11//! use malloc_best_effort::BEMalloc;
12//!
13//! #[global_allocator]
14//! static GLOBAL: BEMalloc = BEMalloc::new();
15//!
16//! fn main() {
17//!     BEMalloc::init();
18//!
19//!     // Rest of main
20//! }
21//! ```
22//!
23//! * Put to `build.rs` to workaround mimalloc build dependencies:
24//! ```rust,ignore
25//! use std::borrow::Cow;
26//! use std::env;
27//!
28//! fn main() {
29//!     let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target_os not defined!");
30//!     let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("target_arch not defined!"); // on armv6 we need to link with libatomic
31//!
32//!     if target_os == "linux" && target_arch == "arm" {
33//!         // Embrace the atomic capability library across various platforms.
34//!         // For instance, on certain platforms, llvm has relocated the atomic of the arm32 architecture to libclang_rt.builtins.a
35//!         // while some use libatomic.a, and others use libatomic_ops.a.
36//!         let atomic_name = match env::var("DEP_ATOMIC") {
37//!             Ok(atomic_name) => Cow::Owned(atomic_name),
38//!             Err(_) => Cow::Borrowed("atomic"),
39//!         };
40//!         println!("cargo:rustc-link-lib={atomic_name}");
41//!     }
42//!
43//!     // Link with libs needed on Windows
44//!     if target_os == "windows" {
45//!         // https://github.com/microsoft/mimalloc/blob/af21001f7a65eafb8fb16460b018ebf9d75e2ad8/CMakeLists.txt#L487
46//!
47//!         for lib in ["psapi", "shell32", "user32", "advapi32", "bcrypt"] {
48//!             println!("cargo:rustc-link-lib={lib}");
49//!         }
50//!     }
51//! }
52//! ```
53//!
54//! ## Feature flags
55#![doc = document_features::document_features!()]
56
57#[cfg(not(all(
58    target_os = "linux",
59    any(target_arch = "x86_64", target_arch = "aarch64")
60)))]
61mod mimalloc {
62    pub(crate) use mimalloc::MiMalloc as BEMallocImpl;
63
64    #[inline]
65    pub(crate) fn init_impl() {}
66}
67#[cfg(all(
68    target_os = "linux",
69    any(target_arch = "x86_64", target_arch = "aarch64")
70))]
71mod tcmalloc {
72    pub(crate) use tcmalloc_better::TCMalloc as BEMallocImpl;
73
74    #[cfg(feature = "std")]
75    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
76    #[inline]
77    pub(crate) fn init_impl() {
78        BEMallocImpl::process_background_actions_thread();
79    }
80
81    #[cfg(not(feature = "std"))]
82    #[cfg_attr(docsrs, doc(cfg(not(feature = "std"))))]
83    #[inline]
84    pub(crate) fn init_impl() {}
85}
86
87#[cfg(not(all(
88    target_os = "linux",
89    any(target_arch = "x86_64", target_arch = "aarch64")
90)))]
91use crate::mimalloc::BEMallocImpl;
92#[cfg(not(all(
93    target_os = "linux",
94    any(target_arch = "x86_64", target_arch = "aarch64")
95)))]
96use crate::mimalloc::init_impl;
97#[cfg(all(
98    target_os = "linux",
99    any(target_arch = "x86_64", target_arch = "aarch64")
100))]
101use crate::tcmalloc::BEMallocImpl;
102#[cfg(all(
103    target_os = "linux",
104    any(target_arch = "x86_64", target_arch = "aarch64")
105))]
106use crate::tcmalloc::init_impl;
107use core::alloc::{GlobalAlloc, Layout};
108
109/// A memory allocator that can be registered as the standard library’s default
110/// through the `#[global_allocator]` attribute.
111pub struct BEMalloc {
112    alloc_impl: BEMallocImpl,
113}
114
115unsafe impl GlobalAlloc for BEMalloc {
116    #[inline]
117    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
118        unsafe { self.alloc_impl.alloc(layout) }
119    }
120
121    #[inline]
122    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
123        unsafe { self.alloc_impl.dealloc(ptr, layout) }
124    }
125
126    #[inline]
127    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
128        unsafe { self.alloc_impl.alloc_zeroed(layout) }
129    }
130
131    #[inline]
132    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
133        unsafe { self.alloc_impl.realloc(ptr, layout, new_size) }
134    }
135}
136
137impl Default for BEMalloc {
138    #[inline]
139    fn default() -> Self {
140        Self::new()
141    }
142}
143
144impl BEMalloc {
145    /// Create new instance of allocator.
146    #[inline]
147    pub const fn new() -> Self {
148        Self {
149            alloc_impl: BEMallocImpl,
150        }
151    }
152    /// Start allocator background job. Should be called in `main()` function.
153    #[inline]
154    pub fn init() {
155        init_impl();
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn it_frees_allocated_memory() {
165        unsafe {
166            let layout = Layout::from_size_align(8, 8).unwrap();
167            let alloc = BEMalloc::new();
168
169            let ptr = alloc.alloc(layout);
170            alloc.dealloc(ptr, layout);
171        }
172    }
173
174    #[test]
175    fn it_frees_allocated_memory_with_init() {
176        unsafe {
177            let layout = Layout::from_size_align(8, 8).unwrap();
178            let alloc = BEMalloc::new();
179            BEMalloc::init();
180
181            let ptr = alloc.alloc(layout);
182            alloc.dealloc(ptr, layout);
183        }
184    }
185
186    #[test]
187    fn it_frees_allocated_big_memory() {
188        unsafe {
189            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
190            let alloc = BEMalloc::new();
191
192            let ptr = alloc.alloc(layout);
193            alloc.dealloc(ptr, layout);
194        }
195    }
196
197    #[test]
198    fn it_frees_zero_allocated_memory() {
199        unsafe {
200            let layout = Layout::from_size_align(8, 8).unwrap();
201            let alloc = BEMalloc::new();
202
203            let ptr = alloc.alloc_zeroed(layout);
204            alloc.dealloc(ptr, layout);
205        }
206    }
207
208    #[test]
209    fn it_frees_zero_allocated_big_memory() {
210        unsafe {
211            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
212            let alloc = BEMalloc::new();
213
214            let ptr = alloc.alloc_zeroed(layout);
215            alloc.dealloc(ptr, layout);
216        }
217    }
218
219    #[test]
220    fn it_frees_reallocated_memory() {
221        unsafe {
222            let layout = Layout::from_size_align(8, 8).unwrap();
223            let new_size = 16;
224            let new_layout = Layout::from_size_align(new_size, layout.align()).unwrap();
225            let alloc = BEMalloc::new();
226
227            let ptr = alloc.alloc(layout);
228            let ptr = alloc.realloc(ptr, layout, new_size);
229            alloc.dealloc(ptr, new_layout);
230        }
231    }
232
233    #[test]
234    fn it_frees_reallocated_big_memory() {
235        unsafe {
236            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
237            let new_size = 2 << 20;
238            let new_layout = Layout::from_size_align(new_size, layout.align()).unwrap();
239            let alloc = BEMalloc::new();
240
241            let ptr = alloc.alloc(layout);
242            let ptr = alloc.realloc(ptr, layout, new_size);
243            alloc.dealloc(ptr, new_layout);
244        }
245    }
246}