I guess a fairly standard trick when implementing a custom free-list
allocator is to re-use the start of the user-data block for the "next"
pointer of the free list. This could look something like the following
template<typename T>
void allocator<T>::deallocate(void* userData)
{
// Assume T is big enough for a void*. Store the link to the next
// free block
*static_cast<void**>(userData) = freeList_;
// Make this block the head of freeList
freeList_ = userData;
}
I'm not sure if that's the exact signature for a standard allocator,
but I think the idea is clear. Unfortunately, this is non****table
because it can violate strict aliasing. Assume that we have
allocator<int>, and we do something like this:
int Foo::bar(int* data)
{
int result = *data;
allocator_.deallocate(data);
return result;
}
When the compiler inlines the deallocate function, we end up with
this:
int result = *data;
*static_cast<void **>(data) = allocator_.freeList_;
So we have two aliases to *data, one of which is int& and one of which
is void*&. We're trying to re-use the same location to store an int
*and* a void* at the same time. I remember seeing a case (with g++ and
its strict-aliasing optimizations) where the compiler reordered the
load and store of *data, meaning that the free list pointer overwrites
the user data before the user data gets read.
The example should probably destroy the int object as below, but from
memory this didn't affect the behaviour in the particular case I
saw...
int result = *data;
data->~int();
allocator_.deallocate(data);
So I guess I have a few questions, firstly should the version with the
destructor work ****tably, and if not, is there a ****table way to make
it work? How about if it doesn't call the destructor?
--
Cheers,
Raoul.
[ See http://www.gotw.ca/resources/clcm.htm
for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


|