1#[derive(Debug)]
11pub struct RuntimeBridgeError(String);
12
13impl std::fmt::Display for RuntimeBridgeError {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 write!(f, "runtime bridge failed: {}", self.0)
16 }
17}
18
19impl std::error::Error for RuntimeBridgeError {}
20
21pub fn block_on_ambient<F, T>(future: F) -> Result<T, RuntimeBridgeError>
30where
31 F: std::future::Future<Output = T> + Send,
32 T: Send,
33{
34 if let Ok(handle) = tokio::runtime::Handle::try_current() {
35 return match handle.runtime_flavor() {
36 tokio::runtime::RuntimeFlavor::MultiThread => {
37 Ok(tokio::task::block_in_place(|| handle.block_on(future)))
38 }
39 tokio::runtime::RuntimeFlavor::CurrentThread => std::thread::scope(|scope| {
40 scope
41 .spawn(|| {
42 let runtime = tokio::runtime::Builder::new_current_thread()
43 .enable_all()
44 .build()
45 .map_err(|error| RuntimeBridgeError(error.to_string()))?;
46 Ok(runtime.block_on(future))
47 })
48 .join()
49 .map_err(|_| {
50 RuntimeBridgeError("transient runtime thread panicked".to_string())
51 })?
52 }),
53 _ => Ok(handle.block_on(future)),
54 };
55 }
56
57 let runtime = tokio::runtime::Builder::new_current_thread()
58 .enable_all()
59 .build()
60 .map_err(|error| RuntimeBridgeError(error.to_string()))?;
61 Ok(runtime.block_on(future))
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn completes_future_outside_any_runtime() {
70 let value = block_on_ambient(async { 41 + 1 }).expect("bridge must succeed");
71 assert_eq!(value, 42);
72 }
73
74 #[test]
75 fn completes_future_inside_multi_thread_runtime() {
76 let runtime = tokio::runtime::Builder::new_multi_thread()
77 .worker_threads(2)
78 .build()
79 .expect("test runtime must build");
80 let value = runtime.block_on(async move {
81 let inner = tokio::task::spawn_blocking(|| {
82 block_on_ambient(async { std::string::String::from("from-blocking") })
83 .expect("bridge must succeed")
84 })
85 .await
86 .expect("spawn_blocking must succeed");
87 assert_eq!(inner, "from-blocking");
88 block_on_ambient(async { true }).expect("bridge must succeed")
89 });
90 assert!(value);
91 }
92}