Skip to main content

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}