use std::{cell::UnsafeCell, time::Duration};
#[cfg(feature = "signals")]
use crate::server::task_tracker::TaskTracker;
use crate::tracing;
use async_executor::LocalExecutor;
#[derive(Clone, Copy)]
pub struct Spawner<'e> {
ex: *const Executor<'e>,
alive: *mut bool,
}
impl<'e> Spawner<'e> {
pub fn spawn<T: 'e>(&self, future: impl Future<Output = T> + 'e) {
unsafe {
if *self.alive {
#[cfg(feature = "signals")]
let future = {
let guard = (*self.ex).tracker.track();
async move {
let _guard = guard;
future.await
}
};
(*self.ex).executor.spawn(future).detach();
} else {
tracing::warn!("Attempting to spawn a task on an inactive executor");
}
}
}
#[allow(unused)]
pub(super) async fn wait_until_empty(self, timeout: Duration) {
#[cfg(feature = "signals")]
unsafe {
if *self.alive {
(*self.ex).tracker.wait_until_empty(timeout).await
}
}
}
}
pub struct Executor<'e> {
executor: LocalExecutor<'e>,
#[cfg(feature = "signals")]
tracker: TaskTracker,
alive: UnsafeCell<bool>,
}
impl<'e> Executor<'e> {
pub fn new() -> Self {
Self::default()
}
pub fn spawner(&self) -> Spawner<'e> {
Spawner {
ex: self,
alive: self.alive.get(),
}
}
#[inline(always)]
pub fn run<T>(&self, future: impl Future<Output = T>) -> impl Future<Output = T> {
self.executor.run(future)
}
#[inline(always)]
pub fn try_tick(&self) -> bool {
self.executor.try_tick()
}
}
impl<'e> Default for Executor<'e> {
fn default() -> Self {
Self {
executor: LocalExecutor::new(),
#[cfg(feature = "signals")]
tracker: TaskTracker::new(),
alive: UnsafeCell::new(true),
}
}
}
impl<'e> Drop for Executor<'e> {
fn drop(&mut self) {
unsafe {
*self.alive.get() = false;
}
}
}