use std::{
future::Future,
pin::Pin,
sync::OnceLock,
task::{Context, Poll},
time::{Duration, Instant},
};
use pin_project_lite::pin_project;
use super::{Executor, Sleep, Timer};
static HPX_RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
#[expect(
clippy::expect_used,
reason = "tokio runtime build failure is unrecoverable"
)]
fn hpx_runtime() -> tokio::runtime::Handle {
HPX_RUNTIME
.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_name("hpx-net")
.worker_threads(4)
.build()
.expect("failed to build the hpx connection runtime")
})
.handle()
.clone()
}
pub(crate) async fn on_hpx_runtime<F, T>(fut: F) -> Result<T, tokio::task::JoinError>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
let handle = hpx_runtime();
match tokio::runtime::Handle::try_current() {
Ok(current) if current.id() == handle.id() => Ok(fut.await),
_ => handle.spawn(fut).await,
}
}
#[non_exhaustive]
#[derive(Default, Debug, Clone)]
pub(crate) struct TokioExecutor {}
#[non_exhaustive]
#[derive(Default, Clone, Debug)]
pub(crate) struct TokioTimer;
pin_project! {
#[derive(Debug)]
struct TokioSleep {
#[pin]
inner: tokio::time::Sleep,
}
}
impl<Fut> Executor<Fut> for TokioExecutor
where
Fut: Future + Send + 'static,
Fut::Output: Send + 'static,
{
fn execute(&self, fut: Fut) {
hpx_runtime().spawn(fut);
}
}
impl TokioExecutor {
pub(crate) const fn new() -> Self {
Self {}
}
}
impl Timer for TokioTimer {
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Sleep>> {
Box::pin(TokioSleep {
inner: tokio::time::sleep(duration),
})
}
fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>> {
Box::pin(TokioSleep {
inner: tokio::time::sleep_until(deadline.into()),
})
}
fn now(&self) -> Instant {
tokio::time::Instant::now().into()
}
fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
if let Some(sleep) = sleep.as_mut().downcast_mut_pin::<TokioSleep>() {
sleep.reset(new_deadline);
}
}
}
impl TokioTimer {
pub(crate) const fn new() -> Self {
Self {}
}
}
impl Future for TokioSleep {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().inner.poll(cx)
}
}
impl Sleep for TokioSleep {}
impl TokioSleep {
fn reset(self: Pin<&mut Self>, deadline: Instant) {
self.project().inner.as_mut().reset(deadline.into());
}
}