turbo_tasks_malloc/
lib.rs1mod counter;
2mod memory_pressure;
3
4use std::{
5 alloc::{GlobalAlloc, Layout},
6 marker::PhantomData,
7 ops::{Add, AddAssign},
8};
9
10use self::counter::{add, flush, get, remove, update};
11
12#[derive(Default, Clone, Debug)]
13pub struct AllocationInfo {
14 pub allocations: usize,
15 pub deallocations: usize,
16 pub allocation_count: usize,
17 pub deallocation_count: usize,
18}
19
20impl AllocationInfo {
21 pub const ZERO: Self = Self {
22 allocations: 0,
23 deallocations: 0,
24 allocation_count: 0,
25 deallocation_count: 0,
26 };
27
28 pub fn is_empty(&self) -> bool {
29 self.allocations == 0
30 && self.deallocations == 0
31 && self.allocation_count == 0
32 && self.deallocation_count == 0
33 }
34
35 pub fn memory_usage(&self) -> usize {
36 self.allocations.saturating_sub(self.deallocations)
37 }
38}
39
40impl Add<Self> for AllocationInfo {
41 type Output = Self;
42
43 fn add(self, other: Self) -> Self {
44 Self {
45 allocations: self.allocations + other.allocations,
46 deallocations: self.deallocations + other.deallocations,
47 allocation_count: self.allocation_count + other.allocation_count,
48 deallocation_count: self.deallocation_count + other.deallocation_count,
49 }
50 }
51}
52
53impl AddAssign<Self> for AllocationInfo {
54 fn add_assign(&mut self, other: Self) {
55 self.allocations += other.allocations;
56 self.deallocations += other.deallocations;
57 self.allocation_count += other.allocation_count;
58 self.deallocation_count += other.deallocation_count;
59 }
60}
61
62#[derive(Default, Clone, Debug)]
63pub struct AllocationCounters {
64 pub allocations: usize,
65 pub deallocations: usize,
66 pub allocation_count: usize,
67 pub deallocation_count: usize,
68 _not_send: PhantomData<*mut ()>,
69}
70
71impl AllocationCounters {
72 const fn new() -> Self {
73 Self {
74 allocation_count: 0,
75 deallocation_count: 0,
76 allocations: 0,
77 deallocations: 0,
78 _not_send: PhantomData {},
79 }
80 }
81}
82
83pub struct TurboMalloc;
86
87impl TurboMalloc {
88 pub fn memory_usage() -> usize {
90 get()
91 }
92
93 pub fn thread_stop() {
94 flush();
95 }
96
97 pub fn thread_park() {
98 Self::collect(false);
99 }
100
101 pub fn collect(force: bool) {
107 #[cfg(all(feature = "custom_allocator", not(target_family = "wasm")))]
108 unsafe {
109 libmimalloc_sys::mi_collect(force);
110 }
111 #[cfg(not(all(feature = "custom_allocator", not(target_family = "wasm"))))]
112 {
113 let _ = force;
114 }
115 }
116
117 pub fn allocation_counters() -> AllocationCounters {
118 self::counter::allocation_counters()
119 }
120
121 pub fn reset_allocation_counters(start: AllocationCounters) {
122 self::counter::reset_allocation_counters(start);
123 }
124
125 pub fn memory_pressure() -> Option<u8> {
139 memory_pressure::memory_pressure()
140 }
141}
142
143#[inline]
145fn base_alloc() -> &'static impl GlobalAlloc {
146 #[cfg(all(feature = "custom_allocator", not(target_family = "wasm")))]
147 return &mimalloc::MiMalloc;
148 #[cfg(not(all(feature = "custom_allocator", not(target_family = "wasm"))))]
149 return &std::alloc::System;
150}
151
152#[allow(unused_variables)]
153unsafe fn base_alloc_size(ptr: *const u8, layout: Layout) -> usize {
154 #[cfg(all(feature = "custom_allocator", not(target_family = "wasm")))]
155 return unsafe { mimalloc::MiMalloc.usable_size(ptr) };
156 #[cfg(not(all(feature = "custom_allocator", not(target_family = "wasm"))))]
157 return layout.size();
158}
159
160unsafe impl GlobalAlloc for TurboMalloc {
161 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
162 let ret = unsafe { base_alloc().alloc(layout) };
163 if !ret.is_null() {
164 let size = unsafe { base_alloc_size(ret, layout) };
165 add(size);
166 }
167 ret
168 }
169
170 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
171 let size = unsafe { base_alloc_size(ptr, layout) };
172 unsafe { base_alloc().dealloc(ptr, layout) };
173 remove(size);
174 }
175
176 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
177 let ret = unsafe { base_alloc().alloc_zeroed(layout) };
178 if !ret.is_null() {
179 let size = unsafe { base_alloc_size(ret, layout) };
180 add(size);
181 }
182 ret
183 }
184
185 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
186 let old_size = unsafe { base_alloc_size(ptr, layout) };
187 let ret = unsafe { base_alloc().realloc(ptr, layout, new_size) };
188 if !ret.is_null() {
189 let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
192 let new_size = unsafe { base_alloc_size(ret, new_layout) };
193 update(old_size, new_size);
194 }
195 ret
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::TurboMalloc;
202
203 #[test]
204 fn memory_pressure_is_in_range() {
205 let value = TurboMalloc::memory_pressure();
206
207 #[cfg(any(
209 all(target_os = "linux", not(target_family = "wasm")),
210 target_os = "macos",
211 windows,
212 ))]
213 let value = value.expect("memory_pressure() should return Some on this platform");
214
215 #[cfg(not(any(
217 all(target_os = "linux", not(target_family = "wasm")),
218 target_os = "macos",
219 windows,
220 )))]
221 let Some(value) = value else {
222 return;
223 };
224
225 assert!(
226 value <= 100,
227 "memory_pressure() returned {value}, expected a value in 0..=100"
228 );
229 }
230}