Date: 2000-05-15 09:53:57
> 1) The pools destructor enumerates through all the elements and deletes
> those that haven't been deleted - potentially this could be quite an
> expensive operation - it's also breaks std::allocators requirements (see
As Dave Abrahams pointed out, this is intended behaviour. However, for your
convenience (and mine), pool<T> is built on sized_pool<N>, which handles
everything necessary for non-type-dependent memory pools (without destructor
I don't *think* it breaks the allocator requirements. If your're worried
that elements might be deleted by pool<T> before the container's done with
them, see below.
> 2) The static allocator version (std_pool_allocator I think) uses a static
> member for the pool - this efectively prevents this allocator from being
> used in program startup/exit code - as would happen if you declare a
> variable using this allocator for example, the normal solution for this
> situation is to use a singleton: boost::singleton anyone?
I'm not sure what the problem is here. The order of initialization?
> 3) The allocator requirements permit containers to allocate memory with
> allocator and free with another un-related allocator (think of
> for example), in fact the lifetime of allocated memory can be independent
> of that of the allocator, I think this kind of usage would crash your
> allocators (due in part to #1)?
As Nathan Myers has pointed out, the allocator requirements don't quite line
up with what the containers require. That's why there's two allocators:
pool_allocator, which is an instance-based allocator, satisfying Allocator
requirements; and std_pool_allocator, which is not instance-based, and
allows usage with std:: supplied containers [20.1.5/4]. std_pool_allocator
is the only allocator that is officially permitted for use with std::
containers (and it does garbage cleanup only at the end of the program).
I took pains to avoid this kind of problem -- that's where the extra
template parameter on the allocators comes from. The last parameter is the
*original* type of the allocator; if the allocator is rebound to another
type, it *won't* use the pool. But if it is rebound *again*, back to the
original type, then it will use the pool again. Kind of convoluted, but it
Because of this, pool allocators are (as they currently stand) simply not
usable. See below for my plans to change this.
> 4) The allocators aren't thread safe - this is very important from my
> of view, but opens up a whole can of worms you may not want to get into.
I can't believe I overlooked this!
I'll probably just look at the way others have done it in the smart pointers
(I'm assuming we used some sort of wrapper around OS-specific mutexes), and
do it the same way.
> 5) It would be nice to see some performance comparisons between the pooled
> allocators and std::allocator/operator new
Yes, but for as many compilers/stdlib vendors as possible. Memory
allocation strategies may vary widely. Maybe we could provide a "benchmark"
--- Future Direction ---
As pointed out in my response to (3), above, the pool allocators are useless
when applied to containers. I'm thinking of having the "standard" allocator
not depend on pool<T> anymore, but rather on sized_pool<N>, and dropping the
instance-based allocator completely. The allocator will lose its "garbage
collection" properties, but I don't really see that as necessary for
allocators, anyway. Then the allocator can always allocate from
sized_pool's, even if rebound.
That will still leave pool<T>, for those who want to have garbage collection
In the mean time: I will look at thread safety issues, and could you, John,
explicitly state your concerns with static data members? (I'm a little slow
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk