Issue #25935: Garbage collector now breaks reference loops with OrderedDict.

This commit is contained in:
Serhiy Storchaka 2016-01-19 14:48:42 +02:00
commit 4918b47c64
3 changed files with 29 additions and 13 deletions

View File

@ -1,10 +1,12 @@
import contextlib import contextlib
import copy import copy
import gc
import pickle import pickle
from random import randrange, shuffle from random import randrange, shuffle
import struct import struct
import sys import sys
import unittest import unittest
import weakref
from collections.abc import MutableMapping from collections.abc import MutableMapping
from test import mapping_tests, support from test import mapping_tests, support
@ -593,6 +595,17 @@ class OrderedDictTests:
dict.update(od, [('spam', 1)]) dict.update(od, [('spam', 1)])
self.assertNotIn('NULL', repr(od)) self.assertNotIn('NULL', repr(od))
def test_reference_loop(self):
# Issue 25935
OrderedDict = self.OrderedDict
class A:
od = OrderedDict()
A.od[A] = None
r = weakref.ref(A)
del A
gc.collect()
self.assertIsNone(r())
class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):

View File

@ -133,6 +133,8 @@ Core and Builtins
Library Library
------- -------
- Issue #25935: Garbage collector now breaks reference loops with OrderedDict.
- Issue #16620: Fixed AttributeError in msilib.Directory.glob(). - Issue #16620: Fixed AttributeError in msilib.Directory.glob().
- Issue #26013: Added compatibility with broken protocol 2 pickles created - Issue #26013: Added compatibility with broken protocol 2 pickles created

View File

@ -772,19 +772,17 @@ _odict_clear_nodes(PyODictObject *od)
{ {
_ODictNode *node, *next; _ODictNode *node, *next;
if (!_odict_EMPTY(od)) {
node = _odict_FIRST(od);
while (node != NULL) {
next = _odictnode_NEXT(node);
_odictnode_DEALLOC(node);
node = next;
}
_odict_FIRST(od) = NULL;
_odict_LAST(od) = NULL;
}
_odict_free_fast_nodes(od); _odict_free_fast_nodes(od);
od->od_fast_nodes = NULL; od->od_fast_nodes = NULL;
node = _odict_FIRST(od);
_odict_FIRST(od) = NULL;
_odict_LAST(od) = NULL;
while (node != NULL) {
next = _odictnode_NEXT(node);
_odictnode_DEALLOC(node);
node = next;
}
} }
/* There isn't any memory management of nodes past this point. */ /* There isn't any memory management of nodes past this point. */
@ -1233,8 +1231,6 @@ odict_clear(register PyODictObject *od)
{ {
PyDict_Clear((PyObject *)od); PyDict_Clear((PyObject *)od);
_odict_clear_nodes(od); _odict_clear_nodes(od);
_odict_FIRST(od) = NULL;
_odict_LAST(od) = NULL;
if (_odict_resize(od) < 0) if (_odict_resize(od) < 0)
return NULL; return NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
@ -1556,8 +1552,13 @@ PyDoc_STRVAR(odict_doc,
static int static int
odict_traverse(PyODictObject *od, visitproc visit, void *arg) odict_traverse(PyODictObject *od, visitproc visit, void *arg)
{ {
_ODictNode *node;
Py_VISIT(od->od_inst_dict); Py_VISIT(od->od_inst_dict);
Py_VISIT(od->od_weakreflist); Py_VISIT(od->od_weakreflist);
_odict_FOREACH(od, node) {
Py_VISIT(_odictnode_KEY(node));
}
return PyDict_Type.tp_traverse((PyObject *)od, visit, arg); return PyDict_Type.tp_traverse((PyObject *)od, visit, arg);
} }