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