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 | |