]> granicus.if.org Git - python/commitdiff
bpo-32841: Fix cancellation in awaiting asyncio.Condition (#5665)
authorBar Harel <bzvi7919@gmail.com>
Wed, 14 Feb 2018 09:18:11 +0000 (11:18 +0200)
committerAndrew Svetlov <andrew.svetlov@gmail.com>
Wed, 14 Feb 2018 09:18:11 +0000 (11:18 +0200)
Lib/asyncio/locks.py
Lib/test/test_asyncio/test_locks.py
Misc/NEWS.d/next/Library/2018-02-14-00-21-24.bpo-32841.bvHDOc.rst [new file with mode: 0644]

index 508a2142d8427d7e539d146af10a6175f43c732a..91f7a01de8ad6cab3a6285b6058602c5ef4f7e7f 100644 (file)
@@ -358,12 +358,16 @@ class Condition(_ContextManagerMixin):
 
         finally:
             # Must reacquire lock even if wait is cancelled
+            cancelled = False
             while True:
                 try:
                     await self.acquire()
                     break
                 except futures.CancelledError:
-                    pass
+                    cancelled = True
+
+            if cancelled:
+                raise futures.CancelledError
 
     async def wait_for(self, predicate):
         """Wait until a predicate becomes true.
index 3e3dd799273edf62176712520e3399fa65eb640d..8642aa86b92b617d875b2a62824f862c75bc3d77 100644 (file)
@@ -214,11 +214,11 @@ class LockTests(test_utils.TestCase):
             call_count += 1
             await lock.acquire()
             lock_count += 1
-  
+
         async def lockandtrigger():
             await lock.acquire()
             self.loop.call_soon(trigger)
-          
+
         def trigger():
             t1.cancel()
             lock.release()
@@ -248,8 +248,6 @@ class LockTests(test_utils.TestCase):
         test_utils.run_briefly(self.loop)
         self.assertTrue(t3.cancelled())
 
-
-
     def test_finished_waiter_cancelled(self):
         lock = asyncio.Lock(loop=self.loop)
 
@@ -576,6 +574,31 @@ class ConditionTests(test_utils.TestCase):
 
         self.assertTrue(cond.locked())
 
+    def test_wait_cancel_after_notify(self):
+        # See bpo-32841
+        cond = asyncio.Condition(loop=self.loop)
+        waited = False
+
+        async def wait_on_cond():
+            nonlocal waited
+            async with cond:
+                waited = True  # Make sure this area was reached
+                await cond.wait()
+
+        waiter = asyncio.ensure_future(wait_on_cond(), loop=self.loop)
+        test_utils.run_briefly(self.loop)  # Start waiting
+
+        self.loop.run_until_complete(cond.acquire())
+        cond.notify()
+        test_utils.run_briefly(self.loop)  # Get to acquire()
+        waiter.cancel()
+        test_utils.run_briefly(self.loop)  # Activate cancellation
+        cond.release()
+        test_utils.run_briefly(self.loop)  # Cancellation should occur
+
+        self.assertTrue(waiter.cancelled())
+        self.assertTrue(waited)
+
     def test_wait_unacquired(self):
         cond = asyncio.Condition(loop=self.loop)
         self.assertRaises(
diff --git a/Misc/NEWS.d/next/Library/2018-02-14-00-21-24.bpo-32841.bvHDOc.rst b/Misc/NEWS.d/next/Library/2018-02-14-00-21-24.bpo-32841.bvHDOc.rst
new file mode 100644 (file)
index 0000000..a6d4566
--- /dev/null
@@ -0,0 +1,2 @@
+Fixed `asyncio.Condition` issue which silently ignored cancellation after
+notifying and cancelling a conditional lock. Patch by Bar Harel.