[Python-Dev] RFC: PEP 445: Add new APIs to customize Python memory allocators

Nick Coghlan ncoghlan at gmail.com
Wed Jun 19 05:32:01 CEST 2013

On 19 June 2013 09:23, Scott Dial <scott+python-dev at scottdial.com> wrote:
> On 6/18/2013 4:40 PM, Victor Stinner wrote:
>> No context argument
>> -------------------
>> Simplify the signature of allocator functions, remove the context
>> argument:
>> * ``void* malloc(size_t size)``
>> * ``void* realloc(void *ptr, size_t new_size)``
>> * ``void free(void *ptr)``
>> It is likely for an allocator hook to be reused for
>> ``PyMem_SetAllocator()`` and ``PyObject_SetAllocator()``, or even
>> ``PyMem_SetRawAllocator()``, but the hook must call a different function
>> depending on the allocator. The context is a convenient way to reuse the
>> same custom allocator or hook for different Python allocators.
> I think there is a lack of justification for the extra argument, and the
> extra argument is not free. The typical use-case for doing this
> continuation-passing style is when the set of contexts is either
> unknown, arbitrarily large, or infinite. In other words, when it would
> be either impossible or impractical to enumerate all of the contexts.
> However, in this case, we have only 3.

Note that the context is part of the BlockAllocator structure, NOT
predefined by Python.

> Your proposal already puts forward having 3 pairs of Get/Set functions,
> so there is no distinct advantage in having a single typedef instance
> that you pass in to all 3 of them. And, having all 3 pairs use the same
> typedef is a bit of an attractive nuisance, in that one could pass the
> wrong allocators to the wrong setter. With that, I could argue that
> there should be 3 typedefs to prevent coding errors.

I'm not sure we *should* be restricting this to the CPython internal
domains indefinitely. If we use a domain based model from the start,
then that will allow us in the future to let extension modules declare
additional domains rather than having to employ library specific logic
in either the CPython core or in embedding applications.

> Nevertheless, the ctx argument buys the implementer nothing if they have
> to begin their alloc function with "if(ctx == X)". In other words, there
> is nothing simpler about:
> """
> void *_alloc(void *ctx, size_t size) {
>   if(ctx == PYALLOC_PYMEM)
>     return _alloc_pymem(size);
>   else if(ctx == PYALLOC_PYMEM_RAW)
>     return _alloc_pymem_raw(size);
>   else if(ctx == PYALLOC_PYOBJECT)
>     return _alloc_pyobject(size);
>   else
>     abort();
> }
> PyMemBlockAllocator pymem_allocator =
>   {.ctx=PYALLOC_PYMEM, .alloc=&_alloc, .free=&_free};
> PyMemBlockAllocator pymem_raw_allocator =
>   {.ctx=PYALLOC_PYMEM_RAW, .alloc=&_alloc, .free=&_free};
> PyMemBlockAllocator pyobject_allocator =
>   {.ctx=PYALLOC_PYOBJECT, .alloc=&_alloc, .free=&_free};
> """

Why would anyone do that? The context is so embedding applications can
distinguish the CPython runtime from their *other* domains that use
the same allocator functions. If you wanted to use completely
different allocators for each domain, you would just do that and
ignore the context argument entirely.

Agreed more of that rationale needs to be moved from the issue tracker
into the PEP, though.


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-Dev mailing list