use crate::PlatformDispatcher; use smol::prelude::*; use std::{ pin::Pin, sync::Arc, task::{Context, Poll}, }; #[derive(Clone)] pub struct Executor { dispatcher: Arc, } #[must_use] pub enum Task { Ready(Option), Spawned(async_task::Task), } impl Task { pub fn ready(val: T) -> Self { Task::Ready(Some(val)) } pub fn detach(self) { match self { Task::Ready(_) => {} Task::Spawned(task) => task.detach(), } } } impl Future for Task { type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { match unsafe { self.get_unchecked_mut() } { Task::Ready(val) => Poll::Ready(val.take().unwrap()), Task::Spawned(task) => task.poll(cx), } } } impl Executor { pub fn new(dispatcher: Arc) -> Self { Self { dispatcher } } /// Enqueues the given closure to be run on any thread. The closure returns /// a future which will be run to completion on any available thread. pub fn spawn(&self, future: impl Future + Send + 'static) -> Task where R: Send + 'static, { let dispatcher = self.dispatcher.clone(); let (runnable, task) = async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable)); runnable.schedule(); Task::Spawned(task) } /// Enqueues the given closure to run on the application's event loop. /// Returns the result asynchronously. pub fn run_on_main(&self, func: F) -> Task where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { if self.dispatcher.is_main_thread() { Task::ready(func()) } else { self.spawn_on_main(move || async move { func() }) } } /// Enqueues the given closure to be run on the application's event loop. The /// closure returns a future which will be run to completion on the main thread. pub fn spawn_on_main(&self, func: impl FnOnce() -> F + Send + 'static) -> Task where F: Future + 'static, R: Send + 'static, { let (runnable, task) = async_task::spawn( { let this = self.clone(); async move { let task = this.spawn_on_main_local(func()); task.await } }, { let dispatcher = self.dispatcher.clone(); move |runnable| dispatcher.dispatch_on_main_thread(runnable) }, ); runnable.schedule(); Task::Spawned(task) } /// Enqueues the given closure to be run on the application's event loop. Must /// be called on the main thread. pub fn spawn_on_main_local(&self, future: impl Future + 'static) -> Task where R: 'static, { assert!( self.dispatcher.is_main_thread(), "must be called on main thread" ); let dispatcher = self.dispatcher.clone(); let (runnable, task) = async_task::spawn_local(future, move |runnable| { dispatcher.dispatch_on_main_thread(runnable) }); runnable.schedule(); Task::Spawned(task) } pub fn is_main_thread(&self) -> bool { self.dispatcher.is_main_thread() } }