nolocal_block_on/
lib.rs

1//! [nl-block-on]: `block_on`
2#![doc = include_str!("../README.md")]
3use core::{future::Future, task::Context};
4
5/// Blocks the current thread on a future, without using any thread-local storage in the
6/// implementation (you will need to check the future you are using as well - this crate can't help
7/// if your future uses thread-local storage anyway).
8///
9/// # Example
10/// ```rust
11/// use nolocal_block_on::block_on;
12///
13/// let val = block_on(async {
14///     1 + 2
15/// });
16///
17/// assert_eq!(val, 3);
18/// ```
19/// For more examples (including of specific circumstances where avoiding thread-local-storage is
20/// actually necessary), see the [main crate documentation](crate).
21///
22/// # When to Use This
23/// Unless you are in specific circumstances where you might not be able to use thread-local
24/// storage, you probably want to stick with the normal
25/// [`futures_lite::future::block_on`][fl-block-on] function, as it has a little extra caching in
26/// the common case of using this to just run a top-level future to completion.
27/// 
28/// This function would typically be needed when writing code to run at the "edges" of the Rust
29/// runtime's lifetime - for instance, in [`libc::atexit`][libc-atexit] callbacks, which run after
30/// a certain amount of teardown occurs that typically would prevent the use of thread-locals in
31/// common circumstances (it definitely panics if you're interacting with other threads
32/// during cleanup). 
33///
34/// [fl-block-on]: https://docs.rs/futures-lite/latest/futures_lite/future/fn.block_on.html 
35/// [libc-atexit]: https://docs.rs/libc/latest/libc/fn.atexit.html
36pub fn block_on<T>(future: impl Future<Output = T>) -> T {
37    // The implementation of this function is essentially identical to that of the futures_lite
38    // function
39    //
40    // However, unlike that function, we don't use a cached, thread-local parker and waker
41    use core::task::Waker;
42    let mut future = core::pin::pin!(future);
43    let (parker, unparker) = parking::pair();
44    let waker = Waker::from(unparker);
45    let cx = &mut Context::from_waker(&waker);
46    // Keep polling until the future is ready, parking the thread while it isn't
47    loop {
48        // .as_mut() basically does nothing but it prevents future from being consumed.
49        match future.as_mut().poll(cx) {
50            core::task::Poll::Ready(r) => break r,
51            core::task::Poll::Pending => parker.park(),
52        }
53    }
54}
55
56// This Source Code Form is subject to the terms of the Mozilla Public
57// License, v. 2.0. If a copy of the MPL was not distributed with this
58// file, You can obtain one at http://mozilla.org/MPL/2.0/.