[Async-sig] Inadvertent layering of synchronous code as frameworks adopt asyncio

Guido van Rossum guido at python.org
Mon Mar 25 19:37:07 EDT 2019


Thanks for bringing this up -- I think it will be good to get to the bottom
of this, before the Jupyter folks accidentally get everyone to use an
approach that is unsound. Maybe they can be redirected to a better
strategy, or maybe they can convince us to change asyncio: it's totally
possible that the reasoning behind this restriction is no longer really
valid.

I expect that Yury will have to jump in, but I believe he's busy with a
release. I also hope Nathaniel has something to say -- I wonder if trio
supports nested event loops? (And maybe a Tornado developer?)

In the meantime, my take on this is that a nested event loop invocation by
a nominally synchronous function, i.e. something that calls
run_until_complete(), violates a guarantee that asyncio makes: callbacks
may only run when `await` is called, and thus any state that is shared
between callbacks or tasks is implicitly protected from mutation *between*
`await` calls. This means that asyncio programmers don't have to worry
about a class of nasty threading bugs caused by arbitrary interleaving of
threads. For example, take these three lines from asyncio/queue.py:

        self._put(item)
        self._unfinished_tasks += 1
        self._finished.clear()

Because there's no `await` visible here, as a reader I know that the
accounting of finished and unfinished tasks here will always be in a
consistent state when some other piece of code receives control.

Also, I looked into nest_asyncio, and I definitely think it should not be
recommended -- it disables use of the asyncio accelerator classes
implemented in C (starting with Python 3.6).

One final thing. What we're talking about here is nested invocation of the
"event pump". There's another form of nested event loop invocation where
two separate event loop objects exist. That is a much more worrisome
scenario, because callbacks associated with one event loop won't run at all
while one is waiting for a task on the other loop. Fortunately that's not
what is requested here. :-)

--Guido

On Fri, Mar 22, 2019 at 10:00 AM Daniel Nugent <nugend at gmail.com> wrote:

> Hello, I was hoping that the Async SIG might have some suggestions on how
> to deal with this sort of issue:
>
> More frameworks are adopting asyncio as time marches on. A notable example
> of this is Jupyter and the Python kernels it supports (please see
> announcement here: blog.jupyter.org/ipython-7-0-async-repl-a35ce050f7f7).
> This was enabled by a change in Tornado version 5.0 to support the asyncio
> event loop.
>
> The problem is that this makes any code which inadvertently ran an asyncio
> event loop (that is, calls through a blocking API provided by a library
> implemented in asyncio) fail. The Jupyter developers seem to feel that this
> is a deficiency in the asyncio event loop model and suggest all users
> encountering such a problem adopt the patch module nest_asyncio (
> github.com/jupyter/notebook/issues/3397#issuecomment-419474214).
>
> However, it is my understanding that the Python team strongly feels this
> is not the correct path: bugs.python.org/issue33523
> bugs.python.org/issue29558bugs.python.org/issue22239
>
> I have been trying to figure out the right way to work around this issue
> such that a library implemented with asyncio that provides a synchronous
> API will not cause a problem and have come up short thus far. I was
> considering investigating the janus sync/async queue as a way of
> facilitating communication between the different modes, but I am not sure
> that the scenario I describe reflects the intended usage. That is, an outer
> asyncio driven program fragment calls into middle synchronous code, which
> calls to inner asynchronous code. It seems that janus is mostly intended to
> facilitate communication between a single outer asynchronous layer and an
> inner synchronous layer. However, the documentation is a little sparse so I
> may just not understand it yet.
>
> I don't believe I'm the only person struggling to figure out how to deal
> with this sort of situation, so I think this would be useful for the
> community to figure out a solid answer to. For example, I found this blog
> post which outlines the same sort of problem and suggests that they elected
> to use nest_asyncio threespeedlogic.com/python-tworoutines.html
>
> If anyone could provide guidance on how to go forward, I would appreciate
> it.
>
> I would also like to understand the decision making around not allowing
> event loop nesting/reentrancy as seen in the bugs.python.orgissues I
> referenced so that I may explain the tradeoffs of possibly adopting the
> nest_asyncio patch module (for the sake of argument, lets ignore the
> possible issues with non-standard event loops) better to my peers.
>
> Thank you,
>
> -Dan Nugent
> _______________________________________________
> Async-sig mailing list
> Async-sig at python.org
> https://mail.python.org/mailman/listinfo/async-sig
> Code of Conduct: https://www.python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/async-sig/attachments/20190325/791f4216/attachment.html>


More information about the Async-sig mailing list