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.
This commit is contained in:
commit
ec60edb977
|
@ -6,6 +6,7 @@ import weakref
|
||||||
import operator
|
import operator
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
|
import time
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import script_helper
|
from test.support import script_helper
|
||||||
|
@ -72,6 +73,29 @@ class TestBase(unittest.TestCase):
|
||||||
self.cbcalled += 1
|
self.cbcalled += 1
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def collect_in_thread(period=0.0001):
|
||||||
|
"""
|
||||||
|
Ensure GC collections happen in a different thread, at a high frequency.
|
||||||
|
"""
|
||||||
|
threading = support.import_module('threading')
|
||||||
|
please_stop = False
|
||||||
|
|
||||||
|
def collect():
|
||||||
|
while not please_stop:
|
||||||
|
time.sleep(period)
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
with support.disable_gc():
|
||||||
|
t = threading.Thread(target=collect)
|
||||||
|
t.start()
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
please_stop = True
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
|
||||||
class ReferencesTestCase(TestBase):
|
class ReferencesTestCase(TestBase):
|
||||||
|
|
||||||
def test_basic_ref(self):
|
def test_basic_ref(self):
|
||||||
|
@ -1636,6 +1660,23 @@ class MappingTestCase(TestBase):
|
||||||
dict = weakref.WeakKeyDictionary()
|
dict = weakref.WeakKeyDictionary()
|
||||||
self.assertRegex(repr(dict), '<WeakKeyDictionary at 0x.*>')
|
self.assertRegex(repr(dict), '<WeakKeyDictionary at 0x.*>')
|
||||||
|
|
||||||
|
def test_threaded_weak_valued_setdefault(self):
|
||||||
|
d = weakref.WeakValueDictionary()
|
||||||
|
with collect_in_thread():
|
||||||
|
for i in range(100000):
|
||||||
|
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(100000):
|
||||||
|
d[10] = RefCycle()
|
||||||
|
x = d.pop(10, 10)
|
||||||
|
self.assertIsNot(x, None) # we never put None in there!
|
||||||
|
|
||||||
|
|
||||||
from test import mapping_tests
|
from test import mapping_tests
|
||||||
|
|
||||||
class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
|
class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
|
||||||
|
|
|
@ -239,24 +239,27 @@ class WeakValueDictionary(collections.MutableMapping):
|
||||||
try:
|
try:
|
||||||
o = self.data.pop(key)()
|
o = self.data.pop(key)()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
o = None
|
||||||
|
if o is None:
|
||||||
if args:
|
if args:
|
||||||
return args[0]
|
return args[0]
|
||||||
raise
|
else:
|
||||||
if o is None:
|
raise KeyError(key)
|
||||||
raise KeyError(key)
|
|
||||||
else:
|
else:
|
||||||
return o
|
return o
|
||||||
|
|
||||||
def setdefault(self, key, default=None):
|
def setdefault(self, key, default=None):
|
||||||
try:
|
try:
|
||||||
wr = self.data[key]
|
o = self.data[key]()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
o = None
|
||||||
|
if o is None:
|
||||||
if self._pending_removals:
|
if self._pending_removals:
|
||||||
self._commit_removals()
|
self._commit_removals()
|
||||||
self.data[key] = KeyedRef(default, self._remove, key)
|
self.data[key] = KeyedRef(default, self._remove, key)
|
||||||
return default
|
return default
|
||||||
else:
|
else:
|
||||||
return wr()
|
return o
|
||||||
|
|
||||||
def update(*args, **kwargs):
|
def update(*args, **kwargs):
|
||||||
if not args:
|
if not args:
|
||||||
|
|
|
@ -200,6 +200,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and
|
||||||
|
WeakValueDictionary.pop() when a GC collection happens in another
|
||||||
|
thread.
|
||||||
|
|
||||||
- Issue #20191: Fixed a crash in resource.prlimit() when pass a sequence that
|
- Issue #20191: Fixed a crash in resource.prlimit() when pass a sequence that
|
||||||
doesn't own its elements as limits.
|
doesn't own its elements as limits.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue