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
10pub struct Enter {
14 on_exit: Vec<Box<dyn Callback>>,
15 permanent: bool,
16}
17
18pub 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
44pub 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#[doc(hidden)]
78pub fn exit<F: FnOnce() -> R, R>(f: F) -> R {
79 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 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 pub fn make_permanent(mut self) {
122 self.permanent = true;
123 }
124
125 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}