tokio_executor/
enter.rs

1use std::cell::Cell;
2use std::error::Error;
3use std::fmt;
4use std::prelude::v1::*;
5
6use futures::{self, Future};
7
8thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
9
10/// Represents an executor context.
11///
12/// For more details, see [`enter` documentation](fn.enter.html)
13pub struct Enter {
14    on_exit: Vec<Box<dyn Callback>>,
15    permanent: bool,
16}
17
18/// An error returned by `enter` if an execution scope has already been
19/// entered.
20pub struct EnterError {
21    _a: (),
22}
23
24impl fmt::Debug for EnterError {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        f.debug_struct("EnterError")
27            .field("reason", &self.description())
28            .finish()
29    }
30}
31
32impl fmt::Display for EnterError {
33    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
34        write!(fmt, "{}", self.description())
35    }
36}
37
38impl Error for EnterError {
39    fn description(&self) -> &str {
40        "attempted to run an executor while another executor is already running"
41    }
42}
43
44/// Marks the current thread as being within the dynamic extent of an
45/// executor.
46///
47/// Executor implementations should call this function before blocking the
48/// thread. If `None` is returned, the executor should fail by panicking or
49/// taking some other action without blocking the current thread. This prevents
50/// deadlocks due to multiple executors competing for the same thread.
51///
52/// # Error
53///
54/// Returns an error if the current thread is already marked
55pub fn enter() -> Result<Enter, EnterError> {
56    ENTERED.with(|c| {
57        if c.get() {
58            Err(EnterError { _a: () })
59        } else {
60            c.set(true);
61
62            Ok(Enter {
63                on_exit: Vec::new(),
64                permanent: false,
65            })
66        }
67    })
68}
69
70// Forces the current "entered" state to be cleared while the closure
71// is executed.
72//
73// # Warning
74//
75// This is hidden for a reason. Do not use without fully understanding
76// executors. Misuing can easily cause your program to deadlock.
77#[doc(hidden)]
78pub fn exit<F: FnOnce() -> R, R>(f: F) -> R {
79    // Reset in case the closure panics
80    struct Reset;
81    impl Drop for Reset {
82        fn drop(&mut self) {
83            ENTERED.with(|c| {
84                c.set(true);
85            });
86        }
87    }
88
89    ENTERED.with(|c| {
90        debug_assert!(c.get());
91        c.set(false);
92    });
93
94    let reset = Reset;
95    let ret = f();
96    ::std::mem::forget(reset);
97
98    ENTERED.with(|c| {
99        assert!(!c.get(), "closure claimed permanent executor");
100        c.set(true);
101    });
102
103    ret
104}
105
106impl Enter {
107    /// Register a callback to be invoked if and when the thread
108    /// ceased to act as an executor.
109    pub fn on_exit<F>(&mut self, f: F)
110    where
111        F: FnOnce() + 'static,
112    {
113        self.on_exit.push(Box::new(f));
114    }
115
116    /// Treat the remainder of execution on this thread as part of an
117    /// executor; used mostly for thread pool worker threads.
118    ///
119    /// All registered `on_exit` callbacks are *dropped* without being
120    /// invoked.
121    pub fn make_permanent(mut self) {
122        self.permanent = true;
123    }
124
125    /// Blocks the thread on the specified future, returning the value with
126    /// which that future completes.
127    pub fn block_on<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
128        futures::executor::spawn(f).wait_future()
129    }
130}
131
132impl fmt::Debug for Enter {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        f.debug_struct("Enter").finish()
135    }
136}
137
138impl Drop for Enter {
139    fn drop(&mut self) {
140        ENTERED.with(|c| {
141            assert!(c.get());
142
143            if self.permanent {
144                return;
145            }
146
147            for callback in self.on_exit.drain(..) {
148                callback.call();
149            }
150
151            c.set(false);
152        });
153    }
154}
155
156trait Callback: 'static {
157    fn call(self: Box<Self>);
158}
159
160impl<F: FnOnce() + 'static> Callback for F {
161    fn call(self: Box<Self>) {
162        (*self)()
163    }
164}