Message231912
If a task that is waiting on an asyncio.Condition is cancelled after notification but before having successfully reacquired the associated lock, the acquire() will be cancelled causing wait() to return without the lock held (violating wait()'s contract and leaving the program in an inconsistent state).
This can be reproduced in cases where there's some contention on the Condition's lock. For example:
import asyncio
loop = asyncio.get_event_loop()
cond = asyncio.Condition()
@asyncio.coroutine
def cond_wait_timeout(condition, timeout):
wait_task = asyncio.async(condition.wait())
loop.call_later(timeout, wait_task.cancel)
try:
yield from wait_task
return True
except asyncio.CancelledError:
print("Timeout (locked: {0})".format(condition.locked()))
return False
@asyncio.coroutine
def waiter():
yield from cond.acquire()
try:
print("Wait")
if (yield from cond_wait_timeout(cond, 1)):
# Cause some lock contention
print("Do work")
yield from asyncio.sleep(1)
finally:
cond.release()
@asyncio.coroutine
def notifier():
# Yield to the waiters
yield from asyncio.sleep(0.1)
yield from cond.acquire()
try:
print("Notify")
cond.notify_all()
finally:
cond.release()
loop.run_until_complete(asyncio.wait([waiter(), waiter(), notifier()]))
The most straightforward fix appears to be just to have wait() retry to acquire the lock, effectively ignoring cancellation at this point (since the condition has already finished waiting and just trying to reacquire the lock before returning). |
|
Date |
User |
Action |
Args |
2014-12-01 03:29:42 | dcoles | set | recipients:
+ dcoles, gvanrossum, vstinner, yselivanov |
2014-12-01 03:29:42 | dcoles | set | messageid: <1417404582.01.0.276361050449.issue22970@psf.upfronthosting.co.za> |
2014-12-01 03:29:41 | dcoles | link | issue22970 messages |
2014-12-01 03:29:40 | dcoles | create | |
|