use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, mpsc};
use executor_core::LocalExecutor;
use executor_core::async_task::{AsyncTask, Runnable};
#[derive(Clone, Debug)]
pub(crate) struct HeadlessMainThreadExecutor {
runnable_tx: mpsc::Sender<Runnable>,
runnable_rx: Rc<mpsc::Receiver<Runnable>>,
pending: Arc<AtomicUsize>,
}
thread_local! {
static THREAD_EXECUTOR: HeadlessMainThreadExecutor = HeadlessMainThreadExecutor::new();
}
impl HeadlessMainThreadExecutor {
fn new() -> Self {
let (runnable_tx, runnable_rx) = mpsc::channel();
Self {
runnable_tx,
runnable_rx: Rc::new(runnable_rx),
pending: Arc::new(AtomicUsize::new(0)),
}
}
pub(crate) fn thread_shared() -> Self {
THREAD_EXECUTOR.with(Clone::clone)
}
pub(super) fn drain(&self) -> bool {
let mut ran = false;
loop {
let Ok(runnable) = self.runnable_rx.try_recv() else {
return ran;
};
ran = true;
runnable.run();
self.pending.fetch_sub(1, Ordering::SeqCst);
}
}
pub(super) fn has_pending(&self) -> bool {
self.pending.load(Ordering::SeqCst) > 0
}
}
impl LocalExecutor for HeadlessMainThreadExecutor {
type Task<T: 'static> = AsyncTask<T>;
fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
where
Fut: std::future::Future + 'static,
{
let runnable_tx = self.runnable_tx.clone();
let pending = Arc::clone(&self.pending);
let (runnable, task) = executor_core::async_task::spawn_local(fut, move |runnable| {
pending.fetch_add(1, Ordering::SeqCst);
if let Err(unsent) = runnable_tx.send(runnable) {
pending.fetch_sub(1, Ordering::SeqCst);
std::mem::forget(unsent);
}
});
runnable.schedule();
task
}
}
#[derive(Debug)]
pub(super) struct DrainExecutorOnDrop(pub(super) HeadlessMainThreadExecutor);
impl Drop for DrainExecutorOnDrop {
fn drop(&mut self) {
let _ = self.0.drain();
}
}