Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions 12 Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,18 @@ def test_shutdown_locks(self):
# Daemon threads must never add it to _shutdown_locks.
self.assertNotIn(tstate_lock, threading._shutdown_locks)

def test_leak_without_join(self):
# bpo-37788: Test that no continuous memory leak occurs when multiple
# subthreads which are not joined are started.
def noop(): pass
for i in range(10):
thread = threading.Thread(target=noop)
thread.start()
self.assertIn(thread._tstate_lock, threading._shutdown_locks)
while thread._tstate_lock.locked():
pass
self.assertLess(len(threading._shutdown_locks), 10)


class ThreadJoinOnShutdown(BaseTestCase):

Expand Down
20 changes: 19 additions & 1 deletion 20 Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,21 @@ def _bootstrap(self):
def _set_ident(self):
self._ident = get_ident()

def _discard_tstate_locks(self):
with _shutdown_locks_lock:
locks = list(_shutdown_locks)

if not locks:
return

for lock in locks:
if lock.locked():
continue
lock.acquire()
lock.release()
with _shutdown_locks_lock:
_shutdown_locks.discard(lock)

def _set_tstate_lock(self):
"""
Set a lock object which will be released by the interpreter when
Expand Down Expand Up @@ -964,7 +979,10 @@ def _bootstrap_inner(self):
# the exception keeps the target alive past when we
# assert that it's dead.
#XXX self._exc_clear()
pass
# To prevent continuous leakage of tstate locks, try to discard at
# the end of each subthread.
if not self.daemon:
self._discard_tstate_locks()
finally:
with _active_limbo_lock:
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add threading.Thread._discard_tstate_locks() method to check whether the lock of other subthreads can be deleted from the _shutdown_locks list when the subthread task ends and prevent continuous leakage of tstate locks.
Morty Proxy This is a proxified and sanitized view of the page, visit original site.