pub fn spawn(fut: impl futures::Future<Output = ()> + Send + 'static) {
#[cfg(feature = "rt-tokio")]
{
if tokio::runtime::Handle::try_current().is_ok() {
tokio::spawn(fut);
return;
}
}
std::thread::spawn(move || {
futures::executor::block_on(fut);
});
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[test]
fn test_spawn_without_tokio_runtime() {
let executed = Arc::new(Mutex::new(false));
let executed_clone = executed.clone();
spawn(async move {
*executed_clone.lock().unwrap() = true;
});
std::thread::sleep(Duration::from_millis(100));
assert!(*executed.lock().unwrap(), "Task should have been executed");
}
#[cfg(feature = "rt-tokio")]
#[tokio::test]
async fn test_spawn_with_tokio_runtime() {
let executed = Arc::new(Mutex::new(false));
let executed_clone = executed.clone();
spawn(async move {
*executed_clone.lock().unwrap() = true;
});
tokio::time::sleep(Duration::from_millis(100)).await;
assert!(
*executed.lock().unwrap(),
"Task should have been executed in tokio runtime"
);
}
#[cfg(feature = "rt-tokio")]
#[test]
fn test_spawn_fallback_when_no_tokio_context() {
let executed = Arc::new(Mutex::new(false));
let executed_clone = executed.clone();
assert!(
tokio::runtime::Handle::try_current().is_err(),
"This test should run outside tokio runtime"
);
spawn(async move {
*executed_clone.lock().unwrap() = true;
});
std::thread::sleep(Duration::from_millis(100));
assert!(
*executed.lock().unwrap(),
"Task should have been executed via thread fallback"
);
}
#[test]
fn test_spawn_multiple_tasks() {
let counter = Arc::new(Mutex::new(0));
let num_tasks = 5;
for i in 0..num_tasks {
let counter_clone = counter.clone();
spawn(async move {
std::thread::sleep(Duration::from_millis(10 * (i + 1) as u64));
*counter_clone.lock().unwrap() += 1;
});
}
std::thread::sleep(Duration::from_millis(200));
assert_eq!(
*counter.lock().unwrap(),
num_tasks,
"All {} tasks should have been executed",
num_tasks
);
}
#[test]
fn test_spawn_with_panic_handling() {
let executed_after_panic = Arc::new(Mutex::new(false));
let executed_clone = executed_after_panic.clone();
spawn(async {
panic!("This should not crash the main thread");
});
spawn(async move {
std::thread::sleep(Duration::from_millis(50));
*executed_clone.lock().unwrap() = true;
});
std::thread::sleep(Duration::from_millis(100));
assert!(
*executed_after_panic.lock().unwrap(),
"Normal task should execute even after another task panics"
);
}
}