agronholm / anyio

High level asynchronous concurrency and networking framework that works on top of either trio or asyncio
MIT License
1.83k stars 138 forks source link

Event.set() with an `EventAdapter` does not work unless already running in an async event loop #819

Open fyellin opened 3 weeks ago

fyellin commented 3 weeks ago

Things to check first

AnyIO version

4.6.2

Python version

3.12

What happened?

Unexpected exception. Code that runs fine with trio and asyncio fails with anyio.

How can we reproduce the bug?

In a newly started Python:

from anyio import Event
event = Event()
event.set()

This code works fine if we replace anyio with trio or asyncio. On anyio I get the error message AsyncLibraryNotFound.

It seems strange that anyio has no problem creating the Event, but fails in setting it.

agronholm commented 3 weeks ago

Well, you're not running any async event loop here, so it doesn't know how to perform the notification, that's all. In an earlier version you wouldn't have gotten even that far, as already the creation of an Event would have failed for the same reason.

agronholm commented 3 weeks ago

On asyncio or trio, their own implementations know to use an asyncio or trio event loop, respectively, but with AnyIO, which event loop should it even try here? If this is a pressing use case, the current event proxy class could be extended to allow the set() operation to just flip the flag if there's no backing implementation.

fyellin commented 3 weeks ago

At least in my case, I'm trying to create an object awaiter = WaitForCallback() for which the user can then get a value synchronously with "awaiter.get_sync()" or asynchronously with await awaiter.get_async().

The code is a lot simpler when the callback can just do callback_happened.set() and not care whether it's really in an event loop or not. In the non-event-loop case, callback_happened is an expensive boolean, and in the yes-event-loop case I can await callback_happend.wait()

I certainly realize this isn't a high-priority bug. I can work around it. But it is an incompatibility.

On Tue, Nov 5, 2024 at 11:36 AM Alex Grönholm @.***> wrote:

Well, you're not running any async event loop here, so it doesn't know how to perform the notification, that's all. In an earlier version you wouldn't have gotten even that far, as already the creation of an Event would have failed for the same reason.

— Reply to this email directly, view it on GitHub https://github.com/agronholm/anyio/issues/819#issuecomment-2458005933, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABC2LIPPPKAX6W7EMPFLDZDZ7EM25AVCNFSM6AAAAABRHI2K6SVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINJYGAYDKOJTGM . You are receiving this because you authored the thread.Message ID: @.***>

agronholm commented 3 weeks ago

Do you mean you have a callback that sets an event from a thread that is not running an event loop?

fyellin commented 3 weeks ago

This is all part of a library that's supposed to work whether or not there is an event loop. Many user-callable methods have both a _sync and an _async version depending on whether they want to use the library without or with an event loop.

The callback does not know whether it is running from a thread with an event loop or not. When I was agnostic about no-event-loop vs asyncio, everything worked. When I increased my agnosticism to include trio (and switched to anyio), that's when I got an error.

Again. This isn't urgent. I can work around it. But at least I wanted to explain the use case.

On Tue, Nov 5, 2024 at 12:02 PM Alex Grönholm @.***> wrote:

Do you mean you have a callback that sets an event from a thread that is not running an event loop?

— Reply to this email directly, view it on GitHub https://github.com/agronholm/anyio/issues/819#issuecomment-2458049969, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABC2LIIVCU23DMHCNXVTERDZ7EP6DAVCNFSM6AAAAABRHI2K6SVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINJYGA2DSOJWHE . You are receiving this because you authored the thread.Message ID: @.***>

agronholm commented 3 weeks ago

If the event is supposed to notify code running in an event loop thread, then setting the event from just any thread is a big no-no! These events (or asyncio or trio events for that matter) are NOT thread safe! It's imperative to call one of the thread-safe entry mechanisms in order to correctly notify waiters in an async event loop.