Currently asyncio.Event use deque to store waiter futures. But now that future can be awaited multiple times, maybe use a single future will be faster?
class Event:
def __init__(self):
self._value = False
self.fut = asyncio.Future()
def is_set(self):
return self._value
def set(self):
if not self._value:
self._value = True
self.fut.set_result(True)
self.fut = asyncio.Future()
def clear(self):
self._value = False
async def wait(self):
if self._value:
return True
try:
await self.fut
return True
agronholm
(Alex Grönholm)
May 11, 2023, 6:06am
2
And if a task waiting on that event gets cancelled? What do you supposed happens to the future then?
I think creating task and cancelling task are task’s scope and has nothing to do with Event implementation? Current Event implementation creates future and add to waiters in wait
method, if you cancel the task then wait method will never run.
agronholm
(Alex Grönholm)
May 11, 2023, 7:11am
4
If you cancel a task, you also cancel any Future it’s waiting on. Now, if that future is shared among multiple tasks…
So in this test case from Cpython:
async def test_wait_cancel(self):
ev = asyncio.Event()
wait = asyncio.create_task(ev.wait())
asyncio.get_running_loop().call_soon(wait.cancel)
with self.assertRaises(asyncio.CancelledError):
await wait
self.assertFalse(ev._waiters)
if I cancel the waiter_task, ev.wait will be cancelled, but Future is in the event and not exposed, will that also be cancelled?
agronholm
(Alex Grönholm)
May 11, 2023, 7:42am
6
What do you mean, it’s not exposed? It will be the awaitable the waiting task would be awaiting on, yes?
I mean if multiple waiters wait for same event:
ev = asyncio.Event()
waiter1 = asyncio.create_task(ev.wait())
waiter2 = asyncio.create_task(ev.wait())
waiter3 = asyncio.create_task(ev.wait())
waiter4 = asyncio.create_task(ev.wait())
If I cancel waiter1 then call ev.set()
, other waiters won’t be awakened?
agronholm
(Alex Grönholm)
May 11, 2023, 7:52am
8
If you cancel waiter1, the shared future will be cancelled, so your call to ev.set()
will fail with InvalidStateError
because it’s trying to set a result on a cancelled future.
agronholm
(Alex Grönholm)
May 11, 2023, 7:56am
9
I should mention that I know all this from experience, as I once tried to implement an Event exactly like you described, and failed miserably
You are right. I understand why each waiter must have its own future now. Thanks for the explanation!
1 Like