1use crate::PlatformDispatcher;
2use smol::prelude::*;
3use std::{
4 pin::Pin,
5 sync::Arc,
6 task::{Context, Poll},
7};
8
9#[derive(Clone)]
10pub struct Executor {
11 dispatcher: Arc<dyn PlatformDispatcher>,
12}
13
14pub enum Task<T> {
15 Ready(Option<T>),
16 Spawned(async_task::Task<T>),
17}
18
19impl<T> Task<T> {
20 pub fn ready(val: T) -> Self {
21 Task::Ready(Some(val))
22 }
23
24 pub fn detach(self) {
25 match self {
26 Task::Ready(_) => {}
27 Task::Spawned(task) => task.detach(),
28 }
29 }
30}
31
32impl<T> Future for Task<T> {
33 type Output = T;
34
35 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
36 match unsafe { self.get_unchecked_mut() } {
37 Task::Ready(val) => Poll::Ready(val.take().unwrap()),
38 Task::Spawned(task) => task.poll(cx),
39 }
40 }
41}
42
43impl Executor {
44 pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
45 Self { dispatcher }
46 }
47
48 /// Enqueues the given closure to be run on any thread. The closure returns
49 /// a future which will be run to completion on any available thread.
50 pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
51 where
52 R: Send + 'static,
53 {
54 let dispatcher = self.dispatcher.clone();
55 let (runnable, task) =
56 async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
57 runnable.schedule();
58 Task::Spawned(task)
59 }
60
61 /// Enqueues the given closure to run on the application's event loop.
62 /// Returns the result asynchronously.
63 pub fn run_on_main<F, R>(&self, func: F) -> Task<R>
64 where
65 F: FnOnce() -> R + Send + 'static,
66 R: Send + 'static,
67 {
68 if self.dispatcher.is_main_thread() {
69 Task::ready(func())
70 } else {
71 self.spawn_on_main(move || async move { func() })
72 }
73 }
74
75 /// Enqueues the given closure to be run on the application's event loop. The
76 /// closure returns a future which will be run to completion on the main thread.
77 pub fn spawn_on_main<F, R>(&self, func: impl FnOnce() -> F + Send + 'static) -> Task<R>
78 where
79 F: Future<Output = R> + 'static,
80 R: Send + 'static,
81 {
82 let dispatcher = self.dispatcher.clone();
83 let (runnable, task) =
84 async_task::spawn_local(async move { func().await }, move |runnable| {
85 dispatcher.dispatch_on_main_thread(runnable)
86 });
87 runnable.schedule();
88 Task::Spawned(task)
89 }
90
91 /// Enqueues the given closure to be run on the application's event loop. Must
92 /// be called on the main thread.
93 pub fn spawn_on_main_local<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
94 where
95 R: 'static,
96 {
97 assert!(
98 self.dispatcher.is_main_thread(),
99 "must be called on main thread"
100 );
101
102 let dispatcher = self.dispatcher.clone();
103 let (runnable, task) = async_task::spawn_local(future, move |runnable| {
104 dispatcher.dispatch_on_main_thread(runnable)
105 });
106 runnable.schedule();
107 Task::Spawned(task)
108 }
109
110 pub fn is_main_thread(&self) -> bool {
111 self.dispatcher.is_main_thread()
112 }
113}