pub struct Talc<S: Source, B: Binning> {
pub source: S,
/* private fields */
}Expand description
The core allocator type.
To use Talc across multiple threads, e.g. as a global allocator, use TalcLock.
To use Talc in a single thread, e.g. via the
Allocator API, use TalcCell.
Talc itself does not exhibit interior mutability.
You need a mutable reference to allocate using Talc, therefore it doesn’t implement
Allocator or GlobalAlloc
itself.
§Generic Parameters
An overview of what to consider:
-
The source contains callbacks for acquiring and reclaiming memory. Implementations provided out of the box include:
ManualClaimGlobalAllocSourceAllocatorSource- WebAssembly also has out-of-the-box sources in
talc::wasm.
-
The binning implementation determines the internal types and operations
Talcuses to classify chunks into free-lists and keeps track of free-list occupancy. The default implementation isDefaultBinning. The main reason to deviate from this would be knowing the profile of your allocations well, and being able to divvy them up better and/or faster than the generic algorithm. SeeBinningand the docs on the trait members for more information.
See the Source and Binning trait documentation for more info.
Fields§
§source: SThe memory source state.
This is user-accessible and can be mutated.
Talc just holds it and calls Source::acquire and Source::resize
as documented. Talc doesn’t read/write to it after initialization.
Implementations§
Source§impl<S: Source, B: Binning> Talc<S, B>
impl<S: Source, B: Binning> Talc<S, B>
Sourcepub fn counters(&self) -> &Counters
Available on crate feature counters only.
pub fn counters(&self) -> &Counters
counters only.Obtain a reference to the internal allocation statistics.
Avoid holding onto the reference as this will block allocations (as you’re effectively holding the lock on the allocator, or preventing a mutable reference being created to the allocator). Reading immediately or cloning the struct is recommended.
Source§impl<S: Source, B: Binning> Talc<S, B>
impl<S: Source, B: Binning> Talc<S, B>
Sourcepub unsafe fn try_allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>
pub unsafe fn try_allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>
Allocate a contiguous region of memory according to layout, if possible.
§Safety
layout.size() must be nonzero.
Sourcepub unsafe fn allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>
pub unsafe fn allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>
Allocate a contiguous region of memory according to layout, if possible.
§Safety
layout.size() must be nonzero.
Sourcepub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout)
pub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout)
Sourcepub unsafe fn try_grow_in_place(
&mut self,
ptr: *mut u8,
layout: Layout,
new_size: usize,
) -> bool
pub unsafe fn try_grow_in_place( &mut self, ptr: *mut u8, layout: Layout, new_size: usize, ) -> bool
Attempt to grow a previously allocated/reallocated region of memory to new_size.
The return value indicates whether the operation was successful.
The validity of the pointer is maintained regardless, but the allocation
size does not change if false is returned.
§Safety
ptr must have been previously allocated or reallocated given layout.
new_size must be larger or equal to layout.size().
Sourcepub unsafe fn shrink(&mut self, ptr: *mut u8, layout: Layout, new_size: usize)
pub unsafe fn shrink(&mut self, ptr: *mut u8, layout: Layout, new_size: usize)
Shrink an allocation to new_size.
This function is infallible given valid inputs, and the reallocation will always be done in-place, maintaining the validity of the pointer.
§Safety
ptrmust have been previously allocated or reallocated givenlayout.new_sizemust be smaller or equal tolayout.size().new_sizemust be nonzero.
Sourcepub unsafe fn try_realloc_in_place(
&mut self,
ptr: *mut u8,
layout: Layout,
new_size: usize,
) -> bool
pub unsafe fn try_realloc_in_place( &mut self, ptr: *mut u8, layout: Layout, new_size: usize, ) -> bool
Attempt to change the size of an allocation without copying memory.
The return value indicates whether the operation was successful.
This just calls shrink or try_grow_in_place
depending on whether new_size is larger or smaller.
If new_size <= layout.size(), then this will always succeed.
§Safety
ptrmust have been previously allocated or reallocated givenlayout.new_sizemust be nonzero.
Sourcepub const fn new(source: S) -> Self
pub const fn new(source: S) -> Self
Create a new Talc. See Talc’s documentation for more info on it.
You won’t typically want to use Talc directly. Consider:
- The cell-like
TalcCell, for single-threaded allocation. Intended for use with theAllocatorAPI. - The lock-based synchronized
TalcLock, for multi-threaded allocation. Intended for use as a global allocator.
TalcSyncCell is also available, if required.
As allocators are called with shared references, iterior mutability is required,
but Talc doesn’t force any particular form of interior mutability to be used
so different wrappers provide different trade-offs.
If the the wrapper types above don’t quite fit your use-case, making your
own may be best.
Sourcepub fn is_metadata_established(&self) -> bool
pub fn is_metadata_established(&self) -> bool
Indicates whether self has already established its allocator metadata into a heap.
§When is this the case?
A successful call to Talc::claim has not been made.
§What does this imply?
If metadata has not been established, the Talc::claim requires a larger arena to succeed.
See min_first_heap_size and min_first_heap_layout.
If metadata has been extablished, then the heap size requirement is much lower.
See Talc::claim for more details.
§How should I use this?
It’s most useful to ensure enough memory is being claimed
in Source implementations. If you’re not implementing Source, either
the Source implementation you’re using will take care of it for you, or
you’ll be claiming memory manually and will know when to consider
the extra requirement.
Use min_first_heap_size or min_first_heap_layout.
§Why is this mechanism the way it is
Sourcepub unsafe fn claim(
&mut self,
base: *mut u8,
size: usize,
) -> Option<NonNull<u8>>
pub unsafe fn claim( &mut self, base: *mut u8, size: usize, ) -> Option<NonNull<u8>>
Establish a new heap to allocate into.
This does not “combine” with neighboring heaps. Use Talc::extend to achieve this.
Due to alignment requirements, the resulting heap may be slightly smaller than the provided memory on either side.
§Failure modes
The first heap needs to hold Talc’s allocation metadata,
this has a fixed size that depends on the Binning configuration.
Currently, it’s a little over BIN_COUNT * PTR_SIZE
but this is subject to change.
Use min_first_heap_layout or
min_first_heap_size to guarantee a
successful first claim.
Once the first heap is established, the allocation metadata permanently
reserves the start of that heap and all subsequent claims are subject to
a much less stringent requirement: None is returned only if size is too
small to tag the base and have enough left over to fit a chunk.
§Safety
The region of memory described by base and size must not be mutated externally
up until the memory is released with Talc::truncate or Talc::resize
or the allocator is no longer active.
- This rule does not apply to memory that is allocated by
self. That’s the caller’s memory until deallocated. - This rule does not apply to memory after the returned pointer, that’s unclaimed.
The Source must not forbid manual heap management, otherwise this can cause UB.
(The Source implementation will clearly state this in its documentation.)
§Example
static mut ARENA: [u8; 5000] = [0; 5000];
let talc = TalcCell::new(Manual);
let arena = unsafe { talc.claim((&raw mut ARENA).cast(), 5000).unwrap() };Sourcepub unsafe fn reserved(&self, heap_end: NonNull<u8>) -> Reserved
pub unsafe fn reserved(&self, heap_end: NonNull<u8>) -> Reserved
Obtain information about the reserved region of a heap.
Memory in the arena is reserved if there is allocated memory above/within it.
The reserved part of a heap cannot be released using Talc::truncate or Talc::resize.
---------------- Linear Memory ----------------
├──Heap───────────────────────────────────┤
────┬─────┬───────────┬─────┬───────────┬─────┬────
... | Gap | Allocated | Gap | Allocated | Gap | ...
────┴─────┴───────────┴─────┴───────────┴─────┴────
├──Reserved─────────────────────────┤
§Return Value
See Reserved. In short, this function indicates where the top of
the reserved portion of the heap is, and whether any of the heap is reserved.
(If none of the heap is reserved, the “top of the reserved portion” is the bottom of the heap.)
Talc::truncate and Talc::resize will not release bytes below
the top of the reserved region.
(You can pass null into these functions and they’ll truncate down to the reserved region,
but no further.)
§Atomicity
Be aware that the reserved region may change before you use the info if you don’t own the allocator or hold a lock on it.
However, you can use Talc::truncate and Talc::resize correctly without
consulting this value at all, as they respect the reserved region automatically.
§Safety
heap_endmust have been previously acquired from this instance ofTalcand has not since changed. (e.g. resizing the heap and then using an oldheap_endpointer is an error.)
Sourcepub unsafe fn extend(
&mut self,
heap_end: NonNull<u8>,
new_end: *mut u8,
) -> NonNull<u8>
pub unsafe fn extend( &mut self, heap_end: NonNull<u8>, new_end: *mut u8, ) -> NonNull<u8>
Extend the heap’s end from heap_end to new_end.
Due to alignment requirements, the resulting pointer indicating the top of the heap
may not quite reach new_end.
The difference will be less than CHUNK_UNIT.
If new_end - heap_end isn’t large enough (less than a CHUNK_UNIT),
this call does nothing, returning heap_end.
§Safety
arenamust be managed by this instance of the allocator.- The memory in
heap_end..new_endmust be exclusively writeable by this instance of the allocator for the lifetimearenaunless truncated away or the allocator is no longer active.- Note that any memory above the returned pointer is unclaimed by the allocator and not subject to this requirement.
- Note that any memory in the heap that is allocated by
selflater on is also not subject to this requirement for the duration of the allocation’s lifetime (this is your memory that you allocated; use it).
- The
Sourcemust not forbid manual heap management, otherwise this can cause UB. (TheSourceimplementation will clearly state this in its documentation.)
§Example
static mut ARENA: [u8; 5000] = [0; 5000];
use talc::{*, source::*};
let talc = TalcCell::new(Manual);
let mut heap_end = unsafe { talc.claim((&raw mut ARENA).cast(), 2500).unwrap() };
unsafe { talc.extend(heap_end, ARENA.as_mut_ptr_range().end) };Sourcepub unsafe fn truncate(
&mut self,
heap_end: NonNull<u8>,
new_end: *mut u8,
) -> Option<NonNull<u8>>
pub unsafe fn truncate( &mut self, heap_end: NonNull<u8>, new_end: *mut u8, ) -> Option<NonNull<u8>>
Reduce the heap’s extent from heap_end to new_end.
Returns the new heap end, or otherwise None if the heap would be
empty or too small to allocate into (less than a CHUNK_UNIT), and is thus deleted.
If new_end is greater or equal to heap_end, this does nothing and returns heap_end.
The extent cannot be reduced further than what is indicated
by Talc::reserved. Attempting to do so (e.g. setting new_end to null_mut)
will truncate as much as valid (i.e. down to the reserved region).
Due to alignment requirements, the resulting heap end
might be slightly lower than requested
by a difference of less than CHUNK_UNIT.
All memory between the resulting pointer and heap_end, if any,
is released back to the caller. You no longer need to guarantee that
unallocated memory in this region is not mutated.
(This is relevant to the safety contract of Talc::claim and Talc::extend.)
§Safety
- The heap must be managed by this instance of the allocator.
heap_endmust have been previously returned as an arena end by this allocator, and not subsequently modified. i.e. it must be the up-to-date arena end.- The
Sourcemust not forbid manual heap management, otherwise this can cause UB. (TheSourceimplementation will clearly state this in its documentation.)
§Example
static mut ARENA: [u8; 5000] = [0; 5000];
let mut talc = TalcCell::new(Manual);
let end = unsafe { talc.claim((&raw mut ARENA).cast(), ARENA.len()).unwrap() };
// do some allocator operations...
// reclaim as much of the arena as possible
let opt_new_end = unsafe { talc.truncate(end, null_mut()) };Sourcepub unsafe fn resize(
&mut self,
heap_end: NonNull<u8>,
new_end: *mut u8,
) -> Option<NonNull<u8>>
pub unsafe fn resize( &mut self, heap_end: NonNull<u8>, new_end: *mut u8, ) -> Option<NonNull<u8>>
This calles Talc::extend or Talc::truncate depending on whether new_end
is higher or lower than heap_end.
This is just a convenience function.
See Talc::extend and Talc::truncate for details.