6

What's the idiomatic way to get a tokio runtime handle based on the current running environment?

  • For methods already run in tokio runtime, I want to use Handle.try_current().unwrap() to get the current one.
  • For methods not run in tokio, I can create a new one: Runtime::new().unwrap().handle().

However, when I wrote my code as:

fn get_runtime_handle() -> Handle {
    match Handle::try_current() {
        Ok(h) => h,
        Err(_) => Runtime::new().unwrap().handle().clone(),
    }
}

async fn a_async() -> Result<()> {
    ....
}

fn a() -> Result<()> {
   let handle = get_runtime_handle();
   handle.block_one (async { a_async().await; })
}

fn main() -> Result<()> {
    a();

    Ok(())
}

and call tokio::fs::read_dir inside, the code crash with Error: Custom { kind: Other, error: "background task failed" }.

And when I substitute handle.block_on with Runtime::new().unwrap().handle().block_on in main, the code runs successfully.

I suppose my get_runtime_handle function is problematic, what's the right way to express this? The full runnable code is here.

Besides, when the method get_runtime_handle is running inside a tokio runtime, other unit tests from the project complains:

thread 'main' panicked at 'Cannot start a runtime from within a runtime. 
This happens because a function (like `block_on`) attempted to block the
current thread while the thread is being used to drive asynchronous tasks.
yjshen
  • 6,583
  • 3
  • 31
  • 40

1 Answers1

8

The problem is the lifetime of your new runtime, it is dropped at the end of the get_runtime_handle(). You should return the runtime if the function create one.

use tokio::runtime::{Runtime, Handle};

fn get_runtime_handle() -> (Handle, Option<Runtime>) {
    match Handle::try_current() {
        Ok(h) => (h, None),
        Err(_) => {
              let rt = Runtime::new().unwrap();
              (rt.handle().clone(), Some(rt))
            }
    }


fn main() {
    // let _ = Runtime::new().unwrap();
    let (handle, rt) = get_runtime_handle();
}
Louis Dispa
  • 116
  • 3
  • Hi, thanks for the answer, but the second problem still exists: since I was implementing `fn a()` using `a_async()` using block_on, when `a()` was called inside a `tokio::main`, the `panicked at 'Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block .....shows. How should I deal with that? – yjshen Aug 18 '21 at 16:16
  • It is because when you use `tokio::main` the main function is inside a runtime, and like the error says you can't run a runtime inside a runtime. `block_on` is an entry point for a runtime, I am pretty sure `tokio::main` use it to run the `async main`. what you want to use in this case is `spawn` or `spawn_blocking`. – Louis Dispa Aug 18 '21 at 16:38