[Python-checkins] cpython (merge 3.5 -> default): Issue #25935: Garbage collector now breaks reference loops with OrderedDict.

serhiy.storchaka python-checkins at python.org
Tue Jan 19 07:49:13 EST 2016


https://hg.python.org/cpython/rev/5f0a08cfc4f6
changeset:   99986:5f0a08cfc4f6
parent:      99984:f2586c381b0b
parent:      99985:627575e9a7aa
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Tue Jan 19 14:48:42 2016 +0200
summary:
  Issue #25935: Garbage collector now breaks reference loops with OrderedDict.

files:
  Lib/test/test_ordered_dict.py |  13 +++++++++++
  Misc/NEWS                     |   2 +
  Objects/odictobject.c         |  27 +++++++++++-----------
  3 files changed, 29 insertions(+), 13 deletions(-)


diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py
--- a/Lib/test/test_ordered_dict.py
+++ b/Lib/test/test_ordered_dict.py
@@ -1,10 +1,12 @@
 import contextlib
 import copy
+import gc
 import pickle
 from random import randrange, shuffle
 import struct
 import sys
 import unittest
+import weakref
 from collections.abc import MutableMapping
 from test import mapping_tests, support
 
@@ -593,6 +595,17 @@
         dict.update(od, [('spam', 1)])
         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):
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -133,6 +133,8 @@
 Library
 -------
 
+- Issue #25935: Garbage collector now breaks reference loops with OrderedDict.
+
 - Issue #16620: Fixed AttributeError in msilib.Directory.glob().
 
 - Issue #26013: Added compatibility with broken protocol 2 pickles created
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -772,19 +772,17 @@
 {
     _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);
     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. */
@@ -1233,8 +1231,6 @@
 {
     PyDict_Clear((PyObject *)od);
     _odict_clear_nodes(od);
-    _odict_FIRST(od) = NULL;
-    _odict_LAST(od) = NULL;
     if (_odict_resize(od) < 0)
         return NULL;
     Py_RETURN_NONE;
@@ -1556,8 +1552,13 @@
 static int
 odict_traverse(PyODictObject *od, visitproc visit, void *arg)
 {
+    _ODictNode *node;
+
     Py_VISIT(od->od_inst_dict);
     Py_VISIT(od->od_weakreflist);
+    _odict_FOREACH(od, node) {
+        Py_VISIT(_odictnode_KEY(node));
+    }
     return PyDict_Type.tp_traverse((PyObject *)od, visit, arg);
 }
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list