forge_core_utils/
tokio.rs

1use std::{future::Future, sync::OnceLock};
2
3use tokio::runtime::{Builder, Handle, Runtime, RuntimeFlavor};
4
5fn rt() -> &'static Runtime {
6    static RT: OnceLock<Runtime> = OnceLock::new();
7    RT.get_or_init(|| {
8        Builder::new_multi_thread()
9            .enable_all()
10            .build()
11            .expect("failed to build global Tokio runtime")
12    })
13}
14
15/// Run an async future from sync code safely.
16/// If already inside a Tokio runtime, it will use that runtime.
17pub fn block_on<F, T>(fut: F) -> T
18where
19    F: Future<Output = T> + Send,
20    T: Send,
21{
22    match Handle::try_current() {
23        // Already inside a Tokio runtime
24        Ok(h) => match h.runtime_flavor() {
25            // Use block_in_place so other tasks keep running.
26            RuntimeFlavor::MultiThread => tokio::task::block_in_place(|| rt().block_on(fut)),
27            // Spawn a new thread to avoid freezing a single-thread runtime.
28            RuntimeFlavor::CurrentThread | _ => std::thread::scope(|s| {
29                s.spawn(|| rt().block_on(fut))
30                    .join()
31                    .expect("thread panicked")
32            }),
33        },
34        // Outside Tokio: block normally.
35        Err(_) => rt().block_on(fut),
36    }
37}