talc/cell.rs
1//! [`TalcCell`] allows using [`Talc`] as a Rust allocator
2//! for single-threaded unsynchronized locking.
3//!
4//! See [`TalcCell`].
5
6use core::{
7 alloc::{GlobalAlloc, Layout},
8 cell::UnsafeCell,
9 marker::PhantomData,
10 ops::{Deref, DerefMut},
11 ptr::null_mut,
12};
13
14use crate::{
15 base::binning::Binning,
16 base::{Reserved, Talc},
17 ptr_utils::nonnull_slice_from_raw_parts,
18 source::Source,
19};
20
21use core::ptr::NonNull;
22
23use allocator_api2::alloc::{AllocError, Allocator};
24
25/// [`TalcCell`] implements [`GlobalAlloc`] and [`Allocator`]
26/// without locking, but is [`!Sync`](Sync).
27///
28/// This type has similar semantics to a [`Cell`](core::cell::Cell).
29///
30/// # Example
31/// ```rust
32/// # #![cfg_attr(feature = "nightly", feature(allocator_api))]
33/// # extern crate allocator_api2;
34/// # extern crate talc;
35///
36/// use allocator_api2::alloc::{Allocator, Layout};
37/// use allocator_api2::vec::Vec;
38/// use talc::{TalcCell, source::*};
39///
40/// static mut HEAP: [u8; 2048] = [0; 2048];
41///
42/// let talc = TalcCell::new(unsafe { Claim::array(&raw mut HEAP) });
43///
44/// let mut my_vec = Vec::<u32, _>::with_capacity_in(42, &talc);
45/// my_vec.push(123);
46/// ```
47///
48/// # Safety
49/// [`TalcCell`]'s API does not expose references to the inner [`Talc`] within
50/// an [`UnsafeCell`] and is `!Sync`, so it's safe to mutate the inner data
51/// through a shared reference.
52///
53/// There is an exception to this; a reference to the inner [`Talc`] is exposed to
54/// sources. [`Source`] is thus an unsafe trait to implement, and the
55/// implementation must uphold that they don't use the
56/// [`TalcCell`]/[`TalcLock`](crate::sync::TalcLock) directly or indirectly
57/// (e.g. calling `dbg!` in [`Source::resize`] when [`TalcLock`](crate::sync::TalcLock) is the global allocator)
58/// in the implementation.
59/// This requirement is not unique to [`TalcCell`].
60/// If [`TalcLock`](crate::sync::TalcLock) is used in the source impl, it'll deadlock.
61///
62/// To help catch bad [`Source`] implementations, [`TalcCell`] tracks
63/// borrows when `debug_assertions` are enabled, similar to a
64/// [`RefCell`](core::cell::RefCell).
65#[derive(Debug)]
66pub struct TalcCell<S: Source, B: Binning> {
67 cell: UnsafeCell<Talc<S, B>>,
68
69 #[cfg(debug_assertions)]
70 borrowed_at: core::cell::Cell<Option<&'static core::panic::Location<'static>>>,
71}
72
73impl<S: Source, B: Binning> TalcCell<S, B> {
74 /// Create a new [`TalcCell`].
75 #[inline]
76 pub const fn new(source: S) -> Self {
77 Self {
78 cell: UnsafeCell::new(Talc::new(source)),
79
80 #[cfg(debug_assertions)]
81 borrowed_at: core::cell::Cell::new(None),
82 }
83 }
84
85 /// Returns a mutable reference to the inner [`Talc`].
86 #[inline]
87 pub fn get_mut(&mut self) -> &mut Talc<S, B> {
88 self.cell.get_mut()
89 }
90
91 /// Consumes the [`TalcCell`], returning the inner [`Talc`].
92 #[inline]
93 pub fn into_inner(self) -> Talc<S, B> {
94 self.cell.into_inner()
95 }
96
97 /// Borrow the inner [`Talc`] mutably.
98 ///
99 /// # Safety
100 /// Creating aliasing references must be avoided.
101 /// [`TalcCell`] ensures against this in the following ways:
102 ///
103 /// - [`TalcCell`]'s functions do not call [`TalcCell::borrow`] more than once.
104 /// - [`TalcCell`]'s functions do not call another [`TalcCell`] function while holding a [`BorrowedTalc`].
105 /// - [`TalcCell`]'s API does not expose references to the inner [`Talc`].
106 /// - There is an exception to this. [`Source::acquire`] provides user
107 /// code with a mutable reference to the inner [`Talc`]. Implementing
108 /// [`Source`] is unsafe because the implementor must uphold that they
109 /// do not touch the outer [`TalcCell`] within the [`Source::acquire`]
110 /// implementation. [`TalcCell`] relies on this for correctness here.
111 #[inline]
112 #[track_caller]
113 unsafe fn borrow(&self) -> BorrowedTalc<'_, S, B> {
114 #[cfg(debug_assertions)]
115 {
116 if let Some(borrowed_at) = self.borrowed_at.take() {
117 panic!(
118 "Tried to borrow the Talc, was borrowed previously at {}:{}:{}. Did the source attempt to use the TalcCell?",
119 borrowed_at.file(),
120 borrowed_at.line(),
121 borrowed_at.column(),
122 );
123 }
124
125 self.borrowed_at.set(Some(core::panic::Location::caller()));
126 }
127
128 BorrowedTalc {
129 ptr: unsafe { NonNull::new_unchecked(self.cell.get()) },
130 _phantom: PhantomData,
131
132 #[cfg(debug_assertions)]
133 borrow_release: &self.borrowed_at,
134 }
135 }
136
137 /// Swaps out the source for another.
138 ///
139 /// If you just want to clone the source, see [`TalcCell::clone_source`].
140 #[inline]
141 #[track_caller]
142 pub fn replace_source(&self, source: S) -> S {
143 unsafe {
144 // SAFETY: See `Self::borrow`'s safety docs
145 core::mem::replace(&mut self.borrow().source, source)
146 }
147 }
148
149 /// Obtain the inner allocation statistics.
150 #[cfg(feature = "counters")]
151 #[inline]
152 #[track_caller]
153 pub fn counters(&self) -> crate::base::Counters {
154 unsafe {
155 // SAFETY: See `Self::borrow`'s safety docs
156 self.borrow().counters().clone()
157 }
158 }
159
160 /// See [`Talc::reserved`] for documentation details.
161 #[inline]
162 #[track_caller]
163 pub unsafe fn reserved(&self, heap_end: NonNull<u8>) -> Reserved {
164 // SAFETY: See `Self::borrow`'s safety docs
165 // SAFETY: `Talc` function safety requirements guaranteed by caller
166 self.borrow().reserved(heap_end)
167 }
168
169 /// See [`Talc::claim`] for documentation details.
170 #[inline]
171 #[track_caller]
172 pub unsafe fn claim(&self, base: *mut u8, size: usize) -> Option<NonNull<u8>> {
173 // SAFETY: See `Self::borrow`'s safety docs
174 // SAFETY: `Talc` function safety requirements guaranteed by caller
175 self.borrow().claim(base, size)
176 }
177
178 /// See [`Talc::extend`] for documentation details.
179 #[inline]
180 #[track_caller]
181 pub unsafe fn extend(&self, heap_end: NonNull<u8>, new_end: *mut u8) -> NonNull<u8> {
182 // SAFETY: See `Self::borrow`'s safety docs
183 // SAFETY: `Talc` function safety requirements guaranteed by caller
184 self.borrow().extend(heap_end, new_end)
185 }
186
187 /// See [`Talc::truncate`] for documentation details.
188 #[inline]
189 #[track_caller]
190 pub unsafe fn truncate(&self, heap_end: NonNull<u8>, new_end: *mut u8) -> Option<NonNull<u8>> {
191 // SAFETY: See `Self::borrow`'s safety docs
192 // SAFETY: `Talc` function safety requirements guaranteed by caller
193 self.borrow().truncate(heap_end, new_end)
194 }
195
196 /// See [`Talc::resize`] for documentation details.
197 #[inline]
198 #[track_caller]
199 pub unsafe fn resize(&self, heap_end: NonNull<u8>, new_end: *mut u8) -> Option<NonNull<u8>> {
200 self.borrow().resize(heap_end, new_end)
201 }
202}
203
204impl<S: Source + Clone, B: Binning> TalcCell<S, B> {
205 /// Returns a clone of [`Talc`]'s source.
206 ///
207 /// To set the source instead, use [`TalcCell::replace_source`].
208 #[inline]
209 #[track_caller]
210 pub fn clone_source(&self) -> S {
211 unsafe {
212 // SAFETY: See `Self::borrow`'s safety docs
213 self.borrow().source.clone()
214 }
215 }
216}
217
218struct BorrowedTalc<'b, S: Source, B: Binning> {
219 ptr: NonNull<Talc<S, B>>,
220 _phantom: PhantomData<&'b ()>,
221
222 #[cfg(debug_assertions)]
223 borrow_release: &'b core::cell::Cell<Option<&'static core::panic::Location<'static>>>,
224}
225impl<S: Source, B: Binning> Drop for BorrowedTalc<'_, S, B> {
226 #[inline]
227 fn drop(&mut self) {
228 #[cfg(debug_assertions)]
229 {
230 self.borrow_release.set(None);
231 }
232 }
233}
234impl<S: Source, B: Binning> Deref for BorrowedTalc<'_, S, B> {
235 type Target = Talc<S, B>;
236
237 #[inline]
238 fn deref(&self) -> &Self::Target {
239 unsafe { self.ptr.as_ref() }
240 }
241}
242impl<S: Source, B: Binning> DerefMut for BorrowedTalc<'_, S, B> {
243 #[inline]
244 fn deref_mut(&mut self) -> &mut Self::Target {
245 unsafe { self.ptr.as_mut() }
246 }
247}
248
249unsafe impl<S: Source, B: Binning> GlobalAlloc for TalcCell<S, B> {
250 #[inline]
251 #[track_caller]
252 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
253 // SAFETY: See `Self::borrow`'s safety docs
254 // SAFETY: guaranteed by caller
255 self.borrow().allocate(layout).map_or(null_mut(), |nn| nn.as_ptr())
256 }
257 #[inline]
258 #[track_caller]
259 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
260 // SAFETY: See `Self::borrow`'s safety docs
261 // SAFETY: guaranteed by caller
262 self.borrow().deallocate(ptr, layout)
263 }
264
265 #[inline]
266 #[track_caller]
267 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
268 let size = layout.size();
269 // SAFETY: the safety contract for `alloc` must be upheld by the caller.
270 let ptr = unsafe { self.alloc(layout) };
271 if !ptr.is_null() {
272 // SAFETY: as allocation succeeded, the region from `ptr`
273 // of size `size` is guaranteed to be valid for writes.
274 unsafe { core::ptr::write_bytes(ptr, 0, size) };
275 }
276 ptr
277 }
278
279 #[cfg(not(any(feature = "disable-grow-in-place", feature = "disable-realloc-in-place")))]
280 #[track_caller]
281 unsafe fn realloc(&self, ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 {
282 // SAFETY: See `Self::borrow`'s safety docs
283 let mut talc = self.borrow();
284
285 // SAFETY: guaranteed by caller that `ptr` was previously allocated by
286 // this allocator given the layout `old_layout`.
287 if talc.try_realloc_in_place(ptr, old_layout, new_size) {
288 return ptr;
289 }
290
291 // grow in-place failed, reallocate manually
292
293 // SAFETY: guaranteed by caller that `new_size` is a valid layout size
294 let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
295
296 // SAFETY: guaranteed by caller that `new_size` is nonzero
297 let allocation = match talc.allocate(new_layout) {
298 Some(ptr) => ptr.as_ptr(),
299 None => return null_mut(),
300 };
301
302 // Shrink always succeeds, only growing the allocation might fail,
303 // so the `old_layout.size() < new_size` here, and thus we just copy
304 // all the old allocation bytes.
305 allocation.copy_from_nonoverlapping(ptr, old_layout.size());
306
307 talc.deallocate(ptr, old_layout);
308
309 allocation
310 }
311
312 #[cfg(all(feature = "disable-grow-in-place", not(feature = "disable-realloc-in-place")))]
313 #[track_caller]
314 unsafe fn realloc(&self, ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 {
315 // SAFETY: See `Self::borrow`'s safety docs
316 let mut talc = self.borrow();
317
318 if new_size <= old_layout.size() {
319 // SAFETY: guaranteed by caller that `ptr` was previously allocated by
320 // this allocator given the layout `old_layout`.
321 talc.shrink(ptr, old_layout, new_size);
322 return ptr;
323 }
324
325 // grow in-place failed, reallocate manually
326
327 // SAFETY: guaranteed by caller that `new_size` is a valid layout size
328 let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
329
330 // SAFETY: guaranteed by caller that `new_size` is nonzero
331 let Some(allocation) = talc.allocate(new_layout) else { return null_mut() };
332
333 // Shrink always succeeds, only growing the allocation might fail,
334 // so the `old_layout.size() < new_size` here, and thus we just copy
335 // all the old allocation bytes.
336 allocation.as_ptr().copy_from_nonoverlapping(ptr, old_layout.size());
337
338 talc.deallocate(ptr, old_layout);
339
340 allocation.as_ptr()
341 }
342
343 #[cfg(feature = "disable-realloc-in-place")]
344 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
345 // SAFETY: the caller must ensure that the `new_size` does not overflow.
346 // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
347 let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
348 // SAFETY: the caller must ensure that `new_layout` is greater than zero.
349 let new_ptr = unsafe { self.alloc(new_layout) };
350 if !new_ptr.is_null() {
351 // SAFETY: the previously allocated block cannot overlap the newly allocated block.
352 // The safety contract for `dealloc` must be upheld by the caller.
353 unsafe {
354 core::ptr::copy_nonoverlapping(
355 ptr,
356 new_ptr,
357 core::cmp::min(layout.size(), new_size),
358 );
359 self.dealloc(ptr, layout);
360 }
361 }
362 new_ptr
363 }
364}
365
366unsafe impl<S: Source, B: Binning> Allocator for TalcCell<S, B> {
367 #[inline]
368 #[track_caller]
369 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
370 if layout.size() == 0 {
371 let dangling = unsafe { NonNull::new_unchecked(layout.align() as *mut u8) };
372 return Ok(nonnull_slice_from_raw_parts(dangling, layout.size()));
373 }
374
375 // SAFETY: See `Self::borrow`'s safety docs
376 // SAFETY: Ensured the size is not zero above.
377 match unsafe { self.borrow().allocate(layout) } {
378 Some(allocation) => Ok(nonnull_slice_from_raw_parts(allocation, layout.size())),
379 None => Err(AllocError),
380 }
381 }
382 #[inline]
383 #[track_caller]
384 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
385 if layout.size() != 0 {
386 // SAFETY: See `Self::borrow`'s safety docs
387 self.borrow().deallocate(ptr.as_ptr(), layout)
388 }
389 }
390
391 #[inline]
392 #[track_caller]
393 fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
394 let ptr = self.allocate(layout)?;
395 // SAFETY: `alloc` returns a valid memory block
396 unsafe { ptr.cast::<u8>().as_ptr().write_bytes(0, ptr.len()) }
397 Ok(ptr)
398 }
399 #[inline]
400 #[track_caller]
401 unsafe fn grow_zeroed(
402 &self,
403 ptr: NonNull<u8>,
404 old_layout: Layout,
405 new_layout: Layout,
406 ) -> Result<NonNull<[u8]>, AllocError> {
407 let res = self.grow(ptr, old_layout, new_layout);
408
409 if let Ok(allocation) = res {
410 allocation
411 .as_ptr()
412 .cast::<u8>()
413 .add(old_layout.size())
414 .write_bytes(0, new_layout.size() - old_layout.size());
415 }
416
417 res
418 }
419
420 #[cfg(not(any(feature = "disable-grow-in-place", feature = "disable-realloc-in-place")))]
421 #[track_caller]
422 unsafe fn grow(
423 &self,
424 ptr: NonNull<u8>,
425 old_layout: Layout,
426 new_layout: Layout,
427 ) -> Result<NonNull<[u8]>, AllocError> {
428 debug_assert!(new_layout.size() >= old_layout.size());
429
430 if old_layout.size() == 0 {
431 return Allocator::allocate(self, new_layout);
432 } else if crate::ptr_utils::is_aligned_to(ptr.as_ptr(), new_layout.align()) {
433 // alignment is fine, try to allocate in-place
434 // SAFETY: See `Self::borrow`'s safety docs
435 if self.borrow().try_grow_in_place(ptr.as_ptr(), old_layout, new_layout.size()) {
436 return Ok(nonnull_slice_from_raw_parts(ptr, new_layout.size()));
437 }
438 }
439
440 // can't grow in place, reallocate manually
441 // SAFETY: See `Self::borrow`'s safety docs
442 let allocation = self.borrow().allocate(new_layout).ok_or(AllocError)?;
443 allocation.as_ptr().copy_from_nonoverlapping(ptr.as_ptr(), old_layout.size());
444 // SAFETY: See `Self::borrow`'s safety docs
445 self.borrow().deallocate(ptr.as_ptr(), old_layout);
446
447 Ok(nonnull_slice_from_raw_parts(allocation, new_layout.size()))
448 }
449
450 // Default implementations
451
452 #[cfg(any(feature = "disable-grow-in-place", feature = "disable-realloc-in-place"))]
453 #[inline]
454 #[track_caller]
455 unsafe fn grow(
456 &self,
457 ptr: NonNull<u8>,
458 old_layout: Layout,
459 new_layout: Layout,
460 ) -> Result<NonNull<[u8]>, AllocError> {
461 debug_assert!(new_layout.size() >= old_layout.size());
462
463 let new_ptr = self.allocate(new_layout)?;
464
465 // SAFETY: because `new_layout.size()` must be greater than or equal to
466 // `old_layout.size()`, both the old and new memory allocation are valid for reads and
467 // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
468 // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
469 // safe. The safety contract for `dealloc` must be upheld by the caller.
470 unsafe {
471 core::ptr::copy_nonoverlapping(
472 ptr.as_ptr(),
473 new_ptr.as_ptr().cast(),
474 old_layout.size(),
475 );
476 self.deallocate(ptr, old_layout);
477 }
478
479 Ok(new_ptr)
480 }
481
482 #[cfg(not(feature = "disable-realloc-in-place"))]
483 #[track_caller]
484 unsafe fn shrink(
485 &self,
486 ptr: NonNull<u8>,
487 old_layout: Layout,
488 new_layout: Layout,
489 ) -> Result<NonNull<[u8]>, AllocError> {
490 debug_assert!(new_layout.size() <= old_layout.size());
491
492 // SAFETY: See `Self::borrow`'s safety docs
493 let mut talc = self.borrow();
494
495 if new_layout.size() == 0 {
496 if old_layout.size() > 0 {
497 talc.deallocate(ptr.as_ptr(), old_layout);
498 }
499
500 let dangling = unsafe { NonNull::new_unchecked(new_layout.align() as *mut u8) };
501 return Ok(nonnull_slice_from_raw_parts(dangling, new_layout.size()));
502 }
503
504 if !crate::ptr_utils::is_aligned_to(ptr.as_ptr(), new_layout.align()) {
505 let allocation = talc.allocate(new_layout).ok_or(AllocError)?;
506 allocation.as_ptr().copy_from_nonoverlapping(ptr.as_ptr(), new_layout.size());
507 talc.deallocate(ptr.as_ptr(), old_layout);
508 return Ok(nonnull_slice_from_raw_parts(allocation, new_layout.size()));
509 }
510
511 talc.shrink(ptr.as_ptr(), old_layout, new_layout.size());
512
513 Ok(nonnull_slice_from_raw_parts(ptr, new_layout.size()))
514 }
515
516 #[cfg(feature = "disable-realloc-in-place")]
517 #[track_caller]
518 unsafe fn shrink(
519 &self,
520 ptr: NonNull<u8>,
521 old_layout: Layout,
522 new_layout: Layout,
523 ) -> Result<NonNull<[u8]>, AllocError> {
524 debug_assert!(new_layout.size() <= old_layout.size());
525
526 let new_ptr = self.allocate(new_layout)?;
527
528 // SAFETY: because `new_layout.size()` must be lower than or equal to
529 // `old_layout.size()`, both the old and new memory allocation are valid for reads and
530 // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
531 // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
532 // safe. The safety contract for `dealloc` must be upheld by the caller.
533 unsafe {
534 core::ptr::copy_nonoverlapping(
535 ptr.as_ptr(),
536 new_ptr.as_ptr().cast(),
537 new_layout.size(),
538 );
539 self.deallocate(ptr, old_layout);
540 }
541
542 Ok(new_ptr)
543 }
544}
545
546/// Wraps [`TalcCell`] but implements [`Sync`].
547///
548/// This is sound on single-threaded platforms without asynchronous interruptions or
549/// signal handling, such as single-threaded WebAssembly. See [`TalcSyncCell::new_wasm`].
550///
551/// Otherwise,
552/// This easily leads to unsoundness. Strongly consider [`TalcLock`](crate::sync::TalcLock) instead.
553///
554/// This type implements [`Self::new`] and [`GlobalAlloc`]
555/// making it usable as a global allocator.
556///
557/// See [`TalcSyncCell::new`] amd [`TalcSyncCell::new_wasm`].
558pub struct TalcSyncCell<S: Source, B: Binning>(TalcCell<S, B>);
559
560/// SAFETY: Upheld by `new*` implementations.
561unsafe impl<S: Source, B: Binning> Sync for TalcSyncCell<S, B> {}
562
563impl<S: Source, B: Binning> TalcSyncCell<S, B> {
564 /// Safely create a new [`TalcSyncCell`] on single-threaded WebAssembly, where it is safe to do so.
565 ///
566 /// # Panics
567 ///
568 /// This panics if the target is not single-threaded WebAssembly.
569 ///
570 /// # Examples
571 ///
572 /// ```
573 /// use talc::{cell::TalcSyncCell, wasm::*};
574 ///
575 /// #[cfg(all(not(target_feature = "atomics"), target_family = "wasm"))]
576 /// #[global_allocator]
577 /// static TALC: TalcSyncCell<WasmGrowAndExtend, WasmBinning>
578 /// = TalcSyncCell::new_wasm(WasmGrowAndExtend::new());
579 /// ```
580 pub const fn new_wasm(source: S) -> Self {
581 if cfg!(all(not(target_feature = "atomics"), target_family = "wasm")) {
582 Self(TalcCell::new(source))
583 } else {
584 panic!("Not running on single-threaded WebAssembly; `TalcSyncCell` is unsafe.")
585 }
586 }
587
588 /// Create a [`TalcSyncCell`] from a [`TalcCell`].
589 ///
590 /// [`TalcSyncCell`] is useful if your program is exclusively
591 /// single-threaded (no multi-threading, no interrupts, no signal handling)
592 /// and you want a global allocator that doesn't lock.
593 /// See [`TalcSyncCell::new_wasm`].
594 ///
595 /// # Safety
596 /// [`TalcSyncCell`] is inherently unsafe by implementing
597 /// [`Sync`] on [`TalcCell`], which has the semantics of a [`Cell`](core::cell::Cell).
598 ///
599 /// Calling a [`GlobalAlloc`] function on this type from two threads simultaneously is UB.
600 /// Calling from an interrupt or signal handler while the main thread is actively
601 /// allocating/deallocating/reallocating is UB.
602 ///
603 /// As the caller of [`TalcSyncCell::new`], you have the responsibility to ensure
604 /// that all uses of this [`TalcSyncCell`] do not violate Rust's aliasing rules.
605 /// This is not easy, and in general using this function is not recommended.
606 ///
607 /// Remember that contention-less locking is cheap.
608 /// It's generally best to use [`TalcLock`](crate::sync::TalcLock) with a basic `Mutex`
609 /// implementation (e.g. from the `spin` crate) instead.
610 ///
611 /// # Example
612 ///
613 /// ```rust
614 /// use talc::{source::Claim, TalcCell, base::binning::DefaultBinning};
615 ///
616 /// #[global_allocator]
617 /// static ALLOC: talc::cell::TalcSyncCell<Claim, DefaultBinning> = unsafe {
618 /// use core::mem::MaybeUninit;
619 /// static mut ARENA: [MaybeUninit<u8>; 100000] = [MaybeUninit::uninit(); 100000];
620 /// talc::cell::TalcSyncCell::new(TalcCell::new(Claim::array(&raw mut ARENA)))
621 /// };
622 /// ```
623 pub const unsafe fn new(talc: TalcCell<S, B>) -> Self {
624 Self(talc)
625 }
626}
627
628unsafe impl<S: Source, B: Binning> GlobalAlloc for TalcSyncCell<S, B> {
629 #[track_caller]
630 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
631 self.0.alloc(layout)
632 }
633 #[track_caller]
634 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
635 self.0.dealloc(ptr, layout)
636 }
637 #[track_caller]
638 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
639 self.0.realloc(ptr, layout, new_size)
640 }
641}