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

Nested sync_to_async(thread_sensitive=True) calls do not run in the same thread. #357

Open
charliesteele opened this issue Nov 18, 2022 · 1 comment

Comments

@charliesteele
Copy link

While testing, I noticed that nesting calls to async_to_sync and sync_to_async causes two calls to sync_to_async(thread_sensitive=True) to run in different threads if you call sync_to_async(thread_sensitive=False) in between those two calls. It seems that once sync_to_async is called with thread_sensitive=False, all subsequent (nested) calls with thread_sensitive=True will run on the new parent thread rather than the main thread.

Example Code

import threading
from asgiref.sync import async_to_sync, sync_to_async


def method5():
    print(f"Third call to sync_to_sync: {threading.get_ident()}")


async def method4():
    print(f"Third call to async_to_sync: {threading.get_ident()}")
    await sync_to_async(method5)()


def method3():
    print(f"Second call to sync_to_async: {threading.get_ident()}")
    async_to_sync(method4)()


async def method2():
    print(f"Second call to async_to_sync: {threading.get_ident()}")
    await sync_to_async(method3, thread_sensitive=False)()


def method1():
    print(f"First call to sync_to_async: {threading.get_ident()}")
    async_to_sync(method2)()


async def start():
    print(f"First call to async_to_sync: {threading.get_ident()}")
    await sync_to_async(method1)()

print(f"Main thread: {threading.get_ident()}")
async_to_sync(start)()

Output

Main thread: 4593313280
First call to async_to_sync: 123145368514560
First call to sync_to_async: 4593313280
Second call to async_to_sync: 123145368514560
Second call to sync_to_async: 123145373769728
Third call to async_to_sync: 123145368514560
Third call to sync_to_sync: 123145373769728

As you can see, the main thread is 4593313280. The first call to sync_to_async(thread_sensitive=True) runs on the main thread, as expected. The second call to sync_to_async(thread_sensitive=False) creates a new thread (123145373769728), as expected. However, the third and fourth calls to sync_to_async(thread_sensitive=True) run on thread 123145373769728 rather than the main thread.

This seems to contradict the behavior described in the README:

All synchronous code called through SyncToAsync and marked with thread_sensitive should run in the same thread as each other (and if the outer layer of the program is synchronous, the main thread)

Is this a bug? Or is this expected behavior, and merely a gap in the documentation?

Thanks!
Charlie

@charliesteele charliesteele changed the title Nested sync_to_async calls do not run in the same thread. Nested sync_to_async(thread_sensitive=True) calls do not run in the same thread. Nov 18, 2022
@andrewgodwin
Copy link
Member

Hmm, it's neither a bug nor necessarily intended - what is happening is that thread_sensitive is finding the first sync thread above it in the stack that it can reuse and doing so.

I'm tempted to just document the behaviour rather than trying to fix it, trying to undo this would be the stuff of nightmares.

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

No branches or pull requests

2 participants