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