summaryrefslogtreecommitdiffstats
path: root/Lib/threading.py
diff options
context:
space:
mode:
authorTim Peters <tim@python.org>2013-09-09 17:57:10 (GMT)
committerTim Peters <tim@python.org>2013-09-09 17:57:10 (GMT)
commit7a6054b19dae9c65ce7cfe897ac589448972b646 (patch)
tree012f8d89e1d5a76ed1e0512d18c387b3a64b06eb /Lib/threading.py
parent8568f66daf088cf235a42288621fb4770ac48199 (diff)
downloadcpython-7a6054b19dae9c65ce7cfe897ac589448972b646.zip
cpython-7a6054b19dae9c65ce7cfe897ac589448972b646.tar.gz
cpython-7a6054b19dae9c65ce7cfe897ac589448972b646.tar.bz2
Minor cleanup of the new scheme for detecting thread termination.
Documented some obscurities, and assert'ed ._stop()'s crucial precondition.
Diffstat (limited to 'Lib/threading.py')
-rw-r--r--Lib/threading.py31
1 files changed, 26 insertions, 5 deletions
diff --git a/Lib/threading.py b/Lib/threading.py
index 1921ee3..9adc2b4 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -703,8 +703,28 @@ class Thread:
pass
def _stop(self):
- self._is_stopped = True
- self._tstate_lock = None
+ # After calling .stop(), .is_alive() returns False and .join() returns
+ # immediately. ._tstate_lock must be released before calling ._stop().
+ #
+ # Normal case: C code at the end of the thread's life
+ # (release_sentinel in _threadmodule.c) releases ._tstate_lock, and
+ # that's detected by our ._wait_for_tstate_lock(), called by .join()
+ # and .is_alive(). Any number of threads _may_ call ._stop()
+ # simultaneously (for example, if multiple threads are blocked in
+ # .join() calls), and they're not serialized. That's harmless -
+ # they'll just make redundant rebindings of ._is_stopped and
+ # ._tstate_lock. Obscure: we rebind ._tstate_lock last so that the
+ # "assert self._is_stopped" in ._wait_for_tstate_lock() always works
+ # (the assert is executed only if ._tstate_lock is None).
+ #
+ # Special case: _main_thread releases ._tstate_lock via this module's
+ # _shutdown() function.
+ tlock = self._tstate_lock
+ if tlock is not None:
+ # It's OK if multiple threads get in here (see above).
+ assert not tlock.locked()
+ self._is_stopped = True
+ self._tstate_lock = None
def _delete(self):
"Remove current thread from the dict of currently running threads."
@@ -921,9 +941,10 @@ def _shutdown():
# the main thread's tstate_lock - that won't happen until the interpreter
# is nearly dead. So we release it here. Note that just calling _stop()
# isn't enough: other threads may already be waiting on _tstate_lock.
- assert _main_thread._tstate_lock is not None
- assert _main_thread._tstate_lock.locked()
- _main_thread._tstate_lock.release()
+ tlock = _main_thread._tstate_lock
+ assert tlock is not None
+ assert tlock.locked()
+ tlock.release()
_main_thread._stop()
t = _pickSomeNonDaemonThread()
while t: