From 14d05942ea21d4a95c9eac1f0bae53540d8f1602 Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
 <31488909+miss-islington@users.noreply.github.com>
Date: Mon, 16 May 2022 08:25:31 -0700
Subject: [PATCH] gh-92530: Fix an issue that occurred after interrupting
 threading.Condition.notify (GH-92534) (GH-92831)

If Condition.notify() was interrupted just after it released the waiter lock,
but before removing it from the queue, the following calls of notify() failed
with RuntimeError: cannot release un-acquired lock.
(cherry picked from commit 70af994fee7c0850ae859727d9468a5f29375a38)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
---
 Lib/threading.py                              | 21 ++++++++++++-------
 ...2-05-09-09-28-02.gh-issue-92530.M4Q1RS.rst |  2 ++
 2 files changed, 16 insertions(+), 7 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Library/2022-05-09-09-28-02.gh-issue-92530.M4Q1RS.rst

diff --git a/Lib/threading.py b/Lib/threading.py
index a3cb245ab96..565f868e928 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -360,14 +360,21 @@ def notify(self, n=1):
         """
         if not self._is_owned():
             raise RuntimeError("cannot notify on un-acquired lock")
-        all_waiters = self._waiters
-        waiters_to_notify = _deque(_islice(all_waiters, n))
-        if not waiters_to_notify:
-            return
-        for waiter in waiters_to_notify:
-            waiter.release()
+        waiters = self._waiters
+        while waiters and n > 0:
+            waiter = waiters[0]
+            try:
+                waiter.release()
+            except RuntimeError:
+                # gh-92530: The previous call of notify() released the lock,
+                # but was interrupted before removing it from the queue.
+                # It can happen if a signal handler raises an exception,
+                # like CTRL+C which raises KeyboardInterrupt.
+                pass
+            else:
+                n -= 1
             try:
-                all_waiters.remove(waiter)
+                waiters.remove(waiter)
             except ValueError:
                 pass
 
diff --git a/Misc/NEWS.d/next/Library/2022-05-09-09-28-02.gh-issue-92530.M4Q1RS.rst b/Misc/NEWS.d/next/Library/2022-05-09-09-28-02.gh-issue-92530.M4Q1RS.rst
new file mode 100644
index 00000000000..8bb8ca0488c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-05-09-09-28-02.gh-issue-92530.M4Q1RS.rst
@@ -0,0 +1,2 @@
+Fix an issue that occurred after interrupting
+:func:`threading.Condition.notify`.
-- 
GitLab