use anyhow::{anyhow, Result}; use async_task::Runnable; pub use async_task::Task; use smol::prelude::*; use smol::{channel, Executor}; use std::rc::Rc; use std::sync::Arc; use std::{marker::PhantomData, thread}; use crate::platform; pub enum Foreground { Platform { dispatcher: Arc, _not_send_or_sync: PhantomData>, }, Test(smol::LocalExecutor<'static>), } pub struct Background { executor: Arc>, _stop: channel::Sender<()>, } impl Foreground { pub fn platform(dispatcher: Arc) -> Result { if dispatcher.is_main_thread() { Ok(Self::Platform { dispatcher, _not_send_or_sync: PhantomData, }) } else { Err(anyhow!("must be constructed on main thread")) } } pub fn test() -> Self { Self::Test(smol::LocalExecutor::new()) } pub fn spawn(&self, future: impl Future + 'static) -> Task { match self { Self::Platform { dispatcher, .. } => { let dispatcher = dispatcher.clone(); let schedule = move |runnable: Runnable| dispatcher.run_on_main_thread(runnable); let (runnable, task) = async_task::spawn_local(future, schedule); runnable.schedule(); task } Self::Test(executor) => executor.spawn(future), } } pub async fn run(&self, future: impl Future) -> T { match self { Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"), Self::Test(executor) => executor.run(future).await, } } } impl Background { pub fn new() -> Self { let executor = Arc::new(Executor::new()); let stop = channel::unbounded::<()>(); for i in 0..num_cpus::get() { let executor = executor.clone(); let stop = stop.1.clone(); thread::Builder::new() .name(format!("background-executor-{}", i)) .spawn(move || smol::block_on(executor.run(stop.recv()))) .unwrap(); } Self { executor, _stop: stop.0, } } pub fn spawn(&self, future: F) -> Task where T: 'static + Send, F: Send + Future + 'static, { self.executor.spawn(future) } }