Another stab at the thread cleanup patch.
Antoine Pitrou found a variation that worked for him on the thread+fork tests, and added an important self._is_stopped = True to the after-fork code. I confess I don't know why things passed before. But then mixing fork with threads is insane ;-)
This commit is contained in:
parent
7875523f16
commit
b5e9ac9ec6
|
@ -566,6 +566,7 @@ class Thread:
|
|||
else:
|
||||
# The thread isn't alive after fork: it doesn't have a tstate
|
||||
# anymore.
|
||||
self._is_stopped = True
|
||||
self._tstate_lock = None
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -703,6 +704,25 @@ class Thread:
|
|||
pass
|
||||
|
||||
def _stop(self):
|
||||
# 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.
|
||||
lock = self._tstate_lock
|
||||
if lock is not None:
|
||||
assert not lock.locked()
|
||||
self._is_stopped = True
|
||||
self._tstate_lock = None
|
||||
|
||||
|
@ -921,9 +941,12 @@ 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
|
||||
# The main thread isn't finished yet, so its thread state lock can't have
|
||||
# been released.
|
||||
assert tlock is not None
|
||||
assert tlock.locked()
|
||||
tlock.release()
|
||||
_main_thread._stop()
|
||||
t = _pickSomeNonDaemonThread()
|
||||
while t:
|
||||
|
|
Loading…
Reference in New Issue