Skip to main content

block_on_place/
lib.rs

1//!  # `block-on-place`
2//!
3//! For when you want to call [`block_on()`][1] on a [`tokio::runtime::Handle`] but
4//! cannot 100% guarantee that you're not inside of a runtime.
5//!
6//! Internally, this uses [`task::block_in_place()`][2] to call
7//! [`Handle::block_on()`][1].
8//!
9//! ## Features
10//!
11//! You can enable the `tracing` feature to get a warning when calling
12//! [`block_on_place()`][3] from within a runtime context.
13//!
14//! [1]: Handle::block_on
15//! [2]: task::block_in_place
16//! [3]: HandleExt::block_on_place
17
18use tokio::{runtime::Handle, task};
19
20/// Extension trait for [`tokio::runtime::Handle`] which allows calling
21/// [`block_on()`] from within a runtime context.
22pub trait HandleExt {
23    fn block_on_place<F: Future>(&self, future: F) -> F::Output;
24}
25
26impl HandleExt for Handle {
27    #[inline]
28    fn block_on_place<F: Future>(&self, future: F) -> F::Output {
29        #[cfg(feature = "tracing")]
30        if Handle::try_current().is_ok() {
31            tracing::warn!("Calling `block_on_place` from within a runtime context.");
32        }
33
34        task::block_in_place(|| self.block_on(future))
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
43    async fn inside_runtime() {
44        let handle = Handle::current();
45        handle.block_on_place(async {
46            // ...
47        });
48    }
49
50    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
51    async fn outside_runtime() {
52        let handle = Handle::current();
53        let task = task::spawn_blocking(move || {
54            handle.block_on_place(async {
55                // ...
56            });
57        });
58
59        task.await.expect("should not fail")
60    }
61}