The rust-for-linux project have their own allocator API including a custom allocator trait. I'm working on some modifications to the Allocator trait that would enable their use-cases. Essentially, what they need is a way to pass some flags at each (possible) allocation. For instance:
Context as associated type on Allocator
Add an associated type for additional context passed into (re)allocating functions.
pub unsafe trait Allocator {
type Ctx: Copy;
fn allocate(&self, context: Self::Ctx, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
fn allocate_zeroed(&self, context: Self::Ctx, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
unsafe fn grow(
&self,
context: Self::Ctx,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn grow_zeroed(
&self,
context: Self::Ctx,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn shrink(
&self,
context: Self::Ctx,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
fn by_ref(&self) -> &Self where Self: Sized { self }
}
// Support storing the context alongside the allocator
unsafe impl<A: Allocator> Allocator for (A, A::Ctx) {
type Ctx = ();
fn allocate(&self, _context: (), layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.0.allocate(self.1, layout)
}
...
}
Then for each heap container, we would have a copy of each (re)allocating function that takes a generic context:
pub struct Vec<T, A = Global> { ... }
// Normal `push` without context
impl<T, A: Allocator<Ctx = ()>> Vec<T, A> {
pub fn push(&mut self, elem: T) { ... }
}
// `cpush` with context
impl<T, A: Allocator> Vec<T, A> {
pub fn cpush(&mut self, elem: T, context: A::Ctx) { ... }
}
rust-for-linux folks would have to use cpush
Could even use #![feature(default_associated_types)] to make Ctx default to (). Though since the allocator API is unstable, this isn't necessary.
Variant: Ctx as a trait generic
You could make Ctx a type parameter of the trait instead:
pub unsafe trait Allocator<Ctx> { ... }
But I fail to see a case where an allocator would want to support multiple types of context.
This would also require that every heap type wanting to generically support allocators taking context would need to add a PhantomData<Ctx> to their struct.
Questions
- Should
Ctx be passed by reference?
Then we could remove the Copy bound, but people could also just set Ctx = &Something.
Problems
- Duplicated API surface:
push, reserve, etc would all need to be duplicated with a context-taking variant
The only way to avoid this is some new language feature, such as making final () arguments optional or calculating disjointness based on associated types.
- Restricted API surface:
Extend, etc can only be implemented for containers where Ctx = ()
This could possibly be mitigated by providing ways to bundle or split up the allocator and context, for instance:
impl<T, A, Ctx> Vec<T, A>
where
A: Allocator<Ctx = Ctx>,
(A, Ctx): Allocator<Ctx = ()>,
{
pub fn to_bundled_context(self, context: Ctx) -> Vec<T, (A, Ctx)>;
}
impl<T, A, Ctx> Vec<T, (A, Ctx)>
where
A: Allocator<Ctx = Ctx>,
(A, Ctx): Allocator<Ctx = ()>,
{
pub fn to_split_context(self) -> (Vec<T, A>, Ctx);
}
// Usage
fn takes_extend<E: Extend<u32>>(e: &mut E);
let mut v = v.to_bundled_context(context);
takes_extend(&mut v);
let (mut v, context) = v.to_split_context();
The rust-for-linux project have their own allocator API including a custom allocator trait. I'm working on some modifications to the
Allocatortrait that would enable their use-cases. Essentially, what they need is a way to pass some flags at each (possible) allocation. For instance:Context as associated type on
AllocatorAdd an associated type for additional context passed into (re)allocating functions.
Then for each heap container, we would have a copy of each (re)allocating function that takes a generic context:
rust-for-linux folks would have to use
cpushCould even use
#![feature(default_associated_types)]to makeCtxdefault to(). Though since the allocator API is unstable, this isn't necessary.Variant:
Ctxas a trait genericYou could make
Ctxa type parameter of the trait instead:But I fail to see a case where an allocator would want to support multiple types of context.
This would also require that every heap type wanting to generically support allocators taking context would need to add a
PhantomData<Ctx>to their struct.Questions
Ctxbe passed by reference?Then we could remove the
Copybound, but people could also just setCtx = &Something.Problems
push,reserve, etc would all need to be duplicated with a context-taking variantThe only way to avoid this is some new language feature, such as making final
()arguments optional or calculating disjointness based on associated types.Extend, etc can only be implemented for containers whereCtx = ()This could possibly be mitigated by providing ways to bundle or split up the allocator and context, for instance: