On 07/03/2015 03:07 PM, Richard Sandiford wrote: > Martin Jambor <mjam...@suse.cz> writes: >> On Fri, Jul 03, 2015 at 09:55:58AM +0100, Richard Sandiford wrote: >>> Trevor Saunders <tbsau...@tbsaunde.org> writes: >>>> On Thu, Jul 02, 2015 at 09:09:31PM +0100, Richard Sandiford wrote: >>>>> Martin Liška <mli...@suse.cz> writes: >>>>>> diff --git a/gcc/asan.c b/gcc/asan.c >>>>>> index e89817e..dabd6f1 100644 >>>>>> --- a/gcc/asan.c >>>>>> +++ b/gcc/asan.c >>>>>> @@ -362,20 +362,20 @@ struct asan_mem_ref >>>>>> /* Pool allocation new operator. */ >>>>>> inline void *operator new (size_t) >>>>>> { >>>>>> - return pool.allocate (); >>>>>> + return ::new (pool.allocate ()) asan_mem_ref (); >>>>>> } >>>>>> >>>>>> /* Delete operator utilizing pool allocation. */ >>>>>> inline void operator delete (void *ptr) >>>>>> { >>>>>> - pool.remove ((asan_mem_ref *) ptr); >>>>>> + pool.remove (ptr); >>>>>> } >>>>>> >>>>>> /* Memory allocation pool. */ >>>>>> - static pool_allocator<asan_mem_ref> pool; >>>>>> + static pool_allocator pool; >>>>>> }; >>>>> >>>>> I'm probably going over old ground/wounds, sorry, but what's the benefit >>>>> of having this sort of pattern? Why not simply have object_allocators >>>>> and make callers use pool.allocate () and pool.remove (x) (with >>>>> pool.remove >>>>> calling the destructor) instead of new and delete? It feels wrong to me >>>>> to tie the data type to a particular allocation object like this. >>>> >>>> Well the big question is what does allocate() do about construction? if >>>> it seems wierd for it to not call the ctor, but I'm not sure we can do a >>>> good job of forwarding args to allocate() with C++98. >>> >>> If you need non-default constructors then: >>> >>> new (pool) type (aaa, bbb)...; >>> >>> doesn't seem too bad. I agree object_allocator's allocate () should call >>> the constructor. >>> >> >> but then the pool allocator must not call placement new on the >> allocated memory itself because that would result in double >> construction. > > But we're talking about two different methods. The "normal" allocator > object_allocator <T>::allocate () would use placement new and return a > pointer to the new object while operator new (size_t, object_allocator <T> &) > wouldn't call placement new and would just return a pointer to the memory. > >>>>> And using the pool allocator functions directly has the nice property >>>>> that you can tell when a delete/remove isn't necessary because the pool >>>>> itself is being cleared. >>>> >>>> Well, all these cases involve a pool with static storage lifetime right? >>>> so actually if you don't delete things in these pool they are >>>> effectively leaked. >>> >>> They might have a static storage lifetime now, but it doesn't seem like >>> a good idea to hard-bake that into the interface >> >> Does that mean that operators new and delete are considered evil? > > Not IMO. Just that static load-time-initialized caches are not > necessarily a good thing. That's effectively what the pool > allocator is. > >>> (by saying that for >>> these types you should use new and delete, but for other pool-allocated >>> types you should use object_allocators). >> >> Depending on what kind of pool allocator you use, you will be forced >> to either call placement new or not, so the inconsistency will be >> there anyway. > > But how we handle argument-taking constructors is a problem that needs > to be solved for the pool-allocated objects that don't use a single > static type-specific pool. And once we solve that, we get consistency > across all pools: > > - if you want a new object and argumentless construction is OK, > use "pool.allocate ()" > > - if you want a new object and need to pass arguments to the constructor, > use "new (pool) some_type (arg1, arg2, ...)" > >>> Maybe I just have bad memories >>> from doing the SWITCHABLE_TARGET stuff, but there I was changing a lot >>> of state that was "obviously" static in the old days, but that needed >>> to become non-static to support vaguely-efficient switching between >>> different subtargets. The same kind of thing is likely to happen again. >>> I assume things like the jit would prefer not to have new global state >>> with load-time construction. >> >> I'm not sure I follow this branch of the discussion, the allocators of >> any kind surely can dynamically allocated themselves? > > Sure, but either (a) you keep the pools as a static part of the class > and some initialisation and finalisation code that has tendrils into > all such classes or (b) you move the static pool outside of the > class to some new (still global) state. Explicit pool allocation, > like in the C days, gives you the option of putting the pool whereever > it needs to go without relying on the principle that you can get to > it from global state. > > Thanks, > Richard >
Ok Richard. I've just finally understood your suggestions and I would suggest following: + I will add a new method to object_allocator<T> that will return an allocated memory (void*) (w/o calling any construction) + object_allocator<T>::allocate will call placement new with for a parameterless ctor + I will remove all overwritten operators new/delete on e.g. et_forest, ... + For these classes, I will add void* operator new (size_t, object_allocator<T> &) + Pool allocators connected to these classes will be back transformed to static variables and one would call new et_forest (my_et_forest_allocator) Please confirm that aforementioned changes make sense and I'll come up with patch. Thanks, Martin