diff options
Diffstat (limited to 'third_party/rust/tokio-executor/src/enter.rs')
-rw-r--r-- | third_party/rust/tokio-executor/src/enter.rs | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/third_party/rust/tokio-executor/src/enter.rs b/third_party/rust/tokio-executor/src/enter.rs new file mode 100644 index 0000000000..1f8ef1a701 --- /dev/null +++ b/third_party/rust/tokio-executor/src/enter.rs @@ -0,0 +1,164 @@ +use std::cell::Cell; +use std::error::Error; +use std::fmt; +use std::prelude::v1::*; + +use futures::{self, Future}; + +thread_local!(static ENTERED: Cell<bool> = Cell::new(false)); + +/// Represents an executor context. +/// +/// For more details, see [`enter` documentation](fn.enter.html) +pub struct Enter { + on_exit: Vec<Box<dyn Callback>>, + permanent: bool, +} + +/// An error returned by `enter` if an execution scope has already been +/// entered. +pub struct EnterError { + _a: (), +} + +impl fmt::Debug for EnterError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("EnterError") + .field("reason", &self.description()) + .finish() + } +} + +impl fmt::Display for EnterError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.description()) + } +} + +impl Error for EnterError { + fn description(&self) -> &str { + "attempted to run an executor while another executor is already running" + } +} + +/// Marks the current thread as being within the dynamic extent of an +/// executor. +/// +/// Executor implementations should call this function before blocking the +/// thread. If `None` is returned, the executor should fail by panicking or +/// taking some other action without blocking the current thread. This prevents +/// deadlocks due to multiple executors competing for the same thread. +/// +/// # Error +/// +/// Returns an error if the current thread is already marked +pub fn enter() -> Result<Enter, EnterError> { + ENTERED.with(|c| { + if c.get() { + Err(EnterError { _a: () }) + } else { + c.set(true); + + Ok(Enter { + on_exit: Vec::new(), + permanent: false, + }) + } + }) +} + +// 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 { + /// Register a callback to be invoked if and when the thread + /// ceased to act as an executor. + pub fn on_exit<F>(&mut self, f: F) + where + F: FnOnce() + 'static, + { + self.on_exit.push(Box::new(f)); + } + + /// Treat the remainder of execution on this thread as part of an + /// executor; used mostly for thread pool worker threads. + /// + /// All registered `on_exit` callbacks are *dropped* without being + /// invoked. + pub fn make_permanent(mut self) { + self.permanent = true; + } + + /// Blocks the thread on the specified future, returning the value with + /// which that future completes. + pub fn block_on<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> { + futures::executor::spawn(f).wait_future() + } +} + +impl fmt::Debug for Enter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Enter").finish() + } +} + +impl Drop for Enter { + fn drop(&mut self) { + ENTERED.with(|c| { + assert!(c.get()); + + if self.permanent { + return; + } + + for callback in self.on_exit.drain(..) { + callback.call(); + } + + c.set(false); + }); + } +} + +trait Callback: 'static { + fn call(self: Box<Self>); +} + +impl<F: FnOnce() + 'static> Callback for F { + fn call(self: Box<Self>) { + (*self)() + } +} |