From d8139fef7a555f99adaa37e139787d26e24daba3 Mon Sep 17 00:00:00 2001 From: Dan Burkert Date: Fri, 24 Apr 2020 06:25:48 -0600 Subject: [PATCH] Add Handle::block_on method (#2437) --- tokio/src/macros/cfg.rs | 16 +++++++++++++++ tokio/src/park/mod.rs | 2 +- tokio/src/park/thread.rs | 2 +- tokio/src/runtime/enter.rs | 14 ++++++++----- tokio/src/runtime/handle.rs | 41 +++++++++++++++++++++++++++++++++++++ tokio/src/runtime/mod.rs | 21 +++++++++++++++++++ tokio/tests/rt_common.rs | 30 +++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 7 deletions(-) diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index 18beb1bdb..0679aa737 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -35,6 +35,22 @@ macro_rules! cfg_blocking_impl { } } +/// Enables enter::block_on +macro_rules! cfg_block_on { + ($($item:item)*) => { + $( + #[cfg(any( + feature = "blocking", + feature = "fs", + feature = "dns", + feature = "io-std", + feature = "rt-core", + ))] + $item + )* + } +} + /// Enables blocking API internals macro_rules! cfg_not_blocking_impl { ($($item:item)*) => { diff --git a/tokio/src/park/mod.rs b/tokio/src/park/mod.rs index a3e49bbed..04d3051d8 100644 --- a/tokio/src/park/mod.rs +++ b/tokio/src/park/mod.rs @@ -42,7 +42,7 @@ cfg_resource_drivers! { mod thread; pub(crate) use self::thread::ParkThread; -cfg_blocking_impl! { +cfg_block_on! { pub(crate) use self::thread::{CachedParkThread, ParkError}; } diff --git a/tokio/src/park/thread.rs b/tokio/src/park/thread.rs index a8cdf1432..2e2397c72 100644 --- a/tokio/src/park/thread.rs +++ b/tokio/src/park/thread.rs @@ -204,7 +204,7 @@ impl Unpark for UnparkThread { } } -cfg_blocking_impl! { +cfg_block_on! { use std::marker::PhantomData; use std::rc::Rc; diff --git a/tokio/src/runtime/enter.rs b/tokio/src/runtime/enter.rs index 9b3f2ad89..ad5580cca 100644 --- a/tokio/src/runtime/enter.rs +++ b/tokio/src/runtime/enter.rs @@ -138,14 +138,11 @@ cfg_rt_threaded! { } } -cfg_blocking_impl! { - use crate::park::ParkError; - use std::time::Duration; - +cfg_block_on! { impl Enter { /// Blocks the thread on the specified future, returning the value with /// which that future completes. - pub(crate) fn block_on(&mut self, mut f: F) -> Result + pub(crate) fn block_on(&mut self, mut f: F) -> Result where F: std::future::Future, { @@ -170,7 +167,14 @@ cfg_blocking_impl! { park.park()?; } } + } +} +cfg_blocking_impl! { + use crate::park::ParkError; + use std::time::Duration; + + impl Enter { /// Blocks the thread on the specified future for **at most** `timeout` /// /// If the future completes before `timeout`, the result is returned. If diff --git a/tokio/src/runtime/handle.rs b/tokio/src/runtime/handle.rs index db53543e8..b232431ba 100644 --- a/tokio/src/runtime/handle.rs +++ b/tokio/src/runtime/handle.rs @@ -119,6 +119,47 @@ cfg_rt_core! { { self.spawner.spawn(future) } + + /// Run a future to completion on the Tokio runtime from a synchronous + /// context. + /// + /// This runs the given future on the runtime, blocking until it is + /// complete, and yielding its resolved result. Any tasks or timers which + /// the future spawns internally will be executed on the runtime. + /// + /// This method should not be called from an asynchronous context. + /// + /// # Panics + /// + /// This function panics if the executor is at capacity, if the provided + /// future panics, or if called within an asynchronous execution context. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::runtime::Runtime; + /// use std::thread; + /// + /// // Create the runtime + /// let rt = Runtime::new().unwrap(); + /// let handle = rt.handle().clone(); + /// + /// // Use the runtime from another thread + /// let th = thread::spawn(move || { + /// // Execute the future, blocking the current thread until completion + /// handle.block_on(async { + /// println!("hello"); + /// }); + /// }); + /// + /// th.join().unwrap(); + /// ``` + pub fn block_on(&self, future: F) -> F::Output { + self.enter(|| { + let mut enter = crate::runtime::enter(true); + enter.block_on(future).expect("failed to park thread") + }) + } } } diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index 2d34e2b6b..293f5c936 100644 --- a/tokio/src/runtime/mod.rs +++ b/tokio/src/runtime/mod.rs @@ -402,12 +402,33 @@ impl Runtime { /// complete, and yielding its resolved result. Any tasks or timers which /// the future spawns internally will be executed on the runtime. /// + /// `&mut` is required as calling `block_on` **may** result in advancing the + /// state of the runtime. The details depend on how the runtime is + /// configured. [`runtime::Handle::block_on`][handle] provides a version + /// that takes `&self`. + /// /// This method should not be called from an asynchronous context. /// /// # Panics /// /// This function panics if the executor is at capacity, if the provided /// future panics, or if called within an asynchronous execution context. + /// + /// # Examples + /// + /// ```no_run + /// use tokio::runtime::Runtime; + /// + /// // Create the runtime + /// let mut rt = Runtime::new().unwrap(); + /// + /// // Execute the future, blocking the current thread until completion + /// rt.block_on(async { + /// println!("hello"); + /// }); + /// ``` + /// + /// [handle]: fn@Handle::block_on pub fn block_on(&mut self, future: F) -> F::Output { let kind = &mut self.kind; diff --git a/tokio/tests/rt_common.rs b/tokio/tests/rt_common.rs index 8dc0da3c5..9f2d3d668 100644 --- a/tokio/tests/rt_common.rs +++ b/tokio/tests/rt_common.rs @@ -82,6 +82,18 @@ rt_test! { assert!(win); } + #[test] + fn block_on_handle_sync() { + let rt = rt(); + + let mut win = false; + rt.handle().block_on(async { + win = true; + }); + + assert!(win); + } + #[test] fn block_on_async() { let mut rt = rt(); @@ -100,6 +112,24 @@ rt_test! { assert_eq!(out, "ZOMG"); } + #[test] + fn block_on_handle_async() { + let rt = rt(); + + let out = rt.handle().block_on(async { + let (tx, rx) = oneshot::channel(); + + thread::spawn(move || { + thread::sleep(Duration::from_millis(50)); + tx.send("ZOMG").unwrap(); + }); + + assert_ok!(rx.await) + }); + + assert_eq!(out, "ZOMG"); + } + #[test] fn spawn_one_bg() { let mut rt = rt();