strands_agents/
async_utils.rs

1//! Private async execution utilities.
2
3use std::future::Future;
4
5use tokio::runtime::Handle;
6
7/// Run an async function, handling event loop conflicts.
8///
9/// This utility handles the common pattern of running async code from sync contexts
10/// by detecting if we're already in a tokio runtime and using the appropriate method.
11///
12/// # Arguments
13///
14/// * `async_func` - A callable that returns an awaitable
15///
16/// # Returns
17///
18/// The result of the async function
19pub fn run_async<F, T>(async_func: F) -> T
20where
21    F: Future<Output = T> + Send,
22    T: Send,
23{
24
25    match Handle::try_current() {
26        Ok(_handle) => {
27
28            std::thread::scope(|s| {
29                s.spawn(move || {
30                    let rt = tokio::runtime::Builder::new_current_thread()
31                        .enable_all()
32                        .build()
33                        .expect("Failed to create runtime");
34                    rt.block_on(async_func)
35                })
36                .join()
37                .expect("Thread panicked")
38            })
39        }
40        Err(_) => {
41
42            let rt = tokio::runtime::Builder::new_current_thread()
43                .enable_all()
44                .build()
45                .expect("Failed to create runtime");
46            rt.block_on(async_func)
47        }
48    }
49}
50
51/// Spawn an async task and return a handle to it.
52///
53/// This is a convenience wrapper around tokio::spawn that works
54/// from both sync and async contexts.
55pub fn spawn_async<F, T>(async_func: F) -> tokio::task::JoinHandle<T>
56where
57    F: Future<Output = T> + Send + 'static,
58    T: Send + 'static,
59{
60    match Handle::try_current() {
61        Ok(handle) => handle.spawn(async_func),
62        Err(_) => panic!("spawn_async must be called from within a tokio runtime"),
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_run_async_simple() {
72        async fn add(a: i32, b: i32) -> i32 {
73            a + b
74        }
75
76        let result = run_async(add(2, 3));
77        assert_eq!(result, 5);
78    }
79
80    #[tokio::test]
81    async fn test_run_async_from_async_context() {
82        async fn multiply(a: i32, b: i32) -> i32 {
83            a * b
84        }
85
86
87        let result = run_async(multiply(4, 5));
88        assert_eq!(result, 20);
89    }
90
91    #[tokio::test]
92    async fn test_spawn_async() {
93        async fn compute() -> String {
94            "hello".to_string()
95        }
96
97        let handle = spawn_async(compute());
98        let result = handle.await.unwrap();
99        assert_eq!(result, "hello");
100    }
101}
102