I’m hoping this is the right forum for this…
We recently hit a curious issue with Channels/Daphne & Django 3.0’s
async_unsafe() checks, and a multi-threaded context, such as running under the Django auto-reloader. (Original Issue)
The long and short of it was that Daphe (which uses Twisted) was instantiating Twisted’s
asyncioreactor in the main thread with a (default)
asyncio.get_event_loop(). The Django auto-reloader was then running this in a second thread. In the main thread the auto-reloader was terminating the process, causing a call to close all database connections.
This last is protected by Django 3.0’s
async_unsafe() decorator, which calls
asyncio.get_event_loop(), which being the main thread returns same default event loop that Twisted was using in the second thread. Because that event loop was running,
async_unsafe() raised, which isn’t strictly correct in this circumstance, but… (read on).
We were able to work around this bug by giving Twisted it’s own event loop, but it seems likely that this kind of situation will come up in the future. (Someone, somewhere will pass event loops around, and trick
async_unsafe() into a false positive.)
So… the ideal solution is to have
async_unsafe() be able to say to the event loop, not only “are you running?” but, “are you running in this thread?” (i.e. is this check happening in a task that you are running, however nestedly?)
Is that possible/feasible?
I suspect I haven’t provided enough context:
async_unsafe()is meant to decorate blocking calls so you don’t call then inside a coroutine.
- It’s not long. Source is at django/utils/asyncio.py (Sorry, the editor will only let me put in two links…)
The essence is this:
# Detect a running event loop in this thread. try: event_loop = asyncio.get_event_loop() except RuntimeError: pass else: if event_loop.is_running(): raise SynchronousOnlyOperation(message)
But in the situation that we hit, the “in this thread” wasn’t true.
Please let me know if I can provide more context.