dabeaz / curio

Good Curio!
Other
4.02k stars 241 forks source link

Add basic curio -> asyncio coroutine bridge. #188

Closed Fuyukai closed 7 years ago

Fuyukai commented 7 years ago

This allows you to run asyncio coroutines from within curio, using a new function curio.acb (Asyncio Curio Bridge).

I put this inside the kernel because it's the easiest and most user-friendly way of co-operating the two event loop types.

The asyncio event loop runs inside a thread that is booted when curio.run() starts, and stops when it exits. asyncio coroutines are submitted to the worker event loop via run_coroutine_threadsafe and the resulting future is then awaited on.

Right now, there's no additional tests or documentation.

dabeaz commented 7 years ago

Interesting. How hard do you think it would be to make this somewhat more like thread/process pools? For a thread-pool, there is a thread_pool attribute on the Kernel that only gets initialized if work is submitted to a thread. The thread pool then gets shutdown when the kernel goes away. So, the main difference is that the thread-setup and initialization would get pulled into the bridge.py file.

Actually, I'm now almost wondering if this thread pool, process pool, asyncio bridge stuff could be generalized into a more general kernel resource concept.

I'm not going to merge this right away, but let me fiddle around with this. Would love to get other's thoughts on it.

Fuyukai commented 7 years ago

I did have the original idea of making this work within the threadpool rather than a single thread, but I wasn't sure how to work this properly with asyncio's threadsafety requirements. I'll tinker and see if I can make it run inside the thread_pool.

dabeaz commented 7 years ago

I'm not sure it needs to run inside thread_pool. I was thinking more about it being initialized/shutdown in a manner similar to how thread pools do it. It could still be a separate thing.

Fuyukai commented 7 years ago

Oh, okay. This would be possible with a small trivial change. However, there is an impl detail about that: when should the worker loop stop running? Once the asyncio coro has finished running, when the Kernel.run has finished, or when the kernel has shutdown fully? Right now I've made it explicit about happening when the run() call has finished instead of at shutdown so that the two event loops are running concurrently but independantly rather than having asyncio as a "separate" thing.

dabeaz commented 7 years ago

I almost wonder if this could be implemented using one of Curio's new-fangled AsyncThread objects. Hopefully you don't mind me thinking out loud about it ;-).

Fuyukai commented 7 years ago

The acb function could be easily put inside an async thread - the only other await function then would be the waiting on the kernel trap. Then the fut.result() can be simply called.

jkbbwr commented 7 years ago

We are hitting issues at work were we want to do something async but all the existing infrastructure is asyncio only and this is pinching slightly. Something like this could improve the situation.

Something that should be importantly considered is the interaction of using curio queues from asyncio code.

dabeaz commented 7 years ago

Curious to know a bit more about the "asyncio" functionality you're trying to use. Admittedly, it's probably a bit insane to release a totally new and incompatible I/O library like Curio into the world. I've always considered it to be a bit of a long-play that would take some time to get up to speed on supporting different kinds of services and protocols.

dabeaz commented 7 years ago

I'm going to merge this, but may refactor it into using an async thread (which I think might be cleaner).

dabeaz commented 7 years ago

I've opened Issue #190 for further discussion of this feature as it evolves,