Skip to main content

async_rt/rt/
tokio.rs

1use crate::{
2    Executor, ExecutorBlockOn, ExecutorBlocking, ExecutorTimeout, InnerJoinHandle, JoinHandle,
3};
4use std::future::Future;
5use std::sync::Arc;
6use tokio::runtime::{Handle, Runtime};
7
8/// Tokio executor
9#[derive(Default, Clone, Copy, Debug, PartialOrd, PartialEq, Eq)]
10pub struct TokioExecutor;
11
12impl Executor for TokioExecutor {
13    fn runtime_type(&self) -> Option<&'static str> {
14        Some("tokio")
15    }
16
17    fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
18    where
19        F: Future + Send + 'static,
20        F::Output: Send + 'static,
21    {
22        let handle = tokio::task::spawn(future);
23        let inner = InnerJoinHandle::tokio(handle);
24        JoinHandle { inner }
25    }
26}
27
28impl ExecutorBlocking for TokioExecutor {
29    fn spawn_blocking<F, R>(&self, f: F) -> JoinHandle<R>
30    where
31        F: FnOnce() -> R + Send + 'static,
32        R: Send + 'static,
33    {
34        let handle = tokio::task::spawn_blocking(f);
35        let inner = InnerJoinHandle::tokio(handle);
36        JoinHandle { inner }
37    }
38}
39
40impl ExecutorTimeout for TokioExecutor {}
41
42impl ExecutorBlockOn for TokioExecutor {
43    /// Blocks the current thread until the provided future has completed.
44    /// Note that this should not run inside a tokio runtime context.
45    fn block_on<F: Future>(&self, f: F) -> F::Output {
46        let handle = Handle::current();
47        handle.block_on(f)
48    }
49}
50
51/// Tokio executor with an [`Runtime`] or [`Handle`]
52#[derive(Clone, Debug)]
53pub struct TokioRuntimeExecutor {
54    handle: Handle,
55    _runtime: Option<Arc<Runtime>>,
56}
57
58impl TokioRuntimeExecutor {
59    /// Creates a tokio runtime with the current thread scheduler selected.
60    pub fn with_single_thread() -> std::io::Result<Self> {
61        let runtime = tokio::runtime::Builder::new_current_thread()
62            .enable_all()
63            .build()?;
64        Ok(Self::with_runtime(runtime))
65    }
66
67    /// Creates a tokio runtime with multi-thread scheduler selected.
68    pub fn with_multi_thread() -> std::io::Result<Self> {
69        let runtime = tokio::runtime::Builder::new_multi_thread()
70            .enable_all()
71            .build()?;
72        Ok(Self::with_runtime(runtime))
73    }
74
75    /// Create an executor with the supplied [`Runtime`].
76    ///
77    /// Note that the runtime remains alive until the final clone of this executor is
78    /// dropped.
79    pub fn with_runtime(runtime: Runtime) -> Self {
80        let runtime = Arc::new(runtime);
81        let handle = runtime.handle().clone();
82        Self {
83            _runtime: Some(runtime),
84            handle,
85        }
86    }
87
88    /// Create an executor with the supplied [`Runtime`].
89    ///
90    /// Note that this executor does not own or keep the associated runtime alive. The
91    /// runtime must remain running for spawned tasks to execute.
92    pub fn with_handle(handle: Handle) -> Self {
93        Self {
94            handle,
95            _runtime: None,
96        }
97    }
98
99    /// Create an executor from the existing Runtime
100    ///
101    /// Note that this returns an error when called outside a Tokio runtime context. The
102    /// resulting executor does not prevent that runtime from shutting down.
103    pub fn from_current_handle() -> std::io::Result<Self> {
104        let handle = Handle::try_current().map_err(std::io::Error::other)?;
105        Ok(Self::with_handle(handle))
106    }
107}
108
109impl Executor for TokioRuntimeExecutor {
110    fn runtime_type(&self) -> Option<&'static str> {
111        Some("tokio")
112    }
113
114    fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
115    where
116        F: Future + Send + 'static,
117        F::Output: Send + 'static,
118    {
119        let handle = self.handle.spawn(future);
120        let inner = InnerJoinHandle::tokio(handle);
121        JoinHandle { inner }
122    }
123}
124
125impl ExecutorBlocking for TokioRuntimeExecutor {
126    fn spawn_blocking<F, R>(&self, f: F) -> JoinHandle<R>
127    where
128        F: FnOnce() -> R + Send + 'static,
129        R: Send + 'static,
130    {
131        let handle = self.handle.spawn_blocking(f);
132        let inner = InnerJoinHandle::tokio(handle);
133        JoinHandle { inner }
134    }
135}
136
137impl ExecutorTimeout for TokioRuntimeExecutor {}
138
139impl ExecutorBlockOn for TokioRuntimeExecutor {
140    /// Blocks the current thread until the provided future has completed.
141    ///
142    /// Note that when this executor owns a [`Runtime`], that runtime is driven directly.
143    /// When created from a [`Handle`], this delegates to [`Handle::block_on`] and
144    /// inherits its runtime-specific limitations. See [`Runtime::block_on`] and
145    /// [`Handle::block_on`] for details.
146    fn block_on<F: Future>(&self, f: F) -> F::Output {
147        match self._runtime.as_ref() {
148            None => self.handle.block_on(f),
149            Some(runtime) => runtime.block_on(f),
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::{TokioExecutor, TokioRuntimeExecutor};
157    use crate::error::JoinError;
158    use crate::{Executor, ExecutorBlockOn, ExecutorBlocking, ExecutorTimeout, TimeoutError};
159    use futures::channel::mpsc::{Receiver, UnboundedReceiver};
160    use futures_timer::Delay;
161
162    #[tokio::test]
163    async fn explicit_abort_is_reported_as_aborted() {
164        let handle = TokioExecutor.spawn(futures::future::pending::<()>());
165
166        handle.abort();
167
168        assert!(matches!(handle.await, Err(JoinError::Aborted)));
169    }
170
171    #[cfg(panic = "unwind")]
172    #[tokio::test]
173    async fn task_panic_is_reported_as_panicked() {
174        async fn panic_task() -> usize {
175            panic!("expected task panic");
176        }
177
178        let handle = TokioExecutor.spawn(panic_task());
179
180        assert!(matches!(handle.await, Err(JoinError::Panicked)));
181    }
182
183    #[test]
184    fn runtime_shutdown_is_reported_as_cancelled() {
185        let executor = TokioRuntimeExecutor::with_multi_thread().unwrap();
186        let handle = executor.spawn(futures::future::pending::<()>());
187
188        drop(executor);
189
190        assert!(matches!(
191            futures::executor::block_on(handle),
192            Err(JoinError::Cancelled)
193        ));
194    }
195
196    #[test]
197    fn block_on_drives_owned_current_thread_runtime() {
198        let executor = TokioRuntimeExecutor::with_single_thread().unwrap();
199        let handle = executor.spawn(async { 42 });
200
201        assert_eq!(executor.block_on(handle).unwrap(), 42);
202    }
203
204    #[tokio::test]
205    async fn default_abortable_task() {
206        let executor = TokioExecutor;
207
208        async fn task(tx: futures::channel::oneshot::Sender<()>) {
209            futures_timer::Delay::new(std::time::Duration::from_secs(5)).await;
210            let _ = tx.send(());
211            unreachable!();
212        }
213
214        let (tx, rx) = futures::channel::oneshot::channel::<()>();
215
216        let handle = executor.spawn_abortable(task(tx));
217
218        drop(handle);
219        let result = rx.await;
220        assert!(result.is_err());
221    }
222
223    #[tokio::test]
224    async fn task_coroutine() {
225        let executor = TokioExecutor;
226
227        enum Message {
228            Send(String, futures::channel::oneshot::Sender<String>),
229        }
230
231        let mut task = executor.spawn_coroutine(|msg: Message| async move {
232            match msg {
233                Message::Send(msg, sender) => {
234                    sender.send(msg).unwrap();
235                }
236            }
237        });
238
239        let (tx, rx) = futures::channel::oneshot::channel::<String>();
240        let msg = Message::Send("Hello".into(), tx);
241
242        task.send(msg).await.unwrap();
243        let resp = rx.await.unwrap();
244        assert_eq!(resp, "Hello");
245    }
246
247    #[tokio::test]
248    async fn task_coroutine_with_context() {
249        let executor = TokioExecutor;
250
251        type Resp = futures::channel::oneshot::Sender<usize>;
252
253        let mut task =
254            executor.spawn_coroutine_with_context(0usize, |counter: &mut usize, resp: Resp| {
255                *counter += 1;
256                let n = *counter;
257                async move {
258                    resp.send(n).unwrap();
259                }
260            });
261
262        let (tx1, rx1) = futures::channel::oneshot::channel::<usize>();
263        let (tx2, rx2) = futures::channel::oneshot::channel::<usize>();
264        task.send(tx1).await.unwrap();
265        task.send(tx2).await.unwrap();
266        assert_eq!(rx1.await.unwrap(), 1);
267        assert_eq!(rx2.await.unwrap(), 2);
268    }
269
270    #[tokio::test]
271    async fn task_coroutine_with_receiver() {
272        use futures::stream::StreamExt;
273        let executor = TokioExecutor;
274
275        enum Message {
276            Send(String, futures::channel::oneshot::Sender<String>),
277        }
278
279        let mut task =
280            executor.spawn_coroutine_with_receiver(|mut rx: Receiver<Message>| async move {
281                while let Some(msg) = rx.next().await {
282                    match msg {
283                        Message::Send(msg, sender) => {
284                            sender.send(msg).unwrap();
285                        }
286                    }
287                }
288            });
289
290        let (tx, rx) = futures::channel::oneshot::channel::<String>();
291        let msg = Message::Send("Hello".into(), tx);
292
293        task.send(msg).await.unwrap();
294        let resp = rx.await.unwrap();
295        assert_eq!(resp, "Hello");
296    }
297
298    #[tokio::test]
299    async fn task_coroutine_with_receiver_and_context() {
300        use futures::stream::StreamExt;
301        let executor = TokioExecutor;
302
303        #[derive(Default)]
304        struct State {
305            message: String,
306        }
307
308        enum Message {
309            Set(String),
310            Get(futures::channel::oneshot::Sender<String>),
311        }
312
313        let mut task = executor.spawn_coroutine_with_receiver_and_context(
314            State::default(),
315            |mut state, mut rx: Receiver<Message>| async move {
316                while let Some(msg) = rx.next().await {
317                    match msg {
318                        Message::Set(msg) => {
319                            state.message = msg;
320                        }
321                        Message::Get(resp) => {
322                            resp.send(state.message.clone()).unwrap();
323                        }
324                    }
325                }
326            },
327        );
328
329        let msg = Message::Set("Hello".into());
330
331        task.send(msg).await.unwrap();
332        let (tx, rx) = futures::channel::oneshot::channel::<String>();
333        let msg = Message::Get(tx);
334        task.send(msg).await.unwrap();
335        let resp = rx.await.unwrap();
336        assert_eq!(resp, "Hello");
337    }
338
339    #[tokio::test]
340    async fn task_unbounded_coroutine() {
341        let executor = TokioExecutor;
342
343        enum Message {
344            Send(String, futures::channel::oneshot::Sender<String>),
345        }
346
347        let mut task = executor.spawn_unbounded_coroutine(|msg: Message| async move {
348            match msg {
349                Message::Send(msg, sender) => {
350                    sender.send(msg).unwrap();
351                }
352            }
353        });
354
355        let (tx, rx) = futures::channel::oneshot::channel::<String>();
356        let msg = Message::Send("Hello".into(), tx);
357
358        task.send(msg).unwrap();
359        let resp = rx.await.unwrap();
360        assert_eq!(resp, "Hello");
361    }
362
363    #[tokio::test]
364    async fn task_unbounded_coroutine_with_context() {
365        let executor = TokioExecutor;
366
367        type Resp = futures::channel::oneshot::Sender<usize>;
368
369        let mut task = executor.spawn_unbounded_coroutine_with_context(
370            0usize,
371            |counter: &mut usize, resp: Resp| {
372                *counter += 1;
373                let n = *counter;
374                async move {
375                    resp.send(n).unwrap();
376                }
377            },
378        );
379
380        let (tx1, rx1) = futures::channel::oneshot::channel::<usize>();
381        let (tx2, rx2) = futures::channel::oneshot::channel::<usize>();
382        task.send(tx1).unwrap();
383        task.send(tx2).unwrap();
384        assert_eq!(rx1.await.unwrap(), 1);
385        assert_eq!(rx2.await.unwrap(), 2);
386    }
387
388    #[tokio::test]
389    async fn task_unbounded_coroutine_with_receiver() {
390        use futures::stream::StreamExt;
391        let executor = TokioExecutor;
392
393        enum Message {
394            Send(String, futures::channel::oneshot::Sender<String>),
395        }
396
397        let mut task = executor.spawn_unbounded_coroutine_with_receiver(
398            |mut rx: UnboundedReceiver<Message>| async move {
399                while let Some(msg) = rx.next().await {
400                    match msg {
401                        Message::Send(msg, sender) => {
402                            sender.send(msg).unwrap();
403                        }
404                    }
405                }
406            },
407        );
408
409        let (tx, rx) = futures::channel::oneshot::channel::<String>();
410        let msg = Message::Send("Hello".into(), tx);
411
412        task.send(msg).unwrap();
413        let resp = rx.await.unwrap();
414        assert_eq!(resp, "Hello");
415    }
416
417    #[tokio::test]
418    async fn task_unbounded_coroutine_with_receiver_and_context() {
419        use futures::stream::StreamExt;
420        let executor = TokioExecutor;
421
422        #[derive(Default)]
423        struct State {
424            message: String,
425        }
426
427        enum Message {
428            Set(String),
429            Get(futures::channel::oneshot::Sender<String>),
430        }
431
432        let mut task = executor.spawn_unbounded_coroutine_with_receiver_and_context(
433            State::default(),
434            |mut state, mut rx: UnboundedReceiver<Message>| async move {
435                while let Some(msg) = rx.next().await {
436                    match msg {
437                        Message::Set(msg) => {
438                            state.message = msg;
439                        }
440                        Message::Get(resp) => {
441                            resp.send(state.message.clone()).unwrap();
442                        }
443                    }
444                }
445            },
446        );
447
448        let msg = Message::Set("Hello".into());
449
450        task.send(msg).unwrap();
451        let (tx, rx) = futures::channel::oneshot::channel::<String>();
452        let msg = Message::Get(tx);
453        task.send(msg).unwrap();
454        let resp = rx.await.unwrap();
455        assert_eq!(resp, "Hello");
456    }
457
458    #[tokio::test]
459    async fn timeout_task() {
460        let executor = TokioExecutor;
461
462        let task = executor.spawn_timeout(
463            std::time::Duration::from_millis(10),
464            futures::future::pending::<()>(),
465        );
466        let resp = task.await.unwrap();
467        assert!(matches!(resp.unwrap_err(), TimeoutError));
468    }
469
470    #[tokio::test]
471    async fn complete_before_timeout_task() {
472        let executor = TokioExecutor;
473
474        let task = executor.spawn_timeout(
475            std::time::Duration::from_millis(10),
476            futures::future::ready("Hello"),
477        );
478        let resp = task.await.unwrap();
479        assert!(resp.is_ok());
480        let result = resp.unwrap();
481        assert_eq!(result, "Hello");
482    }
483
484    #[tokio::test]
485    async fn delay_task() {
486        let executor = TokioExecutor;
487        let duration = std::time::Duration::from_millis(20);
488        let started = std::time::Instant::now();
489
490        let task = executor.spawn_delay(duration, async { "Hello" });
491        let result = task.await.unwrap();
492
493        assert!(started.elapsed() >= duration);
494        assert_eq!(result, "Hello");
495    }
496
497    #[tokio::test]
498    async fn abortable_delay_task() {
499        let executor = TokioExecutor;
500        let (tx, rx) = futures::channel::oneshot::channel();
501        let task = executor.spawn_abortable_delay(std::time::Duration::from_secs(5), async move {
502            let _ = tx.send(());
503        });
504
505        drop(task);
506
507        assert!(rx.await.is_err());
508    }
509
510    #[tokio::test]
511    async fn race_before_timeout_task() {
512        let executor = TokioExecutor;
513
514        let task = executor.spawn_timeout(std::time::Duration::from_millis(500), async {
515            Delay::new(std::time::Duration::from_millis(10)).await;
516            "Hello"
517        });
518        let resp = task.await.unwrap();
519        assert!(resp.is_ok());
520        let result = resp.unwrap();
521        assert_eq!(result, "Hello");
522    }
523
524    #[tokio::test]
525    async fn abortable_timeout_task() {
526        let executor = TokioExecutor;
527
528        let task = executor.spawn_abortable_timeout(
529            std::time::Duration::from_millis(10),
530            futures::future::pending::<()>(),
531        );
532        let resp = task.await.unwrap();
533        assert!(matches!(resp.unwrap_err(), TimeoutError));
534    }
535
536    #[tokio::test]
537    async fn blocking_task() {
538        let executor = TokioExecutor;
539
540        let task = executor.spawn_blocking(|| {
541            std::thread::sleep(std::time::Duration::from_millis(100));
542            "Hello"
543        });
544        let resp = task.await.unwrap();
545        assert_eq!(resp, "Hello");
546    }
547}