pub unsafe trait Source: Debug + Sized {
const TRACK_HEAP_END: bool = false;
// Required method
fn acquire<B: Binning>(
talc: &mut Talc<Self, B>,
layout: Layout,
) -> Result<(), ()>;
// Provided method
unsafe fn resize(
&mut self,
chunk_base: *mut u8,
heap_end: *mut u8,
is_heap_base: bool,
) -> *mut u8 { ... }
}Expand description
Acquire and manage heaps of memory for Talc to utilize.
A Source implementation has two parts:
-
Source::acquireis called whenTalcneeds more memory. The source is expected to use heap management APIs such asclaim/extendto achieve this. -
Optionally, implementing
Source::resizeallows for automatically managing heaps with free space available to be reclaimed. SetSource::TRACK_HEAP_ENDtotrueto enable this.
§The old OomHandler way
Just implement Source::acquire as usual and ignore resize. The API is almost the same.
§Safety
Do not use the parent TalcLock or TalcCell
in the Source implementation.
- The former will deadlock.
- The latter will result in a panic if debug assertion are enabled, or else undefined behavior.
The main cause for concern is using the global allocator, directly or
indirectly, if the global allocator might be the parent TalcLock or
TalcSyncCell.
For examples of how this could go wrong:
- Calling
println!ordbg!or a similar operation in theSourceimplementation which may allocate. This would make theSourceimplementation unusable in global allocators. - Manipulating a
Vecof metadata, as this may allocate to the same effect. (Linked lists are generally more applicable for memory management.)
If dynamic memory is needed, make sure to pre-allocate it beforehand, or have a known-distinct allocator.
§Drop implementation
Keep in mind that if the Source is keeping track of heaps associated with
resources such as mmap’d system memory, it’s a good idea to implement Drop
on a Source implementation such that when the allocator is disposed of,
the resources can be disposed of properly in turn.
Provided Associated Constants§
Sourceconst TRACK_HEAP_END: bool = false
const TRACK_HEAP_END: bool = false
Configures whether Talc tracks the end of heaps.
If this is true, then Talc will recognize when it’s working with
a
This must be true for the Source::resize implementation to have any effect.
Because tracking the end of the heap incurs some overhead,
leave this as false if you don’t need to automatically reduce the
size of the heap.
Note that this does not allow for querying the ends of heaps.
This just means that Talc “knows it when it sees it” and can
call Source::resize to provide implementors
the opportunity to change the size of the heap if they’d like.
See Source::resize for more details on that.
Required Methods§
Sourcefn acquire<B: Binning>(
talc: &mut Talc<Self, B>,
layout: Layout,
) -> Result<(), ()>
fn acquire<B: Binning>( talc: &mut Talc<Self, B>, layout: Layout, ) -> Result<(), ()>
The allocator ran out of available memory and has thus called Source::acquire
your options are as follows:
- use
Talc::claimorTalc::extendto establish/extend the amount of memory available fortalcto allocate from, and then returnOk(()) - allow allocation failure to occur by returning
Err(()), which is typically a last resort
If Ok(()) is returned, but the allocator still finds itself without sufficient memory,
Source::acquire is invoked again.
Therefore an infinite loop will occur if Ok(()) is repeatedly returned
without extending or claiming new memory. To avoid this, don’t return Ok(())
if no additional memory has been made available to the allocator.
§Statefulness
The source may be stateful.
Use talc.source to access the data associated with the Source implementation.
§Safety, Panicking, and Deadlocking
Implementors of Source must be vigilant about not interacting
with the busy-allocating instance of Talc through anything except
the provided mutable reference, talc.
Implementors must uphold that they do not interact with the TalcLock
or TalcCell that wraps the provided Talc.
This includes indirect calls to the global allocator (which might be a wrapper around Talc)
because of something like println! or dbg! or Vec::with_capacity.
See Source’s documentation for more information.
Provided Methods§
Sourceunsafe fn resize(
&mut self,
chunk_base: *mut u8,
heap_end: *mut u8,
is_heap_base: bool,
) -> *mut u8
unsafe fn resize( &mut self, chunk_base: *mut u8, heap_end: *mut u8, is_heap_base: bool, ) -> *mut u8
The allocator has released memory near the top of the heap.
As a result, Talc called Source::resize to give the source an opportunity
to resize the heap. This effectively gives the implementation an opportunity
to reclaim the memory (or claim more memory, if desirable for some reason).
The return value is where the new top of the heap will be.
This function is never called if Source::TRACK_HEAP_END is false.
To set the stage:
chunk_baseis the top of the reserved region. SeeTalc::reserved. In short, it’s either the pointer above the last allocated byte or the base of the heap.heap_endis the top of the heap. This is what you’re adjusting up or down.is_heap_baseis whetherchunk_baseis actually the base of the entire heap.
Implementation details for Source implementations.
chunk_baseis aligned toCHUNK_UNITheap_endis aligned toCHUNK_UNIT- If
is_heap_base:base + 1 <= chunk_base <= base + CHUNK_UNIT
§Performance
If Source::TRACK_HEAP_END is true then Talc will call Source::resize
whenever the free space at the end of a heap gets larger.
This will result in quite a lot of calls!
So avoid doing anything expensive until you know that there’s a decent amount
of memory to free up, or whatever you’re planning to do.
For example, you’ll typically want a quick check at the start, e.g.
if heap_end as usize - chunk_base as usize > PAGE_SIZE
before doing any real work.
§Safety
Callers must guarantee that
chunk_baseandheap_endis aligned toCHUNK_UNITis_heap_basemust be a true reflection of whether the heap will be deleted if the caller returnschunk_base
Implementors must guarantee that
- The returned pointer is greater than or equal to
chunk_base - The returned pointer is aligned to
CHUNK_UNIT - The memory within
heap_end..return_ptr, if any, is subject to the same safety contract asTalc::extend
Note that this constitutes a resizing of the heap, and a change of the heap end,
as far as Talc::extend/Talc::truncate/Talc::resize are concerned,
and thus this affects their safety contracts.
§Do not use manual heap management
Source::resize implementations should not call heap management functions:
Talc::claim, Talc::extend, Talc::truncate, and Talc::resize.
Instead, the extent of the heap being worked on is entirely controlled by
the returned pointer.
Note that this function does not receive a pointer to the Talc instance,
just a reference to the Source instance. This is intentional.
§Consider forbidding the user from doing manual heap management
Typically, Source::resize implementations rely on metadata around the heaps they manage.
Unless the user manually takes care to replicate the metadata that the Source
implementation maintains about the heaps it manages, then manual heap management
while a resizing-Source-implementation is active will probably lead to
erroneous memory accessed and UB.
If manual heap management use may lead to UB, document this clearly on the implementation struct’s docs.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".