[Python-Dev] Improving the Python Memory Allocator

Evan Jones ejones at uwaterloo.ca
Tue Jan 25 01:33:22 CET 2005


On Jan 24, 2005, at 18:16, Martin v. Löwis wrote:
> - please don't post patches here; post them to SF
>   You may ask for comments here after you posted them to SF.

Sure. This should be done even for patches which should absolutely not 
be committed?

> - please follow Python coding style. In particular, don't write
>     if ( available_arenas == NULL ) {
>   but write
>     if (available_arenas == NULL) {

Yikes! This is a "bad" habit of mine that is in the minority of coding 
style . Thank you for catching it.

>> Second, the previous allocator went out of its way to permit a module 
>> to call PyObject_Free while another thread is executing 
>> PyObject_Malloc. Apparently, this was a backwards compatibility hack 
>> for old Python modules which erroneously call these functions without 
>> holding the GIL. These modules will have to be fixed if this 
>> implementation is accepted into the core.
> I'm not certain it is acceptable to make this assumption. Why is it
> not possible to use the same approach that was previously used (i.e.
> leak the arenas array)?

This is definitely a very important point of discussion. The main 
problem is that leaking the "arenas" arena is not sufficient to make 
the memory allocator thread safe. Back in October, Tim Peters suggested 
that it might be possible to make the breakage easily detectable:

http://mail.python.org/pipermail/python-dev/2004-October/049502.html

> If we changed PyMem_{Free, FREE, Del, DEL} to map to the system
> free(), all would be golden (except for broken old code mixing
> PyObject_ with PyMem_ calls).  If any such broken code still exists,
> that remapping would lead to dramatic failures, easy to reproduce; and
> old code broken in the other, infinitely more subtle way (calling
> PyMem_{Free, FREE, Del, DEL} when not holding the GIL) would continue
> to work fine.

I'll be honest, I only have a theoretical understanding of why this 
support is necessary, or why it is currently correct. For example, is 
it possible to call PyMem_Free from two threads simultaneously? Since 
the problem is that threads could call PyMem_Free without holding the 
GIL, it seems to be that it is possible. Shouldn't it also be 
supported? In the current memory allocator, I believe that situation 
can lead to inconsistent state. For example, see obmalloc.c:746, where 
it has been determined that a block needs to be put on the list of free 
blocks:

		*(block **)p = lastfree = pool->freeblock;
		pool->freeblock = (block *)p;

Imagine two threads are simultaneously freeing blocks that belong to 
the same pool. They both read the same value for pool->freeblock, and 
assign that same value to p. The changes to pool->freeblock will have 
some arbitrary ordering. The result? You have just leaked a block of 
memory.

Basically, if a concurrent memory allocator is the requirement, then I 
think some other approach is necessary.

>> - When allocating a page, it is taken from the arena that is the most 
>> full. This gives arenas that are almost completely unused a chance to 
>> be freed.
> It would be helpful if that was documented in the data structures
> somewhere. The fact that the nextarena list is sorted by nfreepools
> is only mentioned in the place where this property is preserved;
> it should be mentioned in the introductory comments as well.

This is one of those rough edges I mentioned before. If there is some 
concensus that these changes should be accepted, then I will need to 
severely edit the comments at the beginning of obmalloc.c.

Thanks for your feedback,

Evan Jones



More information about the Python-Dev mailing list