use core::future::Future;
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use executor_core::LocalExecutor;
use executor_core::async_task::{self, AsyncTask, Runnable};
struct ParkedWork {
sender: Sender<Runnable>,
receiver: Receiver<Runnable>,
pending: Arc<AtomicUsize>,
}
thread_local! {
static PARKED_WORK: ParkedWork = {
let (sender, receiver) = mpsc::channel();
ParkedWork {
sender,
receiver,
pending: Arc::new(AtomicUsize::new(0)),
}
};
}
#[derive(Clone, Copy, Debug, Default)]
pub struct TestLocalExecutor;
impl LocalExecutor for TestLocalExecutor {
type Task<T: 'static> = AsyncTask<T>;
fn spawn_local<Fut>(&self, fut: Fut) -> Self::Task<Fut::Output>
where
Fut: Future + 'static,
{
let (sender, pending) =
PARKED_WORK.with(|parked| (parked.sender.clone(), Arc::clone(&parked.pending)));
let (runnable, task) = async_task::spawn_local(fut, move |runnable: Runnable| {
pending.fetch_add(1, Ordering::SeqCst);
if let Err(unsent) = sender.send(runnable) {
pending.fetch_sub(1, Ordering::SeqCst);
std::mem::forget(unsent.0);
}
});
runnable.schedule();
task
}
}
pub fn install_test_executor() {
let _ = executor_core::try_init_local_executor(waterui::task::monitored_local_executor(
TestLocalExecutor,
));
}
#[must_use]
pub fn drain_parked_local_work() -> usize {
PARKED_WORK.with(|parked| {
let budget = parked.pending.load(Ordering::SeqCst);
let mut ran = 0;
while ran < budget {
match parked.receiver.try_recv() {
Ok(runnable) => {
runnable.run();
parked.pending.fetch_sub(1, Ordering::SeqCst);
ran += 1;
}
Err(_) => break,
}
}
ran
})
}