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