cpython: 2a19d09b08f8 (original) (raw)
Mercurial > cpython
changeset 69832:2a19d09b08f8 3.2
Issue #1856: Avoid crashes and lockups when daemon threads run while the interpreter is shutting down; instead, these threads are now killed when they try to take the GIL. [#1856]
Antoine Pitrou solipsis@pitrou.net | |
---|---|
date | Wed, 04 May 2011 20:02:30 +0200 |
parents | cc7342b4e59d |
children | c892b0321d23 52fb7dc721ed |
files | Include/pythonrun.h Lib/test/test_threading.py Misc/NEWS Python/ceval.c Python/pythonrun.c Python/thread_pthread.h |
diffstat | 6 files changed, 69 insertions(+), 7 deletions(-)[+] [-] Include/pythonrun.h 2 Lib/test/test_threading.py 45 Misc/NEWS 4 Python/ceval.c 6 Python/pythonrun.c 15 Python/thread_pthread.h 4 |
line wrap: on
line diff
--- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -214,6 +214,8 @@ PyAPI_FUNC(void) PyByteArray_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); PyAPI_FUNC(void) PyOS_FiniInterrupts(void); PyAPI_FUNC(void) _PyGC_Fini(void); + +PyAPI_DATA(PyThreadState ) _Py_Finalizing; #endif / Stuff with no proper home (yet) */
--- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -12,6 +12,7 @@ import unittest import weakref import os import subprocess +from test.script_helper import assert_python_ok from test import lock_tests @@ -463,7 +464,6 @@ class ThreadJoinOnShutdown(BaseTestCase) """ self._run_and_join(script) - @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") def test_2_join_in_forked_process(self): # Like the test above, but from a forked interpreter @@ -655,6 +655,49 @@ class ThreadJoinOnShutdown(BaseTestCase) output = "end of worker thread\nend of main thread\n" self.assertScriptHasOutput(script, output)
- def test_6_daemon_threads(self):
# Check that a daemon thread cannot crash the interpreter on shutdown[](#l2.24)
# by manipulating internal structures that are being disposed of in[](#l2.25)
# the main thread.[](#l2.26)
script = """if True:[](#l2.27)
import os[](#l2.28)
import random[](#l2.29)
import sys[](#l2.30)
import time[](#l2.31)
import threading[](#l2.32)
thread_has_run = set()[](#l2.34)
def random_io():[](#l2.36)
'''Loop for a while sleeping random tiny amounts and doing some I/O.'''[](#l2.37)
blank = b'x' * 200[](#l2.38)
while True:[](#l2.39)
in_f = open(os.__file__, 'r')[](#l2.40)
stuff = in_f.read(200)[](#l2.41)
null_f = open(os.devnull, 'w')[](#l2.42)
null_f.write(stuff)[](#l2.43)
time.sleep(random.random() / 1995)[](#l2.44)
null_f.close()[](#l2.45)
in_f.close()[](#l2.46)
thread_has_run.add(threading.current_thread())[](#l2.47)
def main():[](#l2.49)
count = 0[](#l2.50)
for _ in range(40):[](#l2.51)
new_thread = threading.Thread(target=random_io)[](#l2.52)
new_thread.daemon = True[](#l2.53)
new_thread.start()[](#l2.54)
count += 1[](#l2.55)
while len(thread_has_run) < count:[](#l2.56)
time.sleep(0.001)[](#l2.57)
# Trigger process shutdown[](#l2.58)
sys.exit(0)[](#l2.59)
main()[](#l2.61)
"""[](#l2.62)
rc, out, err = assert_python_ok('-c', script)[](#l2.63)
self.assertFalse(err)[](#l2.64)
+ class ThreadingExceptionTests(BaseTestCase): # A RuntimeError should be raised if Thread.start() is called
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ What's New in Python 3.2.1? Core and Builtins ----------------- +- Issue #1856: Avoid crashes and lockups when daemon threads run while the
- interpreter is shutting down; instead, these threads are now killed when
- they try to take the GIL. +
- Issue #9756: When calling a method descriptor or a slot wrapper descriptor, the check of the object type doesn't read the class attribute anymore. Fix a crash if a class override its class attribute (e.g. a proxy of the
--- a/Python/ceval.c +++ b/Python/ceval.c @@ -440,6 +440,12 @@ PyEval_RestoreThread(PyThreadState *tsta if (gil_created()) { int err = errno; take_gil(tstate);
/* _Py_Finalizing is protected by the GIL */[](#l4.7)
if (_Py_Finalizing && tstate != _Py_Finalizing) {[](#l4.8)
drop_gil(tstate);[](#l4.9)
PyThread_exit_thread();[](#l4.10)
assert(0); /* unreachable */[](#l4.11)
}}[](#l4.12) errno = err;[](#l4.13)
--- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -90,6 +90,8 @@ int Py_IgnoreEnvironmentFlag; /* e.g. PY int Py_NoUserSiteDirectory = 0; /* for -s and site.py / int Py_UnbufferedStdioFlag = 0; / Unbuffered binary std{in,out,err} */ +PyThreadState _Py_Finalizing = NULL; + / PyModule_GetWarningsModule is no longer necessary as of 2.6 since _warnings is builtin. This API should not be used. */ PyObject * @@ -188,6 +190,7 @@ Py_InitializeEx(int install_sigs) if (initialized) return; initialized = 1;
#if defined(HAVE_LANGINFO_H) && defined(HAVE_SETLOCALE) /* Set up the LC_CTYPE locale, so we can obtain @@ -388,15 +391,19 @@ Py_Finalize(void) * the threads created via Threading. */ call_py_exitfuncs(); +
- /* Get current thread state and interpreter pointer */
- tstate = PyThreadState_GET();
- interp = tstate->interp;
- /* Remaining threads (e.g. daemon threads) will automatically exit
after taking the GIL (in PyEval_RestoreThread()). */[](#l5.30)
- _Py_Finalizing = tstate; initialized = 0; /* Flush stdout+stderr */ flush_std_files();
- /* Get current thread state and interpreter pointer */
- tstate = PyThreadState_GET();
- interp = tstate->interp;
- /* Disable signal handling */ PyOS_FiniInterrupts();
--- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -250,9 +250,9 @@ void PyThread_exit_thread(void) { dprintf(("PyThread_exit_thread called\n"));