use std::future::Future;
use std::sync::{Mutex, OnceLock};
use tokio::runtime::Runtime;
const WORKERS: usize = 2;
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
static INIT: Mutex<()> = Mutex::new(());
pub fn runtime() -> Result<&'static Runtime, String> {
if let Some(rt) = RUNTIME.get() {
return Ok(rt);
}
let _guard = INIT
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(rt) = RUNTIME.get() {
return Ok(rt);
}
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(WORKERS)
.thread_name("lean-ctx-gateway")
.enable_all()
.build()
.map_err(|e| format!("failed to start the gateway runtime: {e}"))?;
Ok(RUNTIME.get_or_init(|| rt))
}
pub fn block_on<F>(fut: F) -> Result<F::Output, String>
where
F: Future + Send,
F::Output: Send,
{
let rt = runtime()?;
if tokio::runtime::Handle::try_current().is_err() {
return Ok(rt.block_on(fut));
}
std::thread::scope(|scope| {
std::thread::Builder::new()
.name("lean-ctx-gateway-wait".into())
.spawn_scoped(scope, || rt.block_on(fut))
.map_err(|e| format!("failed to start a gateway wait thread: {e}"))?
.join()
.map_err(|_| "a gateway call panicked".to_string())
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_task_spawned_in_one_call_survives_into_the_next() {
let (tx, rx) = tokio::sync::oneshot::channel::<u8>();
let task = block_on(async { Some(tokio::spawn(async move { rx.await.ok() })) })
.unwrap()
.unwrap();
let value = block_on(async move {
tx.send(7).expect("the task's receiver is still alive");
task.await.expect("the task was not cancelled")
})
.unwrap();
assert_eq!(value, Some(7));
}
#[tokio::test(flavor = "current_thread")]
async fn usable_from_inside_an_async_task() {
assert_eq!(block_on(async { 41 + 1 }).unwrap(), 42);
}
#[test]
fn every_call_shares_one_runtime() {
assert!(std::ptr::eq(runtime().unwrap(), runtime().unwrap()));
}
#[tokio::test(flavor = "current_thread")]
async fn a_panicking_call_is_an_error_not_a_crash() {
let out = block_on(async {
assert!(!std::hint::black_box(true), "boom");
0_u8
});
assert_eq!(out.unwrap_err(), "a gateway call panicked");
}
}