]> granicus.if.org Git - python/commitdiff
bpo-32841: Fix cancellation in awaiting asyncio.Condition (GH-5665) (GH-5683)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 14 Feb 2018 10:10:18 +0000 (02:10 -0800)
committerAndrew Svetlov <andrew.svetlov@gmail.com>
Wed, 14 Feb 2018 10:10:18 +0000 (12:10 +0200)
(cherry picked from commit 5746510b7aef423fa4afc92b2abb919307b1dbb9)

Co-authored-by: Bar Harel <bzvi7919@gmail.com>
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 14d21ff4e6f752993d19a974181f47a8be826a96..8ee7e2ea045f9b9270d21496e12d757079d41ec5 100644 (file)
@@ -349,12 +349,16 @@ class Condition(_ContextManagerMixin):
 
         finally:
             # Must reacquire lock even if wait is cancelled
+            cancelled = False
             while True:
                 try:
                     yield from self.acquire()
                     break
                 except futures.CancelledError:
-                    pass
+                    cancelled = True
+
+            if cancelled:
+                raise futures.CancelledError
 
     @coroutine
     def wait_for(self, predicate):
index 835d09ffc5d251485f4ec36a1c40f8567ca335d7..8686395da16f63a544f753609ce0bd651ab32418 100644 (file)
@@ -190,11 +190,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()
@@ -224,8 +224,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)
 
@@ -557,6 +555,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.