blink_alloc/global/local.rs
1use core::{
2 alloc::{GlobalAlloc, Layout},
3 cell::UnsafeCell,
4 ptr::{null_mut, NonNull},
5};
6
7#[cfg(debug_assertions)]
8use core::cell::Cell;
9
10#[cfg(feature = "nightly")]
11use core::alloc::{AllocError, Allocator};
12
13#[cfg(not(feature = "nightly"))]
14use allocator_api2::alloc::{AllocError, Allocator};
15
16use crate::{cold, local::BlinkAlloc};
17
18struct State<A: Allocator> {
19 blink: BlinkAlloc<A>,
20 enabled: bool,
21}
22
23impl<A: Allocator> State<A> {
24 #[inline(always)]
25 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
26 match self.enabled {
27 true => self.blink.allocate(layout),
28 false => {
29 cold();
30 self.blink.inner().allocate(layout)
31 }
32 }
33 }
34
35 #[inline(always)]
36 fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
37 match self.enabled {
38 true => self.blink.allocate_zeroed(layout),
39 false => {
40 cold();
41 self.blink.inner().allocate_zeroed(layout)
42 }
43 }
44 }
45
46 #[inline(always)]
47 unsafe fn resize(
48 &self,
49 ptr: NonNull<u8>,
50 old_layout: Layout,
51 new_layout: Layout,
52 ) -> Result<NonNull<[u8]>, AllocError> {
53 match self.enabled {
54 true => self.blink.resize(ptr, old_layout, new_layout),
55 false => {
56 cold();
57 if old_layout.size() >= new_layout.size() {
58 self.blink.inner().grow(ptr, old_layout, new_layout)
59 } else {
60 self.blink.inner().shrink(ptr, old_layout, new_layout)
61 }
62 }
63 }
64 }
65
66 #[inline(always)]
67 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
68 match self.enabled {
69 true => self.blink.deallocate(ptr, layout.size()),
70 false => {
71 cold();
72 self.blink.inner().deallocate(ptr, layout)
73 }
74 }
75 }
76}
77
78switch_std_default! {
79 /// [`GlobalAlloc`] implementation based on [`BlinkAlloc`].
80 pub struct UnsafeGlobalBlinkAlloc<A: Allocator = +std::alloc::System> {
81 state: UnsafeCell<State<A>>,
82 #[cfg(debug_assertions)]
83 allocations: Cell<u64>,
84 }
85}
86
87// The user is responsible for ensuring that this allocator
88// won't be used concurrently.
89// To make this sound, `UnsafeGlobalBlinkAlloc::new` and `UnsafeGlobalBlinkAlloc::new_in`
90// are marked unsafe.
91unsafe impl<A: Allocator + Send> Send for UnsafeGlobalBlinkAlloc<A> {}
92unsafe impl<A: Allocator + Sync> Sync for UnsafeGlobalBlinkAlloc<A> {}
93
94#[cfg(feature = "std")]
95impl UnsafeGlobalBlinkAlloc<std::alloc::System> {
96 /// Create a new [`UnsafeGlobalBlinkAlloc`].
97 ///
98 /// Const function can be used to initialize a static variable.
99 ///
100 /// # Safety
101 ///
102 /// This method is unsafe because this type is not thread-safe
103 /// but implements `Sync`.
104 /// Allocator returned by this method must not be used concurrently.
105 ///
106 /// For safer alternative see [`GlobalBlinkAlloc`](https://docs.rs/blink-alloc/0.2.2/blink_alloc/struct.GlobalBlinkAlloc.html).
107 ///
108 /// # Example
109 ///
110 /// ```
111 /// use blink_alloc::UnsafeGlobalBlinkAlloc;
112 ///
113 /// // Safety: This program is single-threaded.
114 /// #[global_allocator]
115 /// static GLOBAL_ALLOC: UnsafeGlobalBlinkAlloc = unsafe { UnsafeGlobalBlinkAlloc::new() };
116 ///
117 /// let _ = Box::new(42);
118 /// let _ = vec![1, 2, 3];
119 /// ```
120 pub const unsafe fn new() -> Self {
121 UnsafeGlobalBlinkAlloc::new_in(std::alloc::System)
122 }
123
124 /// Create a new [`UnsafeGlobalBlinkAlloc`].
125 ///
126 /// This method allows to specify initial chunk size.
127 ///
128 /// Const function can be used to initialize a static variable.
129 ///
130 /// # Safety
131 ///
132 /// This method is unsafe because this type is not thread-safe
133 /// but implements `Sync`.
134 /// Allocator returned by this method must not be used concurrently.
135 ///
136 /// For safer alternative see [`GlobalBlinkAlloc`](https://docs.rs/blink-alloc/0.2.2/blink_alloc/struct.GlobalBlinkAlloc.html).
137 ///
138 /// # Example
139 ///
140 /// ```
141 /// # #[cfg(feature = "std")] fn main() {
142 /// use blink_alloc::UnsafeGlobalBlinkAlloc;
143 ///
144 /// // Safety: This program is single-threaded.
145 /// #[global_allocator]
146 /// static GLOBAL_ALLOC: UnsafeGlobalBlinkAlloc<std::alloc::System> = unsafe { UnsafeGlobalBlinkAlloc::new_in(std::alloc::System) };
147 ///
148 /// let _ = Box::new(42);
149 /// let _ = vec![1, 2, 3];
150 /// # }
151 /// # #[cfg(not(feature = "std"))] fn main() {}
152 /// ```
153 pub const unsafe fn with_chunk_size(chunk_size: usize) -> Self {
154 UnsafeGlobalBlinkAlloc::with_chunk_size_in(chunk_size, std::alloc::System)
155 }
156}
157
158impl<A> UnsafeGlobalBlinkAlloc<A>
159where
160 A: Allocator,
161{
162 /// Create a new [`UnsafeGlobalBlinkAlloc`]
163 /// with specified underlying allocator.
164 ///
165 /// Const function can be used to initialize a static variable.
166 ///
167 /// # Safety
168 ///
169 /// This method is unsafe because this type is not thread-safe
170 /// but implements `Sync`.
171 /// Allocator returned by this method must not be used concurrently.
172 ///
173 /// For safer alternative see [`GlobalBlinkAlloc`](https://docs.rs/blink-alloc/0.2.2/blink_alloc/struct.GlobalBlinkAlloc.html).
174 ///
175 /// # Example
176 ///
177 /// ```
178 /// # #[cfg(feature = "std")] fn main() {
179 /// use blink_alloc::UnsafeGlobalBlinkAlloc;
180 ///
181 /// // Safety: This program is single-threaded.
182 /// #[global_allocator]
183 /// static GLOBAL_ALLOC: UnsafeGlobalBlinkAlloc<std::alloc::System> = unsafe { UnsafeGlobalBlinkAlloc::new_in(std::alloc::System) };
184 ///
185 /// let _ = Box::new(42);
186 /// let _ = vec![1, 2, 3];
187 /// # }
188 /// # #[cfg(not(feature = "std"))] fn main() {}
189 /// ```
190 pub const unsafe fn new_in(allocator: A) -> Self {
191 UnsafeGlobalBlinkAlloc {
192 state: UnsafeCell::new(State {
193 blink: BlinkAlloc::new_in(allocator),
194 enabled: false,
195 }),
196 #[cfg(debug_assertions)]
197 allocations: Cell::new(0),
198 }
199 }
200
201 /// Create a new [`UnsafeGlobalBlinkAlloc`]
202 /// with specified underlying allocator.
203 ///
204 /// This method allows to specify initial chunk size.
205 ///
206 /// Const function can be used to initialize a static variable.
207 ///
208 /// # Safety
209 ///
210 /// This method is unsafe because this type is not thread-safe
211 /// but implements `Sync`.
212 /// Allocator returned by this method must not be used concurrently.
213 ///
214 /// For safer alternative see [`GlobalBlinkAlloc`](https://docs.rs/blink-alloc/0.2.2/blink_alloc/struct.GlobalBlinkAlloc.html).
215 ///
216 /// # Example
217 ///
218 /// ```
219 /// # #[cfg(feature = "std")] fn main() {
220 /// use blink_alloc::UnsafeGlobalBlinkAlloc;
221 ///
222 /// // Safety: This program is single-threaded.
223 /// #[global_allocator]
224 /// static GLOBAL_ALLOC: UnsafeGlobalBlinkAlloc<std::alloc::System> = unsafe { UnsafeGlobalBlinkAlloc::new_in(std::alloc::System) };
225 ///
226 /// let _ = Box::new(42);
227 /// let _ = vec![1, 2, 3];
228 /// # }
229 /// # #[cfg(not(feature = "std"))] fn main() {}
230 /// ```
231 pub const unsafe fn with_chunk_size_in(chunk_size: usize, allocator: A) -> Self {
232 UnsafeGlobalBlinkAlloc {
233 state: UnsafeCell::new(State {
234 blink: BlinkAlloc::with_chunk_size_in(chunk_size, allocator),
235 enabled: false,
236 }),
237 #[cfg(debug_assertions)]
238 allocations: Cell::new(0),
239 }
240 }
241
242 /// Resets this allocator, deallocating all chunks except the last one.
243 /// Last chunk will be reused.
244 /// With steady memory usage after few iterations
245 /// one chunk should be sufficient for all allocations between resets.
246 ///
247 /// # Safety
248 ///
249 /// Memory allocated from this allocator in blink mode becomes invalidated.
250 /// The user is responsible to ensure that previously allocated memory
251 /// won't be used after reset.
252 ///
253 /// # Example
254 ///
255 /// ```
256 /// # #[cfg(feature = "std")] fn main() {
257 /// use blink_alloc::UnsafeGlobalBlinkAlloc;
258 ///
259 /// #[global_allocator]
260 /// static GLOBAL_ALLOC: UnsafeGlobalBlinkAlloc = unsafe { UnsafeGlobalBlinkAlloc::new() };
261 ///
262 /// unsafe { GLOBAL_ALLOC.blink_mode() };
263 ///
264 /// let b = Box::new(42);
265 /// let v = vec![1, 2, 3];
266 /// drop(b);
267 /// drop(v);
268 ///
269 /// // Safety: memory allocated in blink mode won't be used after reset.
270 /// unsafe {
271 /// GLOBAL_ALLOC.reset();
272 /// GLOBAL_ALLOC.direct_mode();
273 /// };
274 /// # }
275 /// # #[cfg(not(feature = "std"))] fn main() {}
276 /// ```
277 #[inline(always)]
278 pub unsafe fn reset(&self) {
279 #[cfg(debug_assertions)]
280 {
281 assert_eq!(self.allocations.get(), 0, "Not everything was deallocated");
282 }
283
284 (*self.state.get()).blink.reset_unchecked();
285 }
286
287 /// Switches allocator to blink mode.
288 /// All allocations will be served by blink-allocator.
289 ///
290 /// The type is created in direct mode.
291 /// When used as global allocator, user may manually switch into blink mode
292 /// in `main` or at any point later.
293 ///
294 /// However user must switch back to direct mode before returning from `main`.
295 ///
296 /// # Safety
297 ///
298 /// Must be externally synchronized with other threads accessing this allocator.
299 /// Memory allocated in direct mode must not be deallocated while in blink mode.
300 #[inline(always)]
301 pub unsafe fn blink_mode(&self) {
302 (*self.state.get()).enabled = true;
303 }
304
305 /// Switches allocator to blink mode.
306 /// All allocations will be served by underlying allocator.
307 ///
308 /// The type is created in direct mode.
309 /// When used as global allocator, user may manually switch into blink mode
310 /// in `main` or at any point later.
311 ///
312 /// However user must switch back to direct mode before returning from `main`.
313 ///
314 /// # Safety
315 ///
316 /// Must be externally synchronized with other threads accessing this allocator.
317 /// Memory allocated in blink mode must not be deallocated while in direct mode.
318 #[inline(always)]
319 pub unsafe fn direct_mode(&self) {
320 self.reset();
321 (*self.state.get()).enabled = false;
322 }
323}
324
325unsafe impl<A> GlobalAlloc for UnsafeGlobalBlinkAlloc<A>
326where
327 A: Allocator,
328{
329 #[inline]
330 unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
331 match (*self.state.get()).allocate(layout) {
332 Ok(ptr) => {
333 #[cfg(debug_assertions)]
334 if (*self.state.get()).enabled {
335 self.allocations.set(self.allocations.get() + 1);
336 }
337 ptr.as_ptr().cast()
338 }
339 Err(_) => null_mut(),
340 }
341 }
342
343 #[inline]
344 unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
345 let ptr = NonNull::new_unchecked(ptr);
346 (*self.state.get()).deallocate(ptr, layout);
347 #[cfg(debug_assertions)]
348 if (*self.state.get()).enabled {
349 self.allocations
350 .set(self.allocations.get().saturating_sub(1));
351 }
352 }
353
354 #[inline]
355 unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
356 match (*self.state.get()).allocate_zeroed(layout) {
357 Ok(ptr) => {
358 #[cfg(debug_assertions)]
359 if (*self.state.get()).enabled {
360 self.allocations.set(self.allocations.get() + 1);
361 }
362 ptr.as_ptr().cast()
363 }
364 Err(_) => null_mut(),
365 }
366 }
367
368 #[inline]
369 unsafe fn realloc(
370 &self,
371 ptr: *mut u8,
372 layout: core::alloc::Layout,
373 new_size: usize,
374 ) -> *mut u8 {
375 let Ok(new_layout) = Layout::from_size_align(new_size, layout.align()) else {
376 return null_mut();
377 };
378
379 let result = match NonNull::new(ptr) {
380 None => (*self.state.get()).allocate(new_layout),
381 Some(ptr) => (*self.state.get()).resize(ptr, layout, new_layout),
382 };
383
384 match result {
385 Ok(ptr) => ptr.as_ptr().cast(),
386 Err(_) => null_mut(),
387 }
388 }
389}