diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index c0024acefe3..300e9af9fcf 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -324,6 +324,15 @@ class TestBasic(unittest.TestCase): for s in ('abcd', xrange(2000)): self.assertEqual(list(reversed(deque(s))), list(reversed(s))) + def test_gc_doesnt_blowup(self): + import gc + # This used to assert-fail in deque_traverse() under a debug + # build, or run wild with a NULL pointer in a release build. + d = deque() + for i in xrange(100): + d.append(1) + gc.collect() + def R(seqn): 'Regular generator' for i in seqn: diff --git a/Modules/collectionsmodule.c b/Modules/collectionsmodule.c index f8a6f612205..196cbe2244a 100644 --- a/Modules/collectionsmodule.c +++ b/Modules/collectionsmodule.c @@ -478,19 +478,22 @@ deque_dealloc(dequeobject *deque) static int deque_traverse(dequeobject *deque, visitproc visit, void *arg) { - block * b = deque->leftblock; - int index = deque->leftindex; + block *b; PyObject *item; + int index; + int indexlo = deque->leftindex; - while (b != deque->rightblock || index <= deque->rightindex) { - item = b->data[index]; - index++; - if (index == BLOCKLEN ) { - assert(b->rightlink != NULL); - b = b->rightlink; - index = 0; + assert(deque->leftblock != NULL); + for (b = deque->leftblock; b != NULL; b = b->rightlink) { + const int indexhi = b == deque->rightblock ? + deque->rightindex : + BLOCKLEN - 1; + + for (index = indexlo; index <= indexhi; ++index) { + item = b->data[index]; + Py_VISIT(item); } - Py_VISIT(item); + indexlo = 0; } return 0; }