diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2021-05-15 02:24:44 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-15 02:24:44 -0700 |
commit | 71dca6ea73aaf215fafa094512e8c748248c16b0 (patch) | |
tree | 848a16f29f4098ea9e81e2ff6e88540920563e72 /Lib/threading.py | |
parent | 2e99869f64bbd3c6590cb5ceaf9cf59e63689d63 (diff) | |
download | cpython-git-71dca6ea73aaf215fafa094512e8c748248c16b0.tar.gz |
[3.10] bpo-37788: Fix reference leak when Thread is never joined (GH-26103) (GH-26138)
When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed.
(cherry picked from commit c10c2ec7a0e06975e8010c56c9c3270f8ea322ec)
Co-authored-by: Antoine Pitrou <antoine@python.org>
Diffstat (limited to 'Lib/threading.py')
-rw-r--r-- | Lib/threading.py | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/Lib/threading.py b/Lib/threading.py index fb70abd17a..6c3d49c2d5 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -780,12 +780,27 @@ _active_limbo_lock = _allocate_lock() _active = {} # maps thread id to Thread object _limbo = {} _dangling = WeakSet() + # Set of Thread._tstate_lock locks of non-daemon threads used by _shutdown() # to wait until all Python thread states get deleted: # see Thread._set_tstate_lock(). _shutdown_locks_lock = _allocate_lock() _shutdown_locks = set() +def _maintain_shutdown_locks(): + """ + Drop any shutdown locks that don't correspond to running threads anymore. + + Calling this from time to time avoids an ever-growing _shutdown_locks + set when Thread objects are not joined explicitly. See bpo-37788. + + This must be called with _shutdown_locks_lock acquired. + """ + # If a lock was released, the corresponding thread has exited + to_remove = [lock for lock in _shutdown_locks if not lock.locked()] + _shutdown_locks.difference_update(to_remove) + + # Main class for threads class Thread: @@ -968,6 +983,7 @@ class Thread: if not self.daemon: with _shutdown_locks_lock: + _maintain_shutdown_locks() _shutdown_locks.add(self._tstate_lock) def _bootstrap_inner(self): @@ -1023,7 +1039,8 @@ class Thread: self._tstate_lock = None if not self.daemon: with _shutdown_locks_lock: - _shutdown_locks.discard(lock) + # Remove our lock and other released locks from _shutdown_locks + _maintain_shutdown_locks() def _delete(self): "Remove current thread from the dict of currently running threads." |