From 6fdc4d37f3fdbc1bd51f841be6e5e4708a3b8798 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 4 May 2021 17:08:31 -0700 Subject: [PATCH] bpo-40521: Convert deque freelist from global vars to instance vars (GH-25906) --- Lib/test/test_deque.py | 3 +- Modules/_collectionsmodule.c | 59 +++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 93cc6ca4f44..f1a79373dec 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -740,8 +740,9 @@ class TestBasic(unittest.TestCase): @support.cpython_only def test_sizeof(self): + MAXFREEBLOCKS = 16 BLOCKLEN = 64 - basesize = support.calcvobjsize('2P4nP') + basesize = support.calcvobjsize('2P5n%dPP' % MAXFREEBLOCKS) blocksize = struct.calcsize('P%dPP' % BLOCKLEN) self.assertEqual(object.__sizeof__(deque()), basesize) check = self.check_sizeof diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 9c8701af904..79c6b5752af 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -30,6 +30,7 @@ static PyTypeObject tuplegetter_type; #define BLOCKLEN 64 #define CENTER ((BLOCKLEN - 1) / 2) +#define MAXFREEBLOCKS 16 /* Data for deque objects is stored in a doubly-linked list of fixed * length blocks. This assures that appends or pops never move any @@ -92,6 +93,8 @@ typedef struct { Py_ssize_t rightindex; /* 0 <= rightindex < BLOCKLEN */ size_t state; /* incremented whenever the indices move */ Py_ssize_t maxlen; /* maxlen is -1 for unbounded deques */ + Py_ssize_t numfreeblocks; + block *freeblocks[MAXFREEBLOCKS]; PyObject *weakreflist; } dequeobject; @@ -123,16 +126,12 @@ static PyTypeObject deque_type; added at about the same rate as old blocks are being freed. */ -#define MAXFREEBLOCKS 16 -static Py_ssize_t numfreeblocks = 0; -static block *freeblocks[MAXFREEBLOCKS]; - -static block * -newblock(void) { +static inline block * +newblock(dequeobject *deque) { block *b; - if (numfreeblocks) { - numfreeblocks--; - return freeblocks[numfreeblocks]; + if (deque->numfreeblocks) { + deque->numfreeblocks--; + return deque->freeblocks[deque->numfreeblocks]; } b = PyMem_Malloc(sizeof(block)); if (b != NULL) { @@ -142,12 +141,12 @@ newblock(void) { return NULL; } -static void -freeblock(block *b) +static inline void +freeblock(dequeobject *deque, block *b) { - if (numfreeblocks < MAXFREEBLOCKS) { - freeblocks[numfreeblocks] = b; - numfreeblocks++; + if (deque->numfreeblocks < MAXFREEBLOCKS) { + deque->freeblocks[deque->numfreeblocks] = b; + deque->numfreeblocks++; } else { PyMem_Free(b); } @@ -164,7 +163,7 @@ deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (deque == NULL) return NULL; - b = newblock(); + b = newblock(deque); if (b == NULL) { Py_DECREF(deque); return NULL; @@ -180,6 +179,7 @@ deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds) deque->rightindex = CENTER; deque->state = 0; deque->maxlen = -1; + deque->numfreeblocks = 0; deque->weakreflist = NULL; return (PyObject *)deque; @@ -204,7 +204,7 @@ deque_pop(dequeobject *deque, PyObject *unused) if (Py_SIZE(deque)) { prevblock = deque->rightblock->leftlink; assert(deque->leftblock != deque->rightblock); - freeblock(deque->rightblock); + freeblock(deque, deque->rightblock); CHECK_NOT_END(prevblock); MARK_END(prevblock->rightlink); deque->rightblock = prevblock; @@ -242,7 +242,7 @@ deque_popleft(dequeobject *deque, PyObject *unused) if (Py_SIZE(deque)) { assert(deque->leftblock != deque->rightblock); prevblock = deque->leftblock->rightlink; - freeblock(deque->leftblock); + freeblock(deque, deque->leftblock); CHECK_NOT_END(prevblock); MARK_END(prevblock->leftlink); deque->leftblock = prevblock; @@ -278,7 +278,7 @@ static inline int deque_append_internal(dequeobject *deque, PyObject *item, Py_ssize_t maxlen) { if (deque->rightindex == BLOCKLEN - 1) { - block *b = newblock(); + block *b = newblock(deque); if (b == NULL) return -1; b->leftlink = deque->rightblock; @@ -315,7 +315,7 @@ static inline int deque_appendleft_internal(dequeobject *deque, PyObject *item, Py_ssize_t maxlen) { if (deque->leftindex == 0) { - block *b = newblock(); + block *b = newblock(deque); if (b == NULL) return -1; b->rightlink = deque->leftblock; @@ -584,7 +584,7 @@ deque_clear(dequeobject *deque) adversary could cause it to never terminate). */ - b = newblock(); + b = newblock(deque); if (b == NULL) { PyErr_Clear(); goto alternate_method; @@ -623,13 +623,13 @@ deque_clear(dequeobject *deque) itemptr = leftblock->data; limit = itemptr + m; n -= m; - freeblock(prevblock); + freeblock(deque, prevblock); } item = *(itemptr++); Py_DECREF(item); } CHECK_END(leftblock->rightlink); - freeblock(leftblock); + freeblock(deque, leftblock); return 0; alternate_method: @@ -679,7 +679,7 @@ deque_inplace_repeat(dequeobject *deque, Py_ssize_t n) deque->state++; for (i = 0 ; i < n-1 ; ) { if (deque->rightindex == BLOCKLEN - 1) { - block *b = newblock(); + block *b = newblock(deque); if (b == NULL) { Py_SET_SIZE(deque, Py_SIZE(deque) + i); return NULL; @@ -797,7 +797,7 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n) while (n > 0) { if (leftindex == 0) { if (b == NULL) { - b = newblock(); + b = newblock(deque); if (b == NULL) goto done; } @@ -841,7 +841,7 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n) while (n < 0) { if (rightindex == BLOCKLEN - 1) { if (b == NULL) { - b = newblock(); + b = newblock(deque); if (b == NULL) goto done; } @@ -885,7 +885,7 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n) rv = 0; done: if (b != NULL) - freeblock(b); + freeblock(deque, b); deque->leftblock = leftblock; deque->rightblock = rightblock; deque->leftindex = leftindex; @@ -1306,16 +1306,21 @@ deque_ass_item(dequeobject *deque, Py_ssize_t i, PyObject *v) static void deque_dealloc(dequeobject *deque) { + Py_ssize_t i; + PyObject_GC_UnTrack(deque); if (deque->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) deque); if (deque->leftblock != NULL) { deque_clear(deque); assert(deque->leftblock != NULL); - freeblock(deque->leftblock); + freeblock(deque, deque->leftblock); } deque->leftblock = NULL; deque->rightblock = NULL; + for (i=0 ; i < deque->numfreeblocks ; i++) { + PyMem_Free(deque->freeblocks[i]); + } Py_TYPE(deque)->tp_free(deque); }