Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c1ee488

Browse files
committed
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.
1 parent ca3263c commit c1ee488

3 files changed

Lines changed: 53 additions & 5 deletions

File tree

Lib/test/test_weakref.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import operator
77
import contextlib
88
import copy
9+
import time
910

1011
from test import support
1112
from test.support import script_helper
@@ -72,6 +73,29 @@ def callback(self, ref):
7273
self.cbcalled += 1
7374

7475

76+
@contextlib.contextmanager
77+
def collect_in_thread(period=0.0001):
78+
"""
79+
Ensure GC collections happen in a different thread, at a high frequency.
80+
"""
81+
threading = support.import_module('threading')
82+
please_stop = False
83+
84+
def collect():
85+
while not please_stop:
86+
time.sleep(period)
87+
gc.collect()
88+
89+
with support.disable_gc():
90+
t = threading.Thread(target=collect)
91+
t.start()
92+
try:
93+
yield
94+
finally:
95+
please_stop = True
96+
t.join()
97+
98+
7599
class ReferencesTestCase(TestBase):
76100

77101
def test_basic_ref(self):
@@ -1633,6 +1657,23 @@ def test_make_weak_keyed_dict_repr(self):
16331657
dict = weakref.WeakKeyDictionary()
16341658
self.assertRegex(repr(dict), '<WeakKeyDictionary at 0x.*>')
16351659

1660+
def test_threaded_weak_valued_setdefault(self):
1661+
d = weakref.WeakValueDictionary()
1662+
with collect_in_thread():
1663+
for i in range(100000):
1664+
x = d.setdefault(10, RefCycle())
1665+
self.assertIsNot(x, None) # we never put None in there!
1666+
del x
1667+
1668+
def test_threaded_weak_valued_pop(self):
1669+
d = weakref.WeakValueDictionary()
1670+
with collect_in_thread():
1671+
for i in range(100000):
1672+
d[10] = RefCycle()
1673+
x = d.pop(10, 10)
1674+
self.assertIsNot(x, None) # we never put None in there!
1675+
1676+
16361677
from test import mapping_tests
16371678

16381679
class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):

Lib/weakref.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,24 +239,27 @@ def pop(self, key, *args):
239239
try:
240240
o = self.data.pop(key)()
241241
except KeyError:
242+
o = None
243+
if o is None:
242244
if args:
243245
return args[0]
244-
raise
245-
if o is None:
246-
raise KeyError(key)
246+
else:
247+
raise KeyError(key)
247248
else:
248249
return o
249250

250251
def setdefault(self, key, default=None):
251252
try:
252-
wr = self.data[key]
253+
o = self.data[key]()
253254
except KeyError:
255+
o = None
256+
if o is None:
254257
if self._pending_removals:
255258
self._commit_removals()
256259
self.data[key] = KeyedRef(default, self._remove, key)
257260
return default
258261
else:
259-
return wr()
262+
return o
260263

261264
def update(*args, **kwargs):
262265
if not args:

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ Core and Builtins
133133
Library
134134
-------
135135

136+
- Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and
137+
WeakValueDictionary.pop() when a GC collection happens in another
138+
thread.
139+
136140
- Issue #20191: Fixed a crash in resource.prlimit() when pass a sequence that
137141
doesn't own its elements as limits.
138142

0 commit comments

Comments
 (0)