1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
//! Implementation of [`block_on()`]. //! //! This is equivalent to [`futures::executor::block_on()`], but slightly more efficient. //! //! The following blog post explains it in detail: //! - https://stjepang.github.io/2020/01/25/build-your-own-block-on.html use std::cell::RefCell; use std::future::Future; use std::task::{Context, Poll, Waker}; use crossbeam::sync::Parker; use crate::context; /// Blocks on a single future. /// /// This function polls the future in a loop, parking the current thread after each step to wait /// until its waker is woken. /// /// Unlike [`run()`], it does not run executors or poll the reactor! /// /// You can think of it as the easiest and most efficient way of turning an async operation into a /// blocking operation. /// /// # Examples /// /// ``` /// use futures::future; /// use smol::{Async, Timer}; /// use std::thread; /// use std::time::Duration; /// /// // Run executors and the reactor on a separeate thread, forever. /// thread::spawn(|| smol::run(future::pending::<()>())); /// /// smol::block_on(async { /// // Sleep for a second. /// // This timer only works because there's a thread calling `run()`. /// Timer::after(Duration::from_secs(1)).await; /// }) /// ``` /// /// [`run()`]: crate::run() pub fn block_on<T>(future: impl Future<Output = T>) -> T { 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().expect("recursive `block_on()`"); // If enabled, set up tokio before execution begins. context::enter(|| { futures::pin_mut!(future); let cx = &mut Context::from_waker(&waker); loop { match future.as_mut().poll(cx) { Poll::Ready(output) => return output, Poll::Pending => parker.park(), } } }) }) }