summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2013-08-25 17:48:18 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2013-08-25 17:48:18 (GMT)
commit7eaf3f7080a0b0e1e551d2d9d2dcddd54d53af59 (patch)
treea2f3078056ba9c1e132603385ea9bc4f379032cb
parent0bb766b95cc672a82d29fbe339e515b921f1bfcc (diff)
downloadcpython-7eaf3f7080a0b0e1e551d2d9d2dcddd54d53af59.zip
cpython-7eaf3f7080a0b0e1e551d2d9d2dcddd54d53af59.tar.gz
cpython-7eaf3f7080a0b0e1e551d2d9d2dcddd54d53af59.tar.bz2
Issue #18808: Non-daemon threads are now automatically joined when a sub-interpreter is shutdown (it would previously dump a fatal error).
-rw-r--r--Lib/test/test_threading.py48
-rw-r--r--Misc/NEWS3
-rw-r--r--Python/pythonrun.c3
3 files changed, 54 insertions, 0 deletions
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index ecf22fc..3629749 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -9,6 +9,7 @@ import re
import sys
_thread = import_module('_thread')
threading = import_module('threading')
+import _testcapi
import time
import unittest
import weakref
@@ -754,6 +755,53 @@ class ThreadJoinOnShutdown(BaseTestCase):
t.join()
+class SubinterpThreadingTests(BaseTestCase):
+
+ def test_threads_join(self):
+ # Non-daemon threads should be joined at subinterpreter shutdown
+ # (issue #18808)
+ r, w = os.pipe()
+ self.addCleanup(os.close, r)
+ self.addCleanup(os.close, w)
+ code = r"""if 1:
+ import os
+ import threading
+ import time
+
+ def f():
+ # Sleep a bit so that the thread is still running when
+ # Py_EndInterpreter is called.
+ time.sleep(0.05)
+ os.write(%d, b"x")
+ threading.Thread(target=f).start()
+ """ % (w,)
+ ret = _testcapi.run_in_subinterp(code)
+ self.assertEqual(ret, 0)
+ # The thread was joined properly.
+ self.assertEqual(os.read(r, 1), b"x")
+
+ def test_daemon_threads_fatal_error(self):
+ subinterp_code = r"""if 1:
+ import os
+ import threading
+ import time
+
+ def f():
+ # Make sure the daemon thread is still running when
+ # Py_EndInterpreter is called.
+ time.sleep(10)
+ threading.Thread(target=f, daemon=True).start()
+ """
+ script = r"""if 1:
+ import _testcapi
+
+ _testcapi.run_in_subinterp(%r)
+ """ % (subinterp_code,)
+ rc, out, err = assert_python_failure("-c", script)
+ self.assertIn("Fatal Python error: Py_EndInterpreter: "
+ "not the last thread", err.decode())
+
+
class ThreadingExceptionTests(BaseTestCase):
# A RuntimeError should be raised if Thread.start() is called
# multiple times.
diff --git a/Misc/NEWS b/Misc/NEWS
index b923109..90f15be 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ Projected Release date: 2013-09-08
Core and Builtins
-----------------
+- Issue #18808: Non-daemon threads are now automatically joined when
+ a sub-interpreter is shutdown (it would previously dump a fatal error).
+
- Remove supporting for compiling on systems without getcwd().
- Issue #18774: Remove last bits of GNU PTH thread code and thread_pth.h.
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index b0bc549..37dc3b8 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -789,6 +789,9 @@ Py_EndInterpreter(PyThreadState *tstate)
Py_FatalError("Py_EndInterpreter: thread is not current");
if (tstate->frame != NULL)
Py_FatalError("Py_EndInterpreter: thread still has a frame");
+
+ wait_for_thread_shutdown();
+
if (tstate != interp->tstate_head || tstate->next != NULL)
Py_FatalError("Py_EndInterpreter: not the last thread");