[Python-checkins] cpython (2.7): Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and
antoine.pitrou
python-checkins at python.org
Mon Dec 19 05:13:57 EST 2016
https://hg.python.org/cpython/rev/bcb1f0698401
changeset: 105741:bcb1f0698401
branch: 2.7
parent: 105721:34b9fe09a2b7
user: Antoine Pitrou <solipsis at pitrou.net>
date: Mon Dec 19 11:12:58 2016 +0100
summary:
Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and WeakValueDictionary.pop()
when a GC collection happens in another thread.
Original patch and report by Armin Rigo.
files:
Lib/test/test_support.py | 10 ++++++
Lib/test/test_weakref.py | 44 ++++++++++++++++++++++++++++
Lib/weakref.py | 13 +++++---
Misc/NEWS | 4 ++
4 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -1710,3 +1710,13 @@
# The sequence should be deallocated just after the end of iterating
gc_collect()
test.assertTrue(done[0])
+
+ at contextlib.contextmanager
+def disable_gc():
+ have_gc = gc.isenabled()
+ gc.disable()
+ try:
+ yield
+ finally:
+ if have_gc:
+ gc.enable()
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -6,6 +6,7 @@
import operator
import contextlib
import copy
+import time
from test import test_support
@@ -56,6 +57,32 @@
self.cycle = self
+ at contextlib.contextmanager
+def collect_in_thread(period=0.0001):
+ """
+ Ensure GC collections happen in a different thread, at a high frequency.
+ """
+ threading = test_support.import_module('threading')
+ please_stop = False
+
+ def collect():
+ while not please_stop:
+ time.sleep(period)
+ gc.collect()
+
+ with test_support.disable_gc():
+ old_interval = sys.getcheckinterval()
+ sys.setcheckinterval(20)
+ t = threading.Thread(target=collect)
+ t.start()
+ try:
+ yield
+ finally:
+ please_stop = True
+ t.join()
+ sys.setcheckinterval(old_interval)
+
+
class TestBase(unittest.TestCase):
def setUp(self):
@@ -1394,6 +1421,23 @@
self.assertEqual(len(d), 0)
self.assertEqual(count, 2)
+ def test_threaded_weak_valued_setdefault(self):
+ d = weakref.WeakValueDictionary()
+ with collect_in_thread():
+ for i in range(50000):
+ x = d.setdefault(10, RefCycle())
+ self.assertIsNot(x, None) # we never put None in there!
+ del x
+
+ def test_threaded_weak_valued_pop(self):
+ d = weakref.WeakValueDictionary()
+ with collect_in_thread():
+ for i in range(50000):
+ d[10] = RefCycle()
+ x = d.pop(10, 10)
+ self.assertIsNot(x, None) # we never put None in there!
+
+
from test import mapping_tests
class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
diff --git a/Lib/weakref.py b/Lib/weakref.py
--- a/Lib/weakref.py
+++ b/Lib/weakref.py
@@ -202,24 +202,27 @@
try:
o = self.data.pop(key)()
except KeyError:
+ o = None
+ if o is None:
if args:
return args[0]
- raise
- if o is None:
- raise KeyError, key
+ else:
+ raise KeyError, key
else:
return o
def setdefault(self, key, default=None):
try:
- wr = self.data[key]
+ o = self.data[key]()
except KeyError:
+ o = None
+ if o is None:
if self._pending_removals:
self._commit_removals()
self.data[key] = KeyedRef(default, self._remove, key)
return default
else:
- return wr()
+ return o
def update(*args, **kwargs):
if not args:
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,10 @@
Library
-------
+- Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and
+ WeakValueDictionary.pop() when a GC collection happens in another
+ thread.
+
- Issue #28925: cPickle now correctly propagates errors when unpickle instances
of old-style classes.
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list