Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed new sniffio semantics #38

Open
oremanj opened this issue Jun 14, 2023 · 0 comments · May be fixed by #39
Open

Proposed new sniffio semantics #38

oremanj opened this issue Jun 14, 2023 · 0 comments · May be fixed by #39

Comments

@oremanj
Copy link
Member

oremanj commented Jun 14, 2023

I'd like to make sniffio.current_async_library() return "a name describing the type of the nearest enclosing async event loop".

This means:

  • If you run it from an async task, it returns the kind of trap you need to yield (same as today). If there is something clever going on such that you can yield multiple libraries' traps, the value could be any of them (so sniffio doesn't specify whether a trio_asyncio.allow_asyncio function would see "trio" or "asyncio").
  • If you run it from a non-task context on behalf of a particular loop (Trio abort_fn or instrumentation call or run_sync_soon function, asyncio call_soon/call_later/add_reader/add_writer callback), it returns that loop. This is an indication that you could spawn a new task on that loop, such as by using trio.lowlevel.spawn_system_task or asyncio.create_task. (That loop is not necessarily the only loop you could spawn a task on, because of guest mode and trio-asyncio.) It suggests that iterating an async generator here would cause the generator to be finalized in that context, but can't guarantee that (e.g. trio-asyncio doesn't implement mode-sensitive async generator finalization).
  • If there are multiple "layers" of async loop (due to synchronous foo.run() from a bar task or guest mode or trio-asyncio) you get the innermost one.

A thread spawned by an async loop is not considered part of that async loop.

I think this would fix #35, and would be an easier way than #27 to achieve similar semantics to #27.

Proposed mechanism of action:

  • If you are implementing a regular event loop, set sniffio.thread_local.name to your loop name at the start of run() and reset it to its previous value when run() exits (including via exception).
  • If you are implementing a guest-mode event loop, set sniffio.thread_local.name to your loop name at the start of a guest tick and reset it to its previous value when the guest tick finishes and you return to the host loop.
  • If you are implementing one event loop using async tasks in another event loop (I think trio-asyncio is the only example of this), set sniffio.thread_local.name to your loop name before entering a coroutine you're hosting and reset it to its previous value after that tick.
  • If the thread-local is None, then sniffio will sniff for asyncio via asyncio.get_running_loop() (rather than today's current_task()).

This also has some performance advantages:

  • current_async_library is quite fast; check a single thread-local attribute, and if it's not set then call a simple function implemented in C (asyncio.get_running_loop).
  • Simple cases (with just one library per thread) only set the thread-local once per run, not once per task step.
  • All complex cases I'm aware of still work.

Thoughts?

@oremanj oremanj linked a pull request Jul 11, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant