use std::future::Future;
use std::sync::Arc;
use crate::allocator::MkAsyncFrameAlloc;
tokio::task_local! {
static TASK_ALLOC: Arc<MkAsyncFrameAlloc>;
}
pub async fn with_allocator<F, R>(alloc: MkAsyncFrameAlloc, f: F) -> R
where
F: Future<Output = R>,
{
TASK_ALLOC.scope(Arc::new(alloc), f).await
}
pub fn current_allocator() -> Arc<MkAsyncFrameAlloc> {
TASK_ALLOC.with(|a| Arc::clone(a))
}
pub fn try_current_allocator() -> Option<Arc<MkAsyncFrameAlloc>> {
TASK_ALLOC.try_with(|a| Arc::clone(a)).ok()
}
pub fn mk_spawn<F>(f: F) -> tokio::task::JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let alloc = try_current_allocator();
tokio::spawn(async move {
if let Some(alloc) = alloc {
with_allocator((*alloc).clone(), f).await
} else {
f.await
}
})
}
pub fn mk_spawn_blocking<F, R>(f: F) -> tokio::task::JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
tokio::task::spawn_blocking(f)
}
pub async fn with_timeout<F, R>(
duration: std::time::Duration,
f: F,
) -> Result<R, tokio::time::error::Elapsed>
where
F: Future<Output = R>,
{
tokio::time::timeout(duration, f).await
}
pub async fn yield_now() {
tokio::task::yield_now().await
}
pub async fn sleep(duration: std::time::Duration) {
tokio::time::sleep(duration).await
}
#[cfg(test)]
mod tests {
use super::*;
use crate::allocator::MkAsyncFrameConfig;
#[tokio::test]
async fn test_task_local_allocator() {
let alloc = MkAsyncFrameAlloc::new(MkAsyncFrameConfig::default());
with_allocator(alloc, async {
let a = current_allocator();
assert_eq!(a.frame(), 0);
}).await;
}
#[tokio::test]
async fn test_spawn_propagates_context() {
let alloc = MkAsyncFrameAlloc::new(MkAsyncFrameConfig::default());
with_allocator(alloc, async {
let handle = mk_spawn(async {
let a = current_allocator();
a.frame()
});
let frame = handle.await.unwrap();
assert_eq!(frame, 0);
}).await;
}
#[tokio::test]
async fn test_timeout() {
let result = with_timeout(
std::time::Duration::from_millis(100),
async { 42 }
).await;
assert_eq!(result.unwrap(), 42);
}
}