diff -r 089146b8ccc6 Lib/test/test_deque.py --- a/Lib/test/test_deque.py Fri Sep 25 00:49:44 2015 -0400 +++ b/Lib/test/test_deque.py Fri Sep 25 12:04:32 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 089146b8ccc6 Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Fri Sep 25 00:49:44 2015 -0400 +++ b/Modules/_collectionsmodule.c Fri Sep 25 12:04:32 2015 +0300 @@ -1045,16 +1045,55 @@ PyDoc_STRVAR(remove_doc, static void deque_clear(dequeobject *deque) { + block *b; + block *prevblock; + block *leftblock = deque->leftblock; + Py_ssize_t leftindex = deque->leftindex; + 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; + MARK_END(tmp.rightlink); + if (b == leftblock) { + memcpy(tmp.data + leftindex, b->data + leftindex, + sizeof(PyObject *) * (deque->rightindex + 1 - leftindex)); + leftblock = &tmp; + } + else { + memcpy(tmp.data, b->data, + sizeof(PyObject *) * (deque->rightindex + 1)); + b->leftlink->rightlink = &tmp; + } + 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); } static int