1use std::future::Future;
5use std::sync::OnceLock;
6
7use tokio::runtime::{Handle, Runtime, RuntimeFlavor};
8
9pub fn shared() -> &'static Runtime {
11 static RT: OnceLock<Runtime> = OnceLock::new();
12 RT.get_or_init(|| {
13 tokio::runtime::Builder::new_multi_thread()
14 .thread_name("comindly")
15 .enable_all()
16 .build()
17 .expect("tokio runtime")
18 })
19}
20
21pub fn block_on<F>(f: F) -> F::Output
24where
25 F: Future + Send,
26 F::Output: Send,
27{
28 match Handle::try_current() {
29 Ok(h) if h.runtime_flavor() == RuntimeFlavor::MultiThread => {
30 tokio::task::block_in_place(|| h.block_on(f))
31 }
32 Ok(_) => std::thread::scope(|s| {
33 s.spawn(|| shared().block_on(f))
34 .join()
35 .expect("a worker thread failed")
36 }),
37 Err(_) => shared().block_on(f),
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn blocking_calls_nest_inside_the_runtime_and_its_workers() {
47 let from_main = shared().block_on(async { block_on(async { block_on(async { 1 }) }) });
48 let on_worker = shared()
49 .block_on(shared().spawn(async { block_on(async { block_on(async { 2 }) }) }))
50 .unwrap();
51 assert_eq!((from_main, on_worker), (1, 2));
52 }
53}