zbus/abstractions/
executor.rs1#[cfg(feature = "async-io")]
2use async_executor::Executor as AsyncExecutor;
3#[cfg(feature = "async-io")]
4use async_task::Task as AsyncTask;
5#[cfg(feature = "tokio")]
6use std::io::Error;
7#[cfg(not(feature = "async-io"))]
8use std::marker::PhantomData;
9#[cfg(feature = "async-io")]
10use std::sync::Arc;
11use std::{
12 future::Future,
13 io::Result,
14 pin::Pin,
15 task::{Context, Poll},
16};
17#[cfg(feature = "tokio")]
18use tokio::task::JoinHandle;
19
20#[derive(Debug, Clone)]
27pub struct Executor<'a> {
28 #[cfg(feature = "async-io")]
29 async_io: Option<Arc<AsyncExecutor<'a>>>,
30 #[cfg(not(feature = "async-io"))]
31 phantom: PhantomData<&'a ()>,
32}
33
34impl Executor<'_> {
35 #[doc(hidden)]
37 pub fn spawn<T: Send + 'static>(
38 &self,
39 future: impl Future<Output = T> + Send + 'static,
40 #[allow(unused)] name: &str,
41 ) -> Task<T> {
42 #[cfg(feature = "async-io")]
43 if let Some(executor) = &self.async_io {
44 return Task::from_async_io(executor.spawn(future));
45 }
46
47 #[cfg(feature = "tokio")]
48 return Task::from_tokio(tokio_spawn(future, name));
49
50 #[cfg(all(feature = "async-io", not(feature = "tokio")))]
51 unreachable!("async-io executor is always `Some` when tokio is disabled")
52 }
53
54 pub fn is_empty(&self) -> bool {
58 #[cfg(feature = "async-io")]
59 if let Some(executor) = &self.async_io {
60 return executor.is_empty();
61 }
62
63 true
64 }
65
66 pub async fn tick(&self) {
70 #[cfg(feature = "async-io")]
71 if let Some(executor) = &self.async_io {
72 executor.tick().await;
73 #[cfg(feature = "tokio")]
75 return;
76 }
77
78 #[cfg(feature = "tokio")]
79 std::future::pending::<()>().await;
80 }
81
82 pub(crate) fn new() -> Self {
84 Self {
85 #[cfg(feature = "async-io")]
86 async_io: (!super::use_tokio()).then(|| Arc::new(AsyncExecutor::new())),
87 #[cfg(not(feature = "async-io"))]
88 phantom: PhantomData,
89 }
90 }
91
92 #[cfg(feature = "async-io")]
94 pub(crate) fn needs_internal_driver(&self) -> bool {
95 self.async_io.is_some()
96 }
97
98 pub(crate) async fn run<T>(&self, future: impl Future<Output = T>) -> T {
102 #[cfg(feature = "async-io")]
103 if let Some(executor) = &self.async_io {
104 return executor.run(future).await;
105 }
106
107 future.await
108 }
109}
110
111#[cfg(feature = "tokio")]
112fn tokio_spawn<T: Send + 'static>(
113 future: impl Future<Output = T> + Send + 'static,
114 #[allow(unused)] name: &str,
115) -> JoinHandle<T> {
116 #[cfg(tokio_unstable)]
117 {
118 tokio::task::Builder::new()
119 .name(name)
120 .spawn(future)
121 .unwrap()
123 }
124 #[cfg(not(tokio_unstable))]
125 {
126 tokio::task::spawn(future)
127 }
128}
129
130#[doc(hidden)]
138#[derive(Debug)]
139pub struct Task<T> {
140 #[cfg(feature = "async-io")]
141 async_io: Option<AsyncTask<T>>,
142 #[cfg(feature = "tokio")]
143 tokio: Option<JoinHandle<T>>,
144}
145
146impl<T> Task<T> {
147 #[cfg(feature = "async-io")]
148 fn from_async_io(task: AsyncTask<T>) -> Self {
149 Self {
150 async_io: Some(task),
151 #[cfg(feature = "tokio")]
152 tokio: None,
153 }
154 }
155
156 #[cfg(feature = "tokio")]
157 fn from_tokio(handle: JoinHandle<T>) -> Self {
158 Self {
159 #[cfg(feature = "async-io")]
160 async_io: None,
161 tokio: Some(handle),
162 }
163 }
164
165 #[allow(unused_mut)]
167 pub fn detach(mut self) {
168 #[cfg(feature = "async-io")]
169 if let Some(task) = self.async_io.take() {
170 task.detach();
171 }
172
173 #[cfg(feature = "tokio")]
174 if let Some(handle) = self.tokio.take() {
175 drop(handle);
177 }
178 }
179}
180
181impl<T> Task<T>
182where
183 T: Send + 'static,
184{
185 #[allow(unused)]
190 pub(crate) fn spawn_blocking<F>(f: F, #[allow(unused)] name: &str) -> Self
191 where
192 F: FnOnce() -> T + Send + 'static,
193 {
194 super::select_runtime! {
195 tokio: Self::from_tokio(tokio_spawn_blocking(f, name)),
196 async_io: Self::from_async_io(blocking::unblock(f)),
197 }
198 }
199}
200
201#[cfg(feature = "tokio")]
202fn tokio_spawn_blocking<F, T>(f: F, #[allow(unused)] name: &str) -> JoinHandle<T>
203where
204 F: FnOnce() -> T + Send + 'static,
205 T: Send + 'static,
206{
207 #[cfg(tokio_unstable)]
208 {
209 tokio::task::Builder::new()
210 .name(name)
211 .spawn_blocking(f)
212 .unwrap()
214 }
215 #[cfg(not(tokio_unstable))]
216 {
217 tokio::task::spawn_blocking(f)
218 }
219}
220
221impl<T> Drop for Task<T> {
222 fn drop(&mut self) {
223 #[cfg(feature = "tokio")]
224 if let Some(join_handle) = self.tokio.take() {
225 join_handle.abort();
226 }
227 }
228}
229
230impl<T> Future for Task<T> {
231 type Output = Result<T>;
232
233 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
234 let this = self.get_mut();
235
236 #[cfg(feature = "async-io")]
237 if let Some(task) = &mut this.async_io {
238 return Pin::new(task).poll(cx).map(Ok);
239 }
240
241 #[cfg(feature = "tokio")]
242 if let Some(handle) = &mut this.tokio {
243 return Pin::new(handle).poll(cx).map(|r| match r {
244 Ok(v) => Ok(v),
245 Err(e) => {
246 if e.is_cancelled() {
247 Err(Error::other("tokio::task cancelled"))
248 } else {
249 panic!("tokio::task::JoinHandle error: {e}")
250 }
251 }
252 });
253 }
254
255 unreachable!("Task always has exactly one backend")
256 }
257}