use std::future::Future;
use std::sync::LazyLock;
use anyhow::{Result, bail};
use tokio::runtime::{Builder, Handle, Runtime};
static RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
Builder::new_current_thread()
.enable_all()
.thread_name("servo-fetch-runtime")
.build()
.expect("failed to build servo-fetch tokio runtime")
});
pub(crate) fn block_on<F: Future>(future: F) -> Result<F::Output> {
if Handle::try_current().is_ok() {
bail!(
"servo-fetch sync API cannot be called from within a Tokio runtime; \
wrap the call in `tokio::task::spawn_blocking`"
);
}
Ok(RUNTIME.block_on(future))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn runs_future_in_sync_context() {
let n = block_on(async { 42 }).unwrap();
assert_eq!(n, 42);
}
#[test]
fn rejects_call_from_async_context() {
let outer = Builder::new_current_thread().enable_all().build().unwrap();
let result = outer.block_on(async { block_on(async { 1 }) });
assert!(result.is_err(), "should refuse to run inside a tokio runtime");
assert!(
result.unwrap_err().to_string().contains("Tokio runtime"),
"error message should mention Tokio runtime"
);
}
}