diff -r 68b84643dffd Objects/obmalloc.c --- a/Objects/obmalloc.c Sat Apr 16 07:36:08 2016 +0000 +++ b/Objects/obmalloc.c Sat Apr 16 01:39:07 2016 -0700 @@ -723,26 +723,27 @@ /* * Locking * - * To reduce lock contention, it would probably be better to refine the - * crude function locking with per size class locking. I'm not positive - * however, whether it's worth switching to such locking policy because - * of the performance penalty it might introduce. + * The GIL protects access to memory allocation; the allocator doesn't need + * its own lock. However, the memory allocator has been annotated with + * LOCK/UNLOCK calls. These macros are maintained for experimentation + * and possible future use. We try to keep them current--but we make no + * guarantees, as they aren't actually used. * - * The following macros describe the simplest (should also be the fastest) - * lock object on a particular platform and the init/fini/lock/unlock - * operations on it. The locks defined here are not expected to be recursive - * because it is assumed that they will always be called in the order: - * INIT, [LOCK, UNLOCK]*, FINI. + * To use, simply redefine LOCK and UNLOCK below however you like. + * The lock you provide need not be recursive. + * + * Note that there's no init/shutdown facility. That's because obmalloc + * itself has no startup/shutdown. If your lock needs startup or shutdown + * code you'll have to do that in another place. + * + * Right now the interface is a bit simple. If you experience a lot of + * lock contention, you might consider modifying the lock support to make + * it more fine-grained--for example, one lock per size class. */ -/* - * Python's threads are serialized, so object malloc locking is disabled. - */ -#define SIMPLELOCK_DECL(lock) /* simple lock declaration */ -#define SIMPLELOCK_INIT(lock) /* allocate (if needed) and initialize */ -#define SIMPLELOCK_FINI(lock) /* free/destroy an existing lock */ -#define SIMPLELOCK_LOCK(lock) /* acquire released lock */ -#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */ +#define LOCK() +#define UNLOCK() + /* When you say memory, my mind reasons in terms of (pointers to) blocks */ typedef uchar block; @@ -816,15 +817,6 @@ /*==========================================================================*/ /* - * This malloc lock - */ -SIMPLELOCK_DECL(_malloc_lock) -#define LOCK() SIMPLELOCK_LOCK(_malloc_lock) -#define UNLOCK() SIMPLELOCK_UNLOCK(_malloc_lock) -#define LOCK_INIT() SIMPLELOCK_INIT(_malloc_lock) -#define LOCK_FINI() SIMPLELOCK_FINI(_malloc_lock) - -/* * Pool table -- headed, circular, doubly-linked lists of partially used pools. This is involved. For an index i, usedpools[i+i] is the header for a list of @@ -1020,7 +1012,11 @@ Py_ssize_t _Py_GetAllocatedBlocks(void) { - return _Py_AllocatedBlocks; + Py_ssize_t allocated; + LOCK(); + allocated = _Py_AllocatedBlocks; + UNLOCK(); + return allocated; } @@ -1264,8 +1260,6 @@ poolp next; uint size; - _Py_AllocatedBlocks++; - assert(nelem <= PY_SSIZE_T_MAX / elsize); nbytes = nelem * elsize; @@ -1294,6 +1288,7 @@ ++pool->ref.count; bp = pool->freeblock; assert(bp != NULL); + _Py_AllocatedBlocks++; if ((pool->freeblock = *(block **)bp) != NULL) { UNLOCK(); if (use_calloc) @@ -1399,6 +1394,7 @@ */ bp = pool->freeblock; assert(bp != NULL); + _Py_AllocatedBlocks++; pool->freeblock = *(block **)bp; UNLOCK(); if (use_calloc) @@ -1413,6 +1409,7 @@ pool->szidx = size; size = INDEX2SIZE(size); bp = (block *)pool + POOL_OVERHEAD; + _Py_AllocatedBlocks++; pool->nextoffset = POOL_OVERHEAD + (size << 1); pool->maxnextoffset = POOL_SIZE - size; pool->freeblock = bp + size; @@ -1464,8 +1461,11 @@ result = PyMem_RawCalloc(nelem, elsize); else result = PyMem_RawMalloc(nbytes); - if (!result) - _Py_AllocatedBlocks--; + if (result) { + LOCK(); + _Py_AllocatedBlocks++; + UNLOCK(); + } return result; } } @@ -1499,8 +1499,6 @@ if (p == NULL) /* free(NULL) has no effect */ return; - _Py_AllocatedBlocks--; - #ifdef WITH_VALGRIND if (UNLIKELY(running_on_valgrind > 0)) goto redirect; @@ -1519,6 +1517,7 @@ assert(pool->ref.count > 0); /* else it was empty */ *(block **)p = lastfree = pool->freeblock; pool->freeblock = (block *)p; + _Py_AllocatedBlocks--; if (lastfree) { struct arena_object* ao; uint nf; /* ao->nfreepools */ @@ -1706,6 +1705,9 @@ #endif /* We didn't allocate this address. */ PyMem_RawFree(p); + LOCK(); + _Py_AllocatedBlocks--; + UNLOCK(); } /* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, @@ -1774,15 +1776,28 @@ * at p. Instead we punt: let C continue to manage this block. */ if (nbytes) - return PyMem_RawRealloc(p, nbytes); - /* C doesn't define the result of realloc(p, 0) (it may or may not - * return NULL then), but Python's docs promise that nbytes==0 never - * returns NULL. We don't pass 0 to realloc(), to avoid that endcase - * to begin with. Even then, we can't be sure that realloc() won't - * return NULL. - */ - bp = PyMem_RawRealloc(p, 1); - return bp ? bp : p; + bp = PyMem_RawRealloc(p, nbytes); + else { + /* C doesn't define the result of realloc(p, 0) (it may or may not + * return NULL then), but Python's docs promise that nbytes==0 never + * returns NULL. We don't pass 0 to realloc(), to avoid that endcase + * to begin with. Even then, we can't be sure that realloc() won't + * return NULL. + */ + bp = PyMem_RawRealloc(p, 1); + bp = bp ? bp : p; + } + + if (!p && bp) { + /* + * If you pass in NULL, and get back allocated memory, + * then this is really a new allocation. + */ + LOCK(); + _Py_AllocatedBlocks++; + UNLOCK(); + } + return bp; } #else /* ! WITH_PYMALLOC */