blob: e4124b2ccb627b6ddd81150228090058cc8d1471 [file] [log] [blame]
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +01001//! A simple implementation of `block_on`.
2
3use std::cell::RefCell;
4use std::future::Future;
5use std::task::{Context, Poll, Waker};
6use std::thread;
7use std::time::Duration;
8
9use crossbeam::sync::Parker;
10use futures::channel::oneshot;
11
12/// Runs a future to completion on the current thread.
13fn block_on<F: Future>(future: F) -> F::Output {
14 // Pin the future on the stack.
15 pin_utils::pin_mut!(future);
16
17 thread_local! {
18 // Parker and waker associated with the current thread.
19 static CACHE: RefCell<(Parker, Waker)> = {
20 let parker = Parker::new();
21 let unparker = parker.unparker().clone();
22 let waker = async_task::waker_fn(move || unparker.unpark());
23 RefCell::new((parker, waker))
24 };
25 }
26
27 CACHE.with(|cache| {
28 // Panic if `block_on()` is called recursively.
29 let (parker, waker) = &mut *cache.try_borrow_mut().ok().expect("recursive block_on()");
30
31 // Create the task context.
32 let cx = &mut Context::from_waker(&waker);
33
34 // Keep polling the future until completion.
35 loop {
36 match future.as_mut().poll(cx) {
37 Poll::Ready(output) => return output,
38 Poll::Pending => parker.park(),
39 }
40 }
41 })
42}
43
44fn main() {
45 let (s, r) = oneshot::channel();
46
47 // Spawn a thread that will send a message through the channel.
48 thread::spawn(move || {
49 thread::sleep(Duration::from_secs(1));
50 s.send("Hello, world!").unwrap();
51 });
52
53 // Block until the message is received.
54 let msg = block_on(async {
55 println!("Awaiting...");
56 r.await.unwrap()
57 });
58
59 println!("{}", msg);
60}