|
7 | 7 | //! Fox more complex use cases, consider creating your own runtime.
|
8 | 8 | //! For command handlers, it's recommended to use a plain `async fn` command.
|
9 | 9 |
|
| 10 | +use futures_lite::future::FutureExt; |
10 | 11 | use once_cell::sync::OnceCell;
|
11 | 12 | use tokio::runtime::Runtime;
|
12 |
| -pub use tokio::sync::{ |
13 |
| - mpsc::{channel, Receiver, Sender}, |
14 |
| - Mutex, RwLock, |
| 13 | +pub use tokio::{ |
| 14 | + runtime::Handle, |
| 15 | + sync::{ |
| 16 | + mpsc::{channel, Receiver, Sender}, |
| 17 | + Mutex, RwLock, |
| 18 | + }, |
| 19 | + task::JoinHandle as TokioJoinHandle, |
15 | 20 | };
|
16 | 21 |
|
17 |
| -use std::future::Future; |
| 22 | +use std::{ |
| 23 | + fmt, |
| 24 | + future::Future, |
| 25 | + pin::Pin, |
| 26 | + task::{Context, Poll}, |
| 27 | +}; |
18 | 28 |
|
19 | 29 | static RUNTIME: OnceCell<Runtime> = OnceCell::new();
|
20 | 30 |
|
| 31 | +/// An owned permission to join on a task (await its termination). |
| 32 | +#[derive(Debug)] |
| 33 | +pub struct JoinHandle<T>(TokioJoinHandle<T>); |
| 34 | + |
| 35 | +impl<T> Future for JoinHandle<T> { |
| 36 | + type Output = crate::Result<T>; |
| 37 | + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 38 | + self |
| 39 | + .0 |
| 40 | + .poll(cx) |
| 41 | + .map_err(|e| crate::Error::JoinError(Box::new(e))) |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +/// Runtime handle definition. |
| 46 | +pub trait RuntimeHandle: fmt::Debug + Clone + Sync + Sync { |
| 47 | + /// Spawn a future onto the runtime. |
| 48 | + fn spawn<F: Future>(&self, task: F) -> JoinHandle<F::Output> |
| 49 | + where |
| 50 | + F: Future + Send + 'static, |
| 51 | + F::Output: Send + 'static; |
| 52 | + |
| 53 | + /// Run a future to completion on runtime. |
| 54 | + fn block_on<F: Future>(&self, task: F) -> F::Output; |
| 55 | +} |
| 56 | + |
| 57 | +impl RuntimeHandle for Handle { |
| 58 | + fn spawn<F: Future>(&self, task: F) -> JoinHandle<F::Output> |
| 59 | + where |
| 60 | + F: Future + Send + 'static, |
| 61 | + F::Output: Send + 'static, |
| 62 | + { |
| 63 | + JoinHandle(self.spawn(task)) |
| 64 | + } |
| 65 | + |
| 66 | + fn block_on<F: Future>(&self, task: F) -> F::Output { |
| 67 | + self.block_on(task) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +/// Returns a handle to the async runtime. |
| 72 | +pub fn handle() -> impl RuntimeHandle { |
| 73 | + let runtime = RUNTIME.get_or_init(|| Runtime::new().unwrap()); |
| 74 | + runtime.handle().clone() |
| 75 | +} |
| 76 | + |
21 | 77 | /// Run a future to completion on runtime.
|
22 | 78 | pub fn block_on<F: Future>(task: F) -> F::Output {
|
23 | 79 | let runtime = RUNTIME.get_or_init(|| Runtime::new().unwrap());
|
24 | 80 | runtime.block_on(task)
|
25 | 81 | }
|
26 | 82 |
|
27 | 83 | /// Spawn a future onto the runtime.
|
28 |
| -pub fn spawn<F>(task: F) |
| 84 | +pub fn spawn<F>(task: F) -> JoinHandle<F::Output> |
29 | 85 | where
|
30 | 86 | F: Future + Send + 'static,
|
31 | 87 | F::Output: Send + 'static,
|
32 | 88 | {
|
33 | 89 | let runtime = RUNTIME.get_or_init(|| Runtime::new().unwrap());
|
34 |
| - runtime.spawn(task); |
| 90 | + JoinHandle(runtime.spawn(task)) |
| 91 | +} |
| 92 | + |
| 93 | +#[cfg(test)] |
| 94 | +mod tests { |
| 95 | + use super::*; |
| 96 | + #[tokio::test] |
| 97 | + async fn handle_spawn() { |
| 98 | + let handle = handle(); |
| 99 | + let join = handle.spawn(async { 5 }); |
| 100 | + assert_eq!(join.await.unwrap(), 5); |
| 101 | + } |
| 102 | + |
| 103 | + #[test] |
| 104 | + fn handle_block_on() { |
| 105 | + let handle = handle(); |
| 106 | + assert_eq!(handle.block_on(async { 0 }), 0); |
| 107 | + } |
35 | 108 | }
|
0 commit comments