Skip to content

Commit 86c1792

Browse files
authored
gh-154137, regrtest: Check for Windows handle leaks (#154140)
1 parent 8bcbcf8 commit 86c1792

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

‎Lib/test/libregrtest/refleak.py‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,15 @@ def runtest_refleak(test_name, test_func,
114114
rc_deltas = array('q', [0]) * repcount
115115
alloc_deltas = array('q', [0]) * repcount
116116
fd_deltas = array('q', [0]) * repcount
117+
handle_deltas = array('q', [0]) * repcount
117118
getallocatedblocks = sys.getallocatedblocks
118119
gettotalrefcount = sys.gettotalrefcount
119120
getunicodeinternedsize = sys.getunicodeinternedsize
120121
fd_count = os_helper.fd_count
122+
handle_count = os_helper.handle_count
121123
# initialize variables to make pyflakes quiet
122124
rc_before = alloc_before = fd_before = interned_immortal_before = 0
125+
handle_before = 0
123126

124127
if not quiet:
125128
print("beginning", repcount, "repetitions. Showing number of leaks "
@@ -154,13 +157,17 @@ def runtest_refleak(test_name, test_func,
154157
alloc_after = getallocatedblocks() - interned_immortal_after
155158
rc_after = gettotalrefcount()
156159
fd_after = fd_count()
160+
handle_after = handle_count()
157161

158162
rc_deltas[i] = rc_after - rc_before
159163
alloc_deltas[i] = alloc_after - alloc_before
160164
fd_deltas[i] = fd_after - fd_before
165+
handle_deltas[i] = handle_after - handle_before
161166

162167
if not quiet:
163-
total_leaks = max(rc_deltas[i], alloc_deltas[i], fd_deltas[i])
168+
# use max, not sum, so total_leaks is one of the pooled ints
169+
total_leaks = max(rc_deltas[i], alloc_deltas[i],
170+
fd_deltas[i], handle_deltas[i])
164171
if total_leaks <= 0:
165172
symbol = '.'
166173
elif total_leaks < 10:
@@ -178,18 +185,29 @@ def runtest_refleak(test_name, test_func,
178185
alloc_before = alloc_after
179186
rc_before = rc_after
180187
fd_before = fd_after
188+
handle_before = handle_after
181189
interned_immortal_before = interned_immortal_after
182190

183191
restore_support_xml(xml_filename)
184192

185193
if not quiet:
186194
print(file=sys.stderr)
187195

196+
if ('multiprocessing' in test_name
197+
or 'concurrent_futures' in test_name):
198+
# gh-154208: Disable check for Windows handle leaks when
199+
# multiprocessing is used. There is a known race condition in
200+
# multiprocessing causing handle leak. Disable the multiprocessing
201+
# tests to be able to check for leaks for all other tests.
202+
for i in range(len(handle_deltas)):
203+
handle_deltas[i] = 0
204+
188205
failed = False
189206
for raw_deltas, item_name in [
190207
(rc_deltas, 'references'),
191208
(alloc_deltas, 'memory blocks'),
192-
(fd_deltas, 'file descriptors')
209+
(fd_deltas, 'file descriptors'),
210+
(handle_deltas, 'handles'),
193211
]:
194212
# Ignore warmup runs; convert to a list for reporting
195213
deltas = list(raw_deltas[warmups:])

‎Lib/test/test_regrtest.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,35 @@ def test_leak(self):
14571457
self.check_leak(code, 'file descriptors',
14581458
name='no_fd_leak', deltas=(1, -1, 0))
14591459

1460+
@unittest.skipUnless(support.Py_DEBUG, 'need a debug build')
1461+
@unittest.skipUnless(support.MS_WINDOWS, 'test specific to Windows')
1462+
def test_huntrleaks_handle_leak(self):
1463+
# test --huntrleaks for Windows handle leak
1464+
code = textwrap.dedent("""
1465+
import unittest
1466+
import _winapi
1467+
1468+
handle = None
1469+
1470+
class HandleLeakTest(unittest.TestCase):
1471+
def test_leak(self):
1472+
global handle
1473+
if handle is None:
1474+
handle = _winapi.CreateFile(
1475+
__file__, _winapi.GENERIC_READ,
1476+
0, _winapi.NULL,
1477+
_winapi.OPEN_EXISTING,
1478+
0, _winapi.NULL)
1479+
else:
1480+
hproc = _winapi.GetCurrentProcess()
1481+
copy = _winapi.DuplicateHandle(
1482+
hproc, handle,
1483+
hproc, 0, False,
1484+
_winapi.DUPLICATE_SAME_ACCESS)
1485+
# bug! the new handle is never closed
1486+
""")
1487+
self.check_leak(code, 'handles')
1488+
14601489
def test_list_tests(self):
14611490
# test --list-tests
14621491
tests = [self.create_test() for i in range(5)]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Check for Windows handle leaks in regrtest. Patch by Victor Stinner.

0 commit comments

Comments
 (0)