<div dir="ltr">Hi,<br><br>I noticed there is currently no standard solution to submit a job from a thread to an asyncio event loop.<br><br>Here's what the asyncio documentation says about concurrency and multithreading:<br><br>> To schedule a callback from a different thread, the BaseEventLoop.call_soon_threadsafe() method should be used. <br>> Example to schedule a coroutine from a different thread: <br>>     loop.call_soon_threadsafe(asyncio.async, coro_func())<br><br>The issue with this method is the loss of the coroutine result.<br><br>One way to deal with this issue is to connect the asyncio.Future returned by async (or ensure_future) to a concurrent.futures.Future. It is then possible to use a subclass of concurrent.futures.Executor to submit a callback to an asyncio event loop. Such an executor can also be used to set up communication between two event loops using run_in_executor.<br><br>I posted an implementation called LoopExecutor on GitHub:<br><a href="https://github.com/vxgmichel/asyncio-loopexecutor" target="_blank">https://github.com/vxgmichel/asyncio-loopexecutor</a><br>The repo contains the loopexecutor module along with tests for several use cases. The README describes the whole thing (context, examples, issues, implementation).<br><br>It is interesting to note that this executor is a bit different than ThreadPoolExecutor and ProcessPoolExecutor since it can also submit a coroutine function. Example:<br><br>with LoopExecutor(loop) as executor:<br>    future = executor.submit(operator.add, 1, 2)<br>    assert future.result() == 3<br>    future = executor.submit(asyncio.sleep, 0.1, result=3)<br>    assert future.result() == 3<br><br>This works in both cases because submit always cast the given function to a coroutine. That means it would also work with a function that returns a Future.<br><br>Here's a few topic related to the current implementation that might be interesting to discuss:<br><br>- possible drawback of casting the callback to a coroutine<br>- possible drawback of concurrent.future.Future using asyncio.Future._copy_state<br>- does LoopExecutor need to implement the shutdown method?<br>- removing the limitation in run_in_executor (can't submit a coroutine function)<br>- adding a generic Future connection function in asyncio<br>- reimplementing wrap_future with the generic connection<br>- adding LoopExecutor to asyncio (or concurrent.futures)<br><br>At the moment, the interaction between asyncio and concurrent.futures only goes one way. It would be nice to have a standard solution (LoopExecutor or something else) to make it bidirectional.<br><br>Thanks,<br><br>Vincent<br> <br></div>