]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-118095: Make sure that progress is made if there are pending calls being handled...
authorMark Shannon <mark@hotpy.org>
Wed, 1 May 2024 21:18:31 +0000 (22:18 +0100)
committerGitHub <noreply@github.com>
Wed, 1 May 2024 21:18:31 +0000 (22:18 +0100)
Include/internal/pycore_ceval_state.h
Python/ceval_gil.c

index 11f2a100bf531e6726b0f6a3fdebfbe549573687..376d96ad5d334cc1d9a6b281c5e6456fe52e5963 100644 (file)
@@ -41,7 +41,7 @@ struct _pending_call {
 #define MAXPENDINGCALLSLOOP_MAIN 0
 
 struct _pending_calls {
-    int busy;
+    PyThreadState *handling_thread;
     PyMutex mutex;
     /* Request for running pending calls. */
     int32_t npending;
index fdbb4882c3d71171fc21e2f025fb58d854ee35e8..e4f7217255efd839ce903511659d3a6a595b4e0b 100644 (file)
@@ -877,21 +877,20 @@ make_pending_calls(PyThreadState *tstate)
     /* Only one thread (per interpreter) may run the pending calls
        at once.  In the same way, we don't do recursive pending calls. */
     PyMutex_Lock(&pending->mutex);
-    if (pending->busy) {
+    if (pending->handling_thread != NULL) {
         /* A pending call was added after another thread was already
            handling the pending calls (and had already "unsignaled").
            Once that thread is done, it may have taken care of all the
            pending calls, or there might be some still waiting.
-           Regardless, this interpreter's pending calls will stay
-           "signaled" until that first thread has finished.  At that
-           point the next thread to trip the eval breaker will take
-           care of any remaining pending calls.  Until then, though,
-           all the interpreter's threads will be tripping the eval
-           breaker every time it's checked. */
+           To avoid all threads constantly stopping on the eval breaker,
+           we clear the bit for this thread and make sure it is set
+           for the thread currently handling the pending call. */
+        _Py_set_eval_breaker_bit(pending->handling_thread, _PY_CALLS_TO_DO_BIT);
+        _Py_unset_eval_breaker_bit(tstate, _PY_CALLS_TO_DO_BIT);
         PyMutex_Unlock(&pending->mutex);
         return 0;
     }
-    pending->busy = 1;
+    pending->handling_thread = tstate;
     PyMutex_Unlock(&pending->mutex);
 
     /* unsignal before starting to call callbacks, so that any callback
@@ -900,7 +899,7 @@ make_pending_calls(PyThreadState *tstate)
 
     int32_t npending;
     if (_make_pending_calls(pending, &npending) != 0) {
-        pending->busy = 0;
+        pending->handling_thread = NULL;
         /* There might not be more calls to make, but we play it safe. */
         signal_pending_calls(tstate, interp);
         return -1;
@@ -912,7 +911,7 @@ make_pending_calls(PyThreadState *tstate)
 
     if (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)) {
         if (_make_pending_calls(pending_main, &npending) != 0) {
-            pending->busy = 0;
+            pending->handling_thread = NULL;
             /* There might not be more calls to make, but we play it safe. */
             signal_pending_calls(tstate, interp);
             return -1;
@@ -923,7 +922,7 @@ make_pending_calls(PyThreadState *tstate)
         }
     }
 
-    pending->busy = 0;
+    pending->handling_thread = NULL;
     return 0;
 }