diff options
author | Jon Gjengset <jon@thesquareplanet.com> | 2020-04-20 19:18:47 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-20 19:18:47 -0400 |
commit | 282b00cbe888a96669877ce70662fba87e8c0e3c (patch) | |
tree | 49482c1ffc2862e8154fe81aec5a3c24eac8ba5a /tokio/src/runtime/thread_pool | |
parent | 5a548044d7bfd5d1c59d1a398d34ccbc29cbfe70 (diff) |
Be more principled about when blocking is ok (#2410)
This enables `block_in_place` to be used in more contexts. Specifically,
it allows you to block whenever you are off the tokio runtime (like if
you are not using tokio, are in a `spawn_blocking` closure, etc.), and
in the threaded scheduler's `block_on`. Blocking in `LocalSet` and the
basic scheduler's` block_on` is still disallowed.
Fixes #2327.
Fixes #2393.
Diffstat (limited to 'tokio/src/runtime/thread_pool')
-rw-r--r-- | tokio/src/runtime/thread_pool/mod.rs | 2 | ||||
-rw-r--r-- | tokio/src/runtime/thread_pool/worker.rs | 32 |
2 files changed, 31 insertions, 3 deletions
diff --git a/tokio/src/runtime/thread_pool/mod.rs b/tokio/src/runtime/thread_pool/mod.rs index 82e82d5b..ced9712d 100644 --- a/tokio/src/runtime/thread_pool/mod.rs +++ b/tokio/src/runtime/thread_pool/mod.rs @@ -78,7 +78,7 @@ impl ThreadPool { where F: Future, { - let mut enter = crate::runtime::enter(); + let mut enter = crate::runtime::enter(true); enter.block_on(future).expect("failed to park thread") } } diff --git a/tokio/src/runtime/thread_pool/worker.rs b/tokio/src/runtime/thread_pool/worker.rs index 2213ec6c..e31f237c 100644 --- a/tokio/src/runtime/thread_pool/worker.rs +++ b/tokio/src/runtime/thread_pool/worker.rs @@ -172,6 +172,8 @@ pub(super) fn create(size: usize, park: Parker) -> (Arc<Shared>, Launch) { } cfg_blocking! { + use crate::runtime::enter::EnterContext; + pub(crate) fn block_in_place<F, R>(f: F) -> R where F: FnOnce() -> R, @@ -203,7 +205,33 @@ cfg_blocking! { let mut had_core = false; CURRENT.with(|maybe_cx| { - let cx = maybe_cx.expect("can call blocking only when running in a spawned task on the multi-threaded runtime"); + match (crate::runtime::enter::context(), maybe_cx.is_some()) { + (EnterContext::Entered { .. }, true) => { + // We are on a thread pool runtime thread, so we just need to set up blocking. + } + (EnterContext::Entered { allow_blocking }, false) => { + // We are on an executor, but _not_ on the thread pool. + // That is _only_ okay if we are in a thread pool runtime's block_on method: + if allow_blocking { + return; + } else { + // This probably means we are on the basic_scheduler or in a LocalSet, + // where it is _not_ okay to block. + panic!("can call blocking only when running on the multi-threaded runtime"); + } + } + (EnterContext::NotEntered, true) => { + // This is a nested call to block_in_place (we already exited). + // All the necessary setup has already been done. + return; + } + (EnterContext::NotEntered, false) => { + // We are outside of the tokio runtime, so blocking is fine. + // We can also skip all of the thread pool blocking setup steps. + return; + } + } + let cx = maybe_cx.expect("no .is_some() == false cases above should lead here"); // Get the worker core. If none is set, then blocking is fine! let core = match cx.core.borrow_mut().take() { @@ -273,7 +301,7 @@ fn run(worker: Arc<Worker>) { core: RefCell::new(None), }; - let _enter = crate::runtime::enter(); + let _enter = crate::runtime::enter(true); CURRENT.set(&cx, || { // This should always be an error. It only returns a `Result` to support |