[Python-checkins] bpo-39877: 4th take_gil() fix for daemon threads (GH-19080)

Victor Stinner webhook-mailer at python.org
Thu Mar 19 14:48:35 EDT 2020


https://github.com/python/cpython/commit/a36adfa6bbf5e612a4d4639124502135690899b8
commit: a36adfa6bbf5e612a4d4639124502135690899b8
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-03-19T19:48:25+01:00
summary:

bpo-39877: 4th take_gil() fix for daemon threads (GH-19080)

bpo-39877, bpo-40010: Add a third tstate_must_exit() check in
take_gil() to prevent using tstate which has been freed.

files:
M Python/ceval_gil.h

diff --git a/Python/ceval_gil.h b/Python/ceval_gil.h
index da2a1d6f47c95..b4cb59978269d 100644
--- a/Python/ceval_gil.h
+++ b/Python/ceval_gil.h
@@ -196,8 +196,7 @@ tstate_must_exit(PyThreadState *tstate)
        tstate->interp->runtime to support calls from Python daemon threads.
        After Py_Finalize() has been called, tstate can be a dangling pointer:
        point to PyThreadState freed memory. */
-    _PyRuntimeState *runtime = &_PyRuntime;
-    PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
+    PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
     return (finalizing != NULL && finalizing != tstate);
 }
 
@@ -243,20 +242,23 @@ take_gil(PyThreadState *tstate)
     }
 
     while (_Py_atomic_load_relaxed(&gil->locked)) {
-        int timed_out = 0;
-        unsigned long saved_switchnum;
-
-        saved_switchnum = gil->switch_number;
-
+        unsigned long saved_switchnum = gil->switch_number;
 
         unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
+        int timed_out = 0;
         COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
+
         /* If we timed out and no switch occurred in the meantime, it is time
            to ask the GIL-holding thread to drop it. */
         if (timed_out &&
             _Py_atomic_load_relaxed(&gil->locked) &&
             gil->switch_number == saved_switchnum)
         {
+            if (tstate_must_exit(tstate)) {
+                MUTEX_UNLOCK(gil->mutex);
+                PyThread_exit_thread();
+            }
+
             SET_GIL_DROP_REQUEST(ceval);
         }
     }
@@ -281,6 +283,19 @@ take_gil(PyThreadState *tstate)
     MUTEX_UNLOCK(gil->switch_mutex);
 #endif
 
+    if (tstate_must_exit(tstate)) {
+        /* bpo-36475: If Py_Finalize() has been called and tstate is not
+           the thread which called Py_Finalize(), exit immediately the
+           thread.
+
+           This code path can be reached by a daemon thread which was waiting
+           in take_gil() while the main thread called
+           wait_for_thread_shutdown() from Py_Finalize(). */
+        MUTEX_UNLOCK(gil->mutex);
+        drop_gil(ceval, ceval2, tstate);
+        PyThread_exit_thread();
+    }
+
     if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
         RESET_GIL_DROP_REQUEST(ceval, ceval2);
     }
@@ -293,27 +308,13 @@ take_gil(PyThreadState *tstate)
         COMPUTE_EVAL_BREAKER(ceval, ceval2);
     }
 
-    int must_exit = tstate_must_exit(tstate);
-
     /* Don't access tstate if the thread must exit */
-    if (!must_exit && tstate->async_exc != NULL) {
+    if (tstate->async_exc != NULL) {
         _PyEval_SignalAsyncExc(tstate);
     }
 
     MUTEX_UNLOCK(gil->mutex);
 
-    if (must_exit) {
-        /* bpo-36475: If Py_Finalize() has been called and tstate is not
-           the thread which called Py_Finalize(), exit immediately the
-           thread.
-
-           This code path can be reached by a daemon thread which was waiting
-           in take_gil() while the main thread called
-           wait_for_thread_shutdown() from Py_Finalize(). */
-        drop_gil(ceval, ceval2, tstate);
-        PyThread_exit_thread();
-    }
-
     errno = err;
 }
 



More information about the Python-checkins mailing list