mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-25 12:00:35 +00:00
Add executor::exit, allowing other executors inside threadpool::blocking.
This commit is contained in:
parent
517162792f
commit
fb9809c068
@ -61,6 +61,42 @@ pub fn enter() -> Result<Enter, EnterError> {
|
||||
})
|
||||
}
|
||||
|
||||
// Forces the current "entered" state to be cleared while the closure
|
||||
// is executed.
|
||||
//
|
||||
// # Warning
|
||||
//
|
||||
// This is hidden for a reason. Do not use without fully understanding
|
||||
// executors. Misuing can easily cause your program to deadlock.
|
||||
#[doc(hidden)]
|
||||
pub fn exit<F: FnOnce() -> R, R>(f: F) -> R {
|
||||
// Reset in case the closure panics
|
||||
struct Reset;
|
||||
impl Drop for Reset {
|
||||
fn drop(&mut self) {
|
||||
ENTERED.with(|c| {
|
||||
c.set(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ENTERED.with(|c| {
|
||||
debug_assert!(c.get());
|
||||
c.set(false);
|
||||
});
|
||||
|
||||
let reset = Reset;
|
||||
let ret = f();
|
||||
::std::mem::forget(reset);
|
||||
|
||||
ENTERED.with(|c| {
|
||||
assert!(!c.get(), "closure claimed permanent executor");
|
||||
c.set(true);
|
||||
});
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
impl Enter {
|
||||
/// Blocks the thread on the specified future, returning the value with
|
||||
/// which that future completes.
|
||||
|
@ -64,7 +64,7 @@ mod global;
|
||||
pub mod park;
|
||||
mod typed;
|
||||
|
||||
pub use crate::enter::{enter, Enter, EnterError};
|
||||
pub use crate::enter::{enter, exit, Enter, EnterError};
|
||||
pub use crate::error::SpawnError;
|
||||
pub use crate::executor::Executor;
|
||||
pub use crate::global::{spawn, with_default, DefaultExecutor};
|
||||
|
@ -4,6 +4,7 @@ use futures_core::ready;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::task::Poll;
|
||||
use tokio_executor;
|
||||
|
||||
/// Error raised by `blocking`.
|
||||
pub struct BlockingError {
|
||||
@ -140,7 +141,10 @@ where
|
||||
ready!(res)?;
|
||||
|
||||
// Currently in blocking mode, so call the inner closure
|
||||
let ret = f();
|
||||
//
|
||||
// "Exit" the current executor in case the blocking function wants
|
||||
// to call a different executor.
|
||||
let ret = tokio_executor::exit(move || f());
|
||||
|
||||
// Try to transition out of blocking mode. This is a fast path that takes
|
||||
// back ownership of the worker if the worker handoff didn't complete yet.
|
||||
|
@ -39,6 +39,26 @@ fn basic() {
|
||||
rx2.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_executors_can_run_inside_blocking() {
|
||||
let _ = ::env_logger::try_init();
|
||||
|
||||
let pool = Builder::new().pool_size(1).max_blocking(1).build();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
pool.spawn(async move {
|
||||
let res = blocking(|| {
|
||||
let _e = tokio_executor::enter().expect("nested blocking enter");
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
assert_ready!(res).unwrap();
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify_task_on_capacity() {
|
||||
const BLOCKING: usize = 10;
|
||||
|
Loading…
x
Reference in New Issue
Block a user