Is this Allocator trick correct?

I implemented two thread-local bump allocators, Temp and Local, which free the underlying storage when a thread terminates. Previously I was checking dynamically on deallocation that the pointer was in range. Now I am using a trick, which is to make Temp and Local !Send and !Sync, using PhantomData.

/// Local Allocator.
#[derive(Default, Clone)]
pub struct Local
{
    _x : PhantomData<NonNull<()>> // To make Local !Send
}

The idea is that anything allocated from Temp or Local can no longer be deallocated by a different thread, making the deallocate check uneccessary to achieve safety.

I think this should work, but I wanted to get a second opinion.

Local is defined here: Local in rustdb::alloc - Rust

I think your code is unsound.

You seem to be putting the actual allocator for Local inside a thread-local variable named LA. Someone might make a Box<T, Local>, and then put this box into their own thread-local variable. Then, when the thread terminates, destructors of the thread-local variables might be ran in some non-specified order. If the LA thread-local variable gets destroyed before the user's thread-local variable, then you get a use-after-free.

There is a counter which tracks the number of outstanding allocations. The destructor aborts the process if the counter is not zero. I think that prevents any use-after-free.

The reason for making Local !Send and !Sync is to make sure the counter is correct, that is the de-allocations really are for values allocated by the current thread and not another one.

The trick is slightly restrictive, as it doesn’t allow a value to be sent to another thread for processing, and then to be returned to the original thread before de-allocation. But in exchange, de-allocation is faster, as the dynamic check is not needed.

Ah ok. In that case, I think your trick works.

1 Like