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/.