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<'exec> {
ex: *const Executor<'exec>,
alive: *mut bool,
}
impl<'exec> Spawner<'exec> {
pub fn spawn<T: 'exec>(&self, future: impl Future<Output = T> + 'exec) {
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<'exec> {
executor: LocalExecutor<'exec>,
#[cfg(feature = "signals")]
tracker: TaskTracker,
alive: UnsafeCell<bool>,
}
impl<'exec> Executor<'exec> {
pub fn new() -> Self {
Self::default()
}
pub fn spawner(&self) -> Spawner<'exec> {
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<'exec> Default for Executor<'exec> {
fn default() -> Self {
Self {
executor: LocalExecutor::new(),
#[cfg(feature = "signals")]
tracker: TaskTracker::new(),
alive: UnsafeCell::new(true),
}
}
}
impl<'exec> Drop for Executor<'exec> {
fn drop(&mut self) {
unsafe {
*self.alive.get() = false;
}
}
}
#[macro_export]
macro_rules! spawn_with_span {
($spawner:expr, $name:expr, $future:expr, $($field:ident = $val:expr),+ $(,)?) => {
{
use $crate::tracing::Instrument;
let span = $crate::tracing::info_span!("spawned_task", task = $name, $($field = $val),*);
$spawner.spawn($future.instrument(span));
}
};
($spawner:expr, $name:expr, $future:expr) => {
{
use $crate::tracing::Instrument;
let span = $crate::tracing::info_span!("spawned_task", task = $name);
$spawner.spawn($future.instrument(span));
}
};
}