awestlake87 / pyo3-asyncio

Other
300 stars 45 forks source link

Support separate Tokio runtimes #85

Open cretz opened 1 year ago

cretz commented 1 year ago

Currently Tokio support uses a static runtime. Is there any harm in supporting multiple runtimes? I was able to do it with:

impl pyo3_asyncio::generic::Runtime for TokioRuntime {
    type JoinError = tokio::task::JoinError;
    type JoinHandle = tokio::task::JoinHandle<()>;

    fn spawn<F>(fut: F) -> Self::JoinHandle
    where
        F: Future<Output = ()> + Send + 'static,
    {
        tokio::runtime::Handle::current().spawn(async move {
            fut.await;
        })
    }
}

and before I executed future_into_py from the outside, I just made sure I entered the Tokio runtime context. See any problems with this approach? Any thoughts on supporting explicitly provided runtimes instead of requiring pyo3_asyncio::generic::Runtime be implemented statically without self?

awestlake87 commented 1 year ago

Hey, I'm currently recovering from wrist surgery, so it might be awhile before I fully answer this (I'm pecking the keys rn). It's not problematic to do it this way AFAIK, and it sounds like you understand the restrictions on the calling context.

It'd be interesting to hear more about your use-case. Multiple tokio runtimes seems a bit unusual to me.

cretz commented 1 year ago

might be awhile before I fully answer this

No rush! We already have the implementation working on our side, just want to confirm I wasn't missing anything obvious.

It'd be interesting to hear more about your use-case. Multiple tokio runtimes seems a bit unusual to me.

I work on https://github.com/temporalio/sdk-python (deterministic code-based workflows which are basically a distributed, fault-tolerant asyncio event loop). This project helps us a lot.

Our metrics and logger telemetry options are thread-pool-wide and it's unwieldy to use task locals or resetting thread locals constantly for the use case, so we tie those options to the entire thread pool. Also we might let users set options for the thread pool one day and we really don't like globals, so letting the users control the instantiation of what we call a "runtime" (which is tokio runtime + telemetry) is helpful. Granted we default to just a singleton.