diff -r e33b4c18af59 Lib/test/test_deque.py --- a/Lib/test/test_deque.py Wed Sep 16 12:18:55 2015 -0400 +++ b/Lib/test/test_deque.py Thu Sep 17 17:08:05 2015 +0300 @@ -474,13 +474,14 @@ class TestBasic(unittest.TestCase): self.assertRaises(IndexError, d.popleft) def test_clear(self): - d = deque(range(100)) - self.assertEqual(len(d), 100) - d.clear() - self.assertEqual(len(d), 0) - self.assertEqual(list(d), []) - d.clear() # clear an emtpy deque - self.assertEqual(list(d), []) + for n in range(1, 101): + d = deque(range(n)) + self.assertEqual(len(d), n) + d.clear() + self.assertEqual(len(d), 0) + self.assertEqual(list(d), []) + d.clear() # clear an emtpy deque + self.assertEqual(list(d), []) def test_remove(self): d = deque('abcdefghcij') diff -r e33b4c18af59 Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Wed Sep 16 12:18:55 2015 -0400 +++ b/Modules/_collectionsmodule.c Thu Sep 17 17:08:05 2015 +0300 @@ -1045,16 +1045,61 @@ PyDoc_STRVAR(remove_doc, static void deque_clear(dequeobject *deque) { + block *b; + block *prevblock; + block *leftblock = deque->leftblock; + Py_ssize_t leftindex = deque->leftindex; +#ifndef NDEBUG + Py_ssize_t rightindex = deque->rightindex; +#endif + Py_ssize_t n = Py_SIZE(deque); PyObject *item; + block tmp; /* a copy of right block */ - while (Py_SIZE(deque)) { - item = deque_pop(deque, NULL); - assert (item != NULL); + /* During the process of clearing a deque, decrefs can cause the + deque to mutate. To avoid fatal confusion, we have to make the + deque empty before clearing the blocks and never refer to + anything via deque->ref while clearing. (This is the same + technique used in the clearing functions for lists, sets, and + dicts.) + */ + + b = deque->rightblock; + memcpy(tmp.data, b->data, sizeof(PyObject *) * BLOCKLEN); + MARK_END(tmp.rightlink); + if (b == leftblock) + leftblock = &tmp; + else { + CHECK_NOT_END(b->leftlink); + assert(b->leftlink->rightlink == b); + b->leftlink->rightlink = &tmp; + } +#ifndef NDEBUG + memset(b->data, 0, sizeof(PyObject *) * BLOCKLEN); +#endif + MARK_END(b->leftlink); + MARK_END(b->rightlink); + Py_SIZE(deque) = 0; + deque->leftblock = b; + deque->rightblock = b; + deque->leftindex = CENTER + 1; + deque->rightindex = CENTER; + deque->state++; + + /* Now use the old size, leftblock, and leftindex to decref the pointers */ + while (n--) { + item = leftblock->data[leftindex]; Py_DECREF(item); + leftindex++; + if (leftindex == BLOCKLEN && n) { + prevblock = leftblock; + leftblock = leftblock->rightlink; + leftindex = 0; + freeblock(prevblock); + } } - assert(deque->leftblock == deque->rightblock); - assert(deque->leftindex - 1 == deque->rightindex); - assert(Py_SIZE(deque) == 0); + assert(leftblock == &tmp); + assert(leftindex == rightindex + 1); } static int