Can we make asyncio event.wait with timeout atomic?

This is continued from: Asyncio timeout on event wait in “help” section.


When adding a timeout to asyncio.Event.wait in order to get either an event notificaton or a timeout, we could write:

    try:
        async with asyncio.timeout(delay):
            await event.wait()
    except TimeoutError:
        print("No event")
    else:
        print("Event!")

However, following can happen in this code block:

  1. the timeout occurs internally (scheduled call_later callback is executed)
  2. then another task sets the event
  3. then __aexit__ of the async context manager handles the timeout from 1.
  4. program prints “No event” - which is not true after 2.

It’s a classic check-then-act race condition, but IMO not at all obvious, because it is hidden in implementation. I think this should get some attention, because it’s a possible source of bugs. But if you think the opposite and we should do nothing, please respond too.

Options are:

  • provide an atomic timeout handling for single operations naturally expected either to succed or to raise. Examples: event.wait or queue.get (I didn’t check). By atomic I mean that timeout handling cannot be interleaved with other tasks running and possibly changing the outcome of the operation. This would be nice, but maybe difficult or not possible.
  • add a note to the documentation
1 Like

If the timeout callback triggered before the event was set, the event was arguably late. But in general, the ordering of events at very small time scales is dependent on the implementation of the event loop and that’s fine. Races gonna race.

Event is a synchronisation primitive. I find it not acceptable if operations on it could get re-ordered.