Skip to content

Commit 428bc6c

Browse files
committed
Issue #12573: Add resource checks for dangling Thread and Process objects.
2 parents 91fe815 + c081c0c commit 428bc6c

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

Lib/multiprocessing/process.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import sys
4343
import signal
4444
import itertools
45+
from _weakrefset import WeakSet
4546

4647
#
4748
#
@@ -109,6 +110,7 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
109110
self._kwargs = dict(kwargs)
110111
self._name = name or type(self).__name__ + '-' + \
111112
':'.join(str(i) for i in self._identity)
113+
_dangling.add(self)
112114

113115
def run(self):
114116
'''
@@ -344,3 +346,6 @@ def __init__(self):
344346
for name, signum in list(signal.__dict__.items()):
345347
if name[:3]=='SIG' and '_' not in name:
346348
_exitcode_to_name[-signum] = name
349+
350+
# For debug and leak testing
351+
_dangling = WeakSet()

Lib/test/regrtest.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,15 @@
182182
import warnings
183183
from inspect import isabstract
184184

185+
try:
186+
import threading
187+
except ImportError:
188+
threading = None
189+
try:
190+
import multiprocessing.process
191+
except ImportError:
192+
multiprocessing = None
193+
185194

186195
# Some times __path__ and __file__ are not absolute (e.g. while running from
187196
# Lib/) and, if we change the CWD to run the tests in a temporary dir, some
@@ -930,7 +939,8 @@ def __init__(self, testname, verbose=0, quiet=False):
930939
'os.environ', 'sys.path', 'sys.path_hooks', '__import__',
931940
'warnings.filters', 'asyncore.socket_map',
932941
'logging._handlers', 'logging._handlerList', 'sys.gettrace',
933-
'sys.warnoptions')
942+
'sys.warnoptions', 'threading._dangling',
943+
'multiprocessing.process._dangling')
934944

935945
def get_sys_argv(self):
936946
return id(sys.argv), sys.argv, sys.argv[:]
@@ -1023,6 +1033,31 @@ def restore_sys_warnoptions(self, saved_options):
10231033
sys.warnoptions = saved_options[1]
10241034
sys.warnoptions[:] = saved_options[2]
10251035

1036+
# Controlling dangling references to Thread objects can make it easier
1037+
# to track reference leaks.
1038+
def get_threading__dangling(self):
1039+
if not threading:
1040+
return None
1041+
# This copies the weakrefs without making any strong reference
1042+
return threading._dangling.copy()
1043+
def restore_threading__dangling(self, saved):
1044+
if not threading:
1045+
return
1046+
threading._dangling.clear()
1047+
threading._dangling.update(saved)
1048+
1049+
# Same for Process objects
1050+
def get_multiprocessing_process__dangling(self):
1051+
if not multiprocessing:
1052+
return None
1053+
# This copies the weakrefs without making any strong reference
1054+
return multiprocessing.process._dangling.copy()
1055+
def restore_multiprocessing_process__dangling(self, saved):
1056+
if not multiprocessing:
1057+
return
1058+
multiprocessing.process._dangling.clear()
1059+
multiprocessing.process._dangling.update(saved)
1060+
10261061
def resource_info(self):
10271062
for name in self.resources:
10281063
method_suffix = name.replace('.', '_')

Lib/threading.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from time import time as _time, sleep as _sleep
77
from traceback import format_exc as _format_exc
88
from collections import deque
9+
from _weakrefset import WeakSet
910

1011
# Note regarding PEP 8 compliant names
1112
# This threading model was originally inspired by Java, and inherited
@@ -608,6 +609,8 @@ def _newname(template="Thread-%d"):
608609
_active = {} # maps thread id to Thread object
609610
_limbo = {}
610611

612+
# For debug and leak testing
613+
_dangling = WeakSet()
611614

612615
# Main class for threads
613616

@@ -645,6 +648,7 @@ def __init__(self, group=None, target=None, name=None,
645648
# sys.stderr is not stored in the class like
646649
# sys.exc_info since it can be changed between instances
647650
self._stderr = _sys.stderr
651+
_dangling.add(self)
648652

649653
def _reset_internal_locks(self):
650654
# private! Called by _after_fork() to reset our internal locks as

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,8 @@ Extension Modules
10361036
Tests
10371037
-----
10381038

1039+
- Issue #12573: Add resource checks for dangling Thread and Process objects.
1040+
10391041
- Issue #12549: Correct test_platform to not fail when OS X returns 'x86_64'
10401042
as the processor type on some Mac systems.
10411043

0 commit comments

Comments
 (0)