| 1 | // This example shows how to use the tokio runtime with any other executor |
| 2 | // |
| 3 | // The main components are a spawn fn that will wrap futures in a special future |
| 4 | // that will always enter the tokio context on poll. This only spawns one extra thread |
| 5 | // to manage and run the tokio drivers in the background. |
| 6 | |
| 7 | use tokio::net::TcpListener; |
| 8 | use tokio::sync::oneshot; |
| 9 | |
| 10 | fn main() { |
| 11 | let (tx, rx) = oneshot::channel(); |
| 12 | |
| 13 | my_custom_runtime::spawn(async move { |
| 14 | let listener = TcpListener::bind("0.0.0.0:0" ).await.unwrap(); |
| 15 | |
| 16 | println!("addr: {:?}" , listener.local_addr()); |
| 17 | |
| 18 | tx.send(()).unwrap(); |
| 19 | }); |
| 20 | |
| 21 | futures::executor::block_on(rx).unwrap(); |
| 22 | } |
| 23 | |
| 24 | mod my_custom_runtime { |
| 25 | use once_cell::sync::Lazy; |
| 26 | use std::future::Future; |
| 27 | use tokio_util::context::TokioContext; |
| 28 | |
| 29 | pub fn spawn(f: impl Future<Output = ()> + Send + 'static) { |
| 30 | EXECUTOR.spawn(f); |
| 31 | } |
| 32 | |
| 33 | struct ThreadPool { |
| 34 | inner: futures::executor::ThreadPool, |
| 35 | rt: tokio::runtime::Runtime, |
| 36 | } |
| 37 | |
| 38 | static EXECUTOR: Lazy<ThreadPool> = Lazy::new(|| { |
| 39 | // Spawn tokio runtime on a single background thread |
| 40 | // enabling IO and timers. |
| 41 | let rt = tokio::runtime::Builder::new_multi_thread() |
| 42 | .enable_all() |
| 43 | .build() |
| 44 | .unwrap(); |
| 45 | let inner = futures::executor::ThreadPool::builder().create().unwrap(); |
| 46 | |
| 47 | ThreadPool { inner, rt } |
| 48 | }); |
| 49 | |
| 50 | impl ThreadPool { |
| 51 | fn spawn(&self, f: impl Future<Output = ()> + Send + 'static) { |
| 52 | let handle = self.rt.handle().clone(); |
| 53 | self.inner.spawn_ok(TokioContext::new(f, handle)); |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |