block-on-place 0.1.0

block_on meets block_in_place
Documentation
//!  # `block-on-place`
//!
//! For when you want to call [`block_on()`][1] on a [`tokio::runtime::Handle`] but
//! cannot 100% guarantee that you're not inside of a runtime.
//!
//! Internally, this uses [`task::block_in_place()`][2] to call
//! [`Handle::block_on()`][1].
//!
//! ## Features
//!
//! You can enable the `tracing` feature to get a warning when calling
//! [`block_on_place()`][3] from within a runtime context.
//!
//! [1]: Handle::block_on
//! [2]: task::block_in_place
//! [3]: HandleExt::block_on_place

use tokio::{runtime::Handle, task};

/// Extension trait for [`tokio::runtime::Handle`] which allows calling
/// [`block_on()`] from within a runtime context.
pub trait HandleExt {
    fn block_on_place<F: Future>(&self, future: F) -> F::Output;
}

impl HandleExt for Handle {
    #[inline]
    fn block_on_place<F: Future>(&self, future: F) -> F::Output {
        #[cfg(feature = "tracing")]
        if Handle::try_current().is_ok() {
            tracing::warn!("Calling `block_on_place` from within a runtime context.");
        }

        task::block_in_place(|| self.block_on(future))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
    async fn inside_runtime() {
        let handle = Handle::current();
        handle.block_on_place(async {
            // ...
        });
    }

    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
    async fn outside_runtime() {
        let handle = Handle::current();
        let task = task::spawn_blocking(move || {
            handle.block_on_place(async {
                // ...
            });
        });

        task.await.expect("should not fail")
    }
}