actix_rt/arbiter.rs
1use std::{
2 cell::RefCell,
3 fmt,
4 future::Future,
5 io,
6 pin::Pin,
7 sync::atomic::{AtomicUsize, Ordering},
8 task::{Context, Poll},
9 thread,
10};
11
12use futures_core::ready;
13use tokio::sync::mpsc;
14
15use crate::system::{System, SystemCommand};
16
17pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
18
19thread_local!(
20 static HANDLE: RefCell<Option<ArbiterHandle>> = const { RefCell::new(None) };
21);
22
23pub(crate) enum ArbiterCommand {
24 Stop,
25 Execute(Pin<Box<dyn Future<Output = ()> + Send>>),
26}
27
28impl fmt::Debug for ArbiterCommand {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 ArbiterCommand::Stop => write!(f, "ArbiterCommand::Stop"),
32 ArbiterCommand::Execute(_) => write!(f, "ArbiterCommand::Execute"),
33 }
34 }
35}
36
37/// A handle for sending spawn and stop messages to an [Arbiter].
38#[derive(Debug, Clone)]
39pub struct ArbiterHandle {
40 tx: mpsc::UnboundedSender<ArbiterCommand>,
41}
42
43impl ArbiterHandle {
44 pub(crate) fn new(tx: mpsc::UnboundedSender<ArbiterCommand>) -> Self {
45 Self { tx }
46 }
47
48 /// Send a future to the [Arbiter]'s thread and spawn it.
49 ///
50 /// If you require a result, include a response channel in the future.
51 ///
52 /// Returns true if future was sent successfully and false if the [Arbiter] has died.
53 pub fn spawn<Fut>(&self, future: Fut) -> bool
54 where
55 Fut: Future<Output = ()> + Send + 'static,
56 {
57 self.tx
58 .send(ArbiterCommand::Execute(Box::pin(future)))
59 .is_ok()
60 }
61
62 /// Send a function to the [Arbiter]'s thread and execute it.
63 ///
64 /// Any result from the function is discarded. If you require a result, include a response
65 /// channel in the function.
66 ///
67 /// Returns true if function was sent successfully and false if the [Arbiter] has died.
68 pub fn spawn_fn<F>(&self, f: F) -> bool
69 where
70 F: FnOnce() + Send + 'static,
71 {
72 self.spawn(async { f() })
73 }
74
75 /// Instruct [Arbiter] to stop processing it's event loop.
76 ///
77 /// Returns true if stop message was sent successfully and false if the [Arbiter] has
78 /// been dropped.
79 pub fn stop(&self) -> bool {
80 self.tx.send(ArbiterCommand::Stop).is_ok()
81 }
82}
83
84/// An Arbiter represents a thread that provides an asynchronous execution environment for futures
85/// and functions.
86///
87/// When an arbiter is created, it spawns a new [OS thread](thread), and hosts an event loop.
88#[derive(Debug)]
89pub struct Arbiter {
90 tx: mpsc::UnboundedSender<ArbiterCommand>,
91 thread_handle: thread::JoinHandle<()>,
92}
93
94impl Arbiter {
95 /// Spawn a new Arbiter thread and start its event loop.
96 ///
97 /// # Panics
98 /// Panics if a [System] is not registered on the current thread, or if creating the Arbiter's
99 /// thread or Tokio runtime fails.
100 #[allow(clippy::new_without_default)]
101 pub fn new() -> Arbiter {
102 Self::try_new().expect("Failed to create new Arbiter")
103 }
104
105 /// Try to spawn a new Arbiter thread and start its event loop with the default Tokio runtime.
106 ///
107 /// # Panics
108 /// Panics if a [System] is not registered on the current thread.
109 ///
110 /// # Errors
111 /// Returns an `io::Error` if creating the underlying OS thread or Tokio runtime fails.
112 pub fn try_new() -> io::Result<Arbiter> {
113 Self::try_with_tokio_rt(crate::runtime::default_tokio_runtime)
114 }
115
116 /// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure.
117 ///
118 /// The closure may return any type that can be converted into [`Runtime`], such as
119 /// `tokio::runtime::Runtime`, `Arc<tokio::runtime::Runtime>`, or
120 /// `&'static tokio::runtime::Runtime`.
121 ///
122 /// # Panics
123 /// Panics if a [System] is not registered on the current thread, or if creating the Arbiter's
124 /// thread or Tokio runtime fails.
125 ///
126 /// [tokio-runtime]: tokio::runtime::Runtime
127 /// [`Runtime`]: crate::Runtime
128 pub fn with_tokio_rt<F, R>(runtime_factory: F) -> Arbiter
129 where
130 F: FnOnce() -> R + Send + 'static,
131 R: Into<crate::runtime::Runtime> + Send + 'static,
132 {
133 Self::try_with_tokio_rt(|| Ok(runtime_factory())).expect("Failed to create new Arbiter")
134 }
135
136 /// Try to spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure.
137 ///
138 /// The closure may return any `Result` whose success value can be converted into [`Runtime`],
139 /// such as `tokio::runtime::Runtime`, `Arc<tokio::runtime::Runtime>`, or
140 /// `&'static tokio::runtime::Runtime`.
141 ///
142 /// # Panics
143 /// Panics if a [System] is not registered on the current thread.
144 ///
145 /// # Errors
146 /// Returns an `io::Error` if creating the underlying OS thread or Tokio runtime fails.
147 ///
148 /// [tokio-runtime]: tokio::runtime::Runtime
149 /// [`Runtime`]: crate::Runtime
150 pub fn try_with_tokio_rt<F, R>(runtime_factory: F) -> io::Result<Arbiter>
151 where
152 F: FnOnce() -> io::Result<R> + Send + 'static,
153 R: Into<crate::runtime::Runtime> + Send + 'static,
154 {
155 let sys = System::current();
156 let system_id = sys.id();
157 let arb_id = COUNT.fetch_add(1, Ordering::Relaxed);
158
159 let name = format!("actix-rt|system:{system_id}|arbiter:{arb_id}");
160 let (tx, rx) = mpsc::unbounded_channel();
161
162 let (ready_tx, ready_rx) = std::sync::mpsc::channel::<io::Result<()>>();
163
164 let thread_handle = thread::Builder::new().name(name.clone()).spawn({
165 let tx = tx.clone();
166 move || {
167 let rt = match runtime_factory() {
168 Ok(rt) => rt.into(),
169 Err(err) => {
170 let _ = ready_tx.send(Err(err));
171 return;
172 }
173 };
174
175 let hnd = ArbiterHandle::new(tx);
176
177 System::set_current(sys);
178
179 HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
180
181 // register arbiter
182 let _ = System::current()
183 .tx()
184 .send(SystemCommand::RegisterArbiter(arb_id, hnd));
185
186 if ready_tx.send(Ok(())).is_err() {
187 unreachable!("Arbiter ready signal receiver should not be dropped before send");
188 }
189
190 // run arbiter event processing loop
191 rt.block_on(ArbiterRunner { rx });
192
193 // deregister arbiter
194 let _ = System::current()
195 .tx()
196 .send(SystemCommand::DeregisterArbiter(arb_id));
197 }
198 })?;
199
200 match ready_rx.recv() {
201 Ok(Ok(())) => Ok(Arbiter { tx, thread_handle }),
202 Ok(Err(err)) => {
203 let _ = thread_handle.join();
204 Err(err)
205 }
206 Err(_) => {
207 let _ = thread_handle.join();
208 Err(io::Error::other(format!(
209 "Arbiter thread {name} panicked during intialization"
210 )))
211 }
212 }
213 }
214
215 /// Sets up an Arbiter runner in a new System using the environment's local set.
216 pub(crate) fn in_new_system() -> ArbiterHandle {
217 let (tx, rx) = mpsc::unbounded_channel();
218
219 let hnd = ArbiterHandle::new(tx);
220
221 HANDLE.with(|cell| *cell.borrow_mut() = Some(hnd.clone()));
222
223 crate::spawn(ArbiterRunner { rx });
224
225 hnd
226 }
227
228 /// Return a handle to the this Arbiter's message sender.
229 pub fn handle(&self) -> ArbiterHandle {
230 ArbiterHandle::new(self.tx.clone())
231 }
232
233 /// Return a handle to the current thread's Arbiter's message sender.
234 ///
235 /// # Panics
236 /// Panics if no Arbiter is running on the current thread.
237 pub fn current() -> ArbiterHandle {
238 HANDLE.with(|cell| match *cell.borrow() {
239 Some(ref hnd) => hnd.clone(),
240 None => panic!("Arbiter is not running."),
241 })
242 }
243
244 /// Try to get current running arbiter handle.
245 ///
246 /// Returns `None` if no Arbiter has been started.
247 ///
248 /// Unlike [`current`](Self::current), this never panics.
249 pub fn try_current() -> Option<ArbiterHandle> {
250 HANDLE.with(|cell| cell.borrow().clone())
251 }
252
253 /// Stop Arbiter from continuing it's event loop.
254 ///
255 /// Returns true if stop message was sent successfully and false if the Arbiter has been dropped.
256 pub fn stop(&self) -> bool {
257 self.tx.send(ArbiterCommand::Stop).is_ok()
258 }
259
260 /// Send a future to the Arbiter's thread and spawn it.
261 ///
262 /// If you require a result, include a response channel in the future.
263 ///
264 /// Returns true if future was sent successfully and false if the Arbiter has died.
265 #[track_caller]
266 pub fn spawn<Fut>(&self, future: Fut) -> bool
267 where
268 Fut: Future<Output = ()> + Send + 'static,
269 {
270 self.tx
271 .send(ArbiterCommand::Execute(Box::pin(future)))
272 .is_ok()
273 }
274
275 /// Send a function to the Arbiter's thread and execute it.
276 ///
277 /// Any result from the function is discarded. If you require a result, include a response
278 /// channel in the function.
279 ///
280 /// Returns true if function was sent successfully and false if the Arbiter has died.
281 #[track_caller]
282 pub fn spawn_fn<F>(&self, f: F) -> bool
283 where
284 F: FnOnce() + Send + 'static,
285 {
286 self.spawn(async { f() })
287 }
288
289 /// Wait for Arbiter's event loop to complete.
290 ///
291 /// Joins the underlying OS thread handle. See [`JoinHandle::join`](thread::JoinHandle::join).
292 pub fn join(self) -> thread::Result<()> {
293 self.thread_handle.join()
294 }
295}
296
297/// A persistent future that processes [Arbiter] commands.
298struct ArbiterRunner {
299 rx: mpsc::UnboundedReceiver<ArbiterCommand>,
300}
301
302impl Future for ArbiterRunner {
303 type Output = ();
304
305 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
306 // process all items currently buffered in channel
307 loop {
308 match ready!(self.rx.poll_recv(cx)) {
309 // channel closed; no more messages can be received
310 None => return Poll::Ready(()),
311
312 // process arbiter command
313 Some(item) => match item {
314 ArbiterCommand::Stop => {
315 return Poll::Ready(());
316 }
317 ArbiterCommand::Execute(task_fut) => {
318 tokio::task::spawn_local(task_fut);
319 }
320 },
321 }
322 }
323 }
324}