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.
(cherry picked from commit 5909a494cd
)
Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
7c4d8fa82a
commit
cf70854f10
|
@ -439,6 +439,35 @@ class ThreadTests(BaseTestCase):
|
|||
t = threading.Thread(daemon=True)
|
||||
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()')
|
||||
def test_dummy_thread_after_fork(self):
|
||||
# Issue #14308: a dummy thread in the active list doesn't mess up
|
||||
|
|
|
@ -826,8 +826,12 @@ class Thread:
|
|||
# they may be in an invalid state leading to a deadlock or crash.
|
||||
self._started._at_fork_reinit()
|
||||
if is_alive:
|
||||
self._tstate_lock._at_fork_reinit()
|
||||
self._tstate_lock.acquire()
|
||||
# bpo-42350: If the fork happens when the thread is already stopped
|
||||
# (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:
|
||||
# The thread isn't alive after fork: it doesn't have a tstate
|
||||
# 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