| //! A simple implementation of `block_on`. |
| |
| use std::cell::RefCell; |
| use std::future::Future; |
| use std::task::{Context, Poll, Waker}; |
| use std::thread; |
| use std::time::Duration; |
| |
| use crossbeam::sync::Parker; |
| use futures::channel::oneshot; |
| |
| /// Runs a future to completion on the current thread. |
| fn block_on<F: Future>(future: F) -> F::Output { |
| // Pin the future on the stack. |
| futures::pin_mut!(future); |
| |
| thread_local! { |
| // Parker and waker associated with the current thread. |
| static CACHE: RefCell<(Parker, Waker)> = { |
| let parker = Parker::new(); |
| let unparker = parker.unparker().clone(); |
| let waker = async_task::waker_fn(move || unparker.unpark()); |
| RefCell::new((parker, waker)) |
| }; |
| } |
| |
| CACHE.with(|cache| { |
| // Panic if `block_on()` is called recursively. |
| let (parker, waker) = &mut *cache.try_borrow_mut().ok().expect("recursive block_on()"); |
| |
| // Create the task context. |
| let cx = &mut Context::from_waker(&waker); |
| |
| // Keep polling the future until completion. |
| loop { |
| match future.as_mut().poll(cx) { |
| Poll::Ready(output) => return output, |
| Poll::Pending => parker.park(), |
| } |
| } |
| }) |
| } |
| |
| fn main() { |
| let (s, r) = oneshot::channel(); |
| |
| // Spawn a thread that will send a message through the channel. |
| thread::spawn(move || { |
| thread::sleep(Duration::from_secs(1)); |
| s.send("Hello, world!").unwrap(); |
| }); |
| |
| // Block until the message is received. |
| let msg = block_on(async { |
| println!("Awaiting..."); |
| r.await.unwrap() |
| }); |
| |
| println!("{}", msg); |
| } |