[issue17094] sys._current_frames() reports too many/wrong stack frames

Charles-François Natali report at bugs.python.org
Sun Mar 3 15:17:11 CET 2013


Charles-François Natali added the comment:

> Did you forget to attach the patch?

Oops...

----------
Added file: http://bugs.python.org/file29295/tstate_after_fork.diff

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue17094>
_______________________________________
-------------- next part --------------
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -728,6 +728,31 @@
         for t in threads:
             t.join()
 
+    @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
+    def test_clear_threads_states_after_fork(self):
+        # Issue #17094: check that threads states are cleared after fork()
+
+        # start a bunch of threads
+        threads = []
+        for i in range(16):
+            t = threading.Thread(target=lambda : time.sleep(0.3))
+            threads.append(t)
+            t.start()
+
+        pid = os.fork()
+        if pid == 0:
+            # check that threads states have been cleared
+            if len(sys._current_frames()) == 1:
+                os._exit(0)
+            else:
+                os._exit(1)
+        else:
+            _, status = os.waitpid(pid, 0)
+            self.assertEqual(0, status)
+
+        for t in threads:
+            t.join()
+
 
 class ThreadingExceptionTests(BaseTestCase):
     # A RuntimeError should be raised if Thread.start() is called
diff --git a/Python/ceval.c b/Python/ceval.c
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -364,29 +364,29 @@
     drop_gil(tstate);
 }
 
-/* This function is called from PyOS_AfterFork to ensure that newly
-   created child processes don't hold locks referring to threads which
-   are not running in the child process.  (This could also be done using
-   pthread_atfork mechanism, at least for the pthreads implementation.) */
+/* This function is called from PyOS_AfterFork to destroy all threads which are
+ * not running in the child process, and clear internal locks which might be
+ * held by those threads. (This could also be done using pthread_atfork
+ * mechanism, at least for the pthreads implementation.) */
 
 void
 PyEval_ReInitThreads(void)
 {
     _Py_IDENTIFIER(_after_fork);
     PyObject *threading, *result;
-    PyThreadState *tstate = PyThreadState_GET();
+    PyThreadState *current_tstate = PyThreadState_GET();
+    PyThreadState *tstate, *tstate_next;
 
     if (!gil_created())
         return;
     recreate_gil();
     pending_lock = PyThread_allocate_lock();
-    take_gil(tstate);
+    take_gil(current_tstate);
     main_thread = PyThread_get_thread_ident();
 
     /* Update the threading module with the new state.
      */
-    tstate = PyThreadState_GET();
-    threading = PyMapping_GetItemString(tstate->interp->modules,
+    threading = PyMapping_GetItemString(current_tstate->interp->modules,
                                         "threading");
     if (threading == NULL) {
         /* threading not imported */
@@ -399,6 +399,16 @@
     else
         Py_DECREF(result);
     Py_DECREF(threading);
+
+    /* destroy all threads except the current one */
+    for (tstate = PyInterpreterState_ThreadHead(current_tstate->interp);
+         tstate; tstate = tstate_next) {
+        tstate_next = PyThreadState_Next(tstate);
+        if (tstate != current_tstate) {
+            PyThreadState_Clear(tstate);
+            PyThreadState_Delete(tstate);
+        }
+    }
 }
 
 #else


More information about the Python-bugs-list mailing list