[Python-ideas] Enabling Event.set to notify all waiters with an exception

Pau Freixes pfreixes at gmail.com
Wed Jul 19 02:37:07 EDT 2017


> 'Event' is designed as a lowish-level primitive: the idea is that it
> purely provides the operation of "waiting for something", and then you
> can compose it with other data structures to build whatever
> higher-level semantics you need. From this point of view, it doesn't
> make much sense to add features like exception throwing -- that would
> make it more useful for some particular cases, but add overhead that
> others don't want or need.

I do agree, indeed this was the main rationale that I wrote down. All
languages kept the same interface, Python shouldn't be different in
this. But, in the other side, the change added to the set method is
almost negligible, enabling as advantage reduce the complexity in the
client code to handle some situations. Just that, pros and cons.

> In this case, don't you want to cache an error return as well, anyway?

Not at all, the error is ephemeral, is never cached. If an error is
produced, perhaps a network outage, this is broadcasted to the other
waiting clients. Once there are no more waiting clients, the same DNS
resolution will have the chance to make the proper query. No error

> It sounds like you're reinventing the idea of a Future, which is
> intended as a multi-reader eventually-arriving value-or-error --
> exactly what you want here. So it seems like you could just write:
> # Conceptually correct, but subtly broken due to asyncio quirks
> if key not in cache:
>     cache[key] = asyncio.ensure_future(resolver.resolve(...))
> return await cache[key]

Not at all, the idea is taking into advantage the Event principle,
having a set of Futures waiting to be awakened and returning either a
value or exception.

> BUT, unfortunately, this turns out to be really broken when combined
> with asyncio's cancellation feature, so you shouldn't do this :-(.
> When using asyncio, you basically need to make sure to never await any
> given Future more than once.
> Maybe adding a shield() inside the await is the right solution? The
> downside is that you actually do want to propagate the cancellation
> into the resolution Task, just... only if *all* the callers are
> cancelled *and* only if you can make sure that the cancellation is not
> cached. It's quite tricky actually!

I've realized that I must protect the resolve() with a shield() for
the caller that holds the event. Otherwise, the other waiters will
have the chance to get a Canceled exception.

Regarding the propagation of the cancellation if and only if *all*
callers are canceled IMHO will fall on the side of a complex problem,
and the solution might be do nothing.

> But I don't think adding exception-throwing functionality to Event()
> is the right solution :-)

Then I will be forced to make the code stateful, getting as an output
as a complex solution if you compare it with the code that you might
get using the Event()


More information about the Python-ideas mailing list