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
7use tokio::net::TcpListener;
8use tokio::sync::oneshot;
9
10fn 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
24mod 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