bpo-42350: Fix Thread._reset_internal_locks() (GH-23268)
Fix the threading.Thread class at fork: do nothing if the thread is already stopped (ex: fork called at Python exit). Previously, an error was logged in the child process.
This commit is contained in:
parent
3df5c68487
commit
5909a494cd
|
@ -469,6 +469,35 @@ class ThreadTests(BaseTestCase):
|
||||||
t = threading.Thread(daemon=True)
|
t = threading.Thread(daemon=True)
|
||||||
self.assertTrue(t.daemon)
|
self.assertTrue(t.daemon)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(os, 'fork'), 'needs os.fork()')
|
||||||
|
def test_fork_at_exit(self):
|
||||||
|
# bpo-42350: Calling os.fork() after threading._shutdown() must
|
||||||
|
# not log an error.
|
||||||
|
code = textwrap.dedent("""
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from test.support import wait_process
|
||||||
|
|
||||||
|
# Import the threading module to register its "at fork" callback
|
||||||
|
import threading
|
||||||
|
|
||||||
|
def exit_handler():
|
||||||
|
pid = os.fork()
|
||||||
|
if not pid:
|
||||||
|
print("child process ok", file=sys.stderr, flush=True)
|
||||||
|
# child process
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
wait_process(pid, exitcode=0)
|
||||||
|
|
||||||
|
# exit_handler() will be called after threading._shutdown()
|
||||||
|
atexit.register(exit_handler)
|
||||||
|
""")
|
||||||
|
_, out, err = assert_python_ok("-c", code)
|
||||||
|
self.assertEqual(out, b'')
|
||||||
|
self.assertEqual(err.rstrip(), b'child process ok')
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
|
@unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
|
||||||
def test_dummy_thread_after_fork(self):
|
def test_dummy_thread_after_fork(self):
|
||||||
# Issue #14308: a dummy thread in the active list doesn't mess up
|
# Issue #14308: a dummy thread in the active list doesn't mess up
|
||||||
|
|
|
@ -844,8 +844,12 @@ class Thread:
|
||||||
# they may be in an invalid state leading to a deadlock or crash.
|
# they may be in an invalid state leading to a deadlock or crash.
|
||||||
self._started._at_fork_reinit()
|
self._started._at_fork_reinit()
|
||||||
if is_alive:
|
if is_alive:
|
||||||
self._tstate_lock._at_fork_reinit()
|
# bpo-42350: If the fork happens when the thread is already stopped
|
||||||
self._tstate_lock.acquire()
|
# (ex: after threading._shutdown() has been called), _tstate_lock
|
||||||
|
# is None. Do nothing in this case.
|
||||||
|
if self._tstate_lock is not None:
|
||||||
|
self._tstate_lock._at_fork_reinit()
|
||||||
|
self._tstate_lock.acquire()
|
||||||
else:
|
else:
|
||||||
# The thread isn't alive after fork: it doesn't have a tstate
|
# The thread isn't alive after fork: it doesn't have a tstate
|
||||||
# anymore.
|
# anymore.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix the :class:`threading.Thread` class at fork: do nothing if the thread is
|
||||||
|
already stopped (ex: fork called at Python exit). Previously, an error was
|
||||||
|
logged in the child process.
|
Loading…
Reference in New Issue