actix_rt/system.rs
1use std::{
2 cell::RefCell,
3 collections::HashMap,
4 future::Future,
5 io,
6 pin::Pin,
7 sync::atomic::{AtomicUsize, Ordering},
8 task::{Context, Poll},
9};
10
11use futures_core::ready;
12use tokio::sync::{mpsc, watch};
13
14use crate::{arbiter::ArbiterHandle, Arbiter};
15
16static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
17
18thread_local!(
19 static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
20);
21
22/// A manager for a per-thread distributed async runtime.
23#[derive(Clone, Debug)]
24pub struct System {
25 id: usize,
26 sys_tx: mpsc::UnboundedSender<SystemCommand>,
27
28 /// Handle to the first [Arbiter] that is created with the System.
29 arbiter_handle: ArbiterHandle,
30}
31
32impl System {
33 /// Create a new system.
34 ///
35 /// # Panics
36 /// Panics if underlying Tokio runtime can not be created.
37 #[allow(clippy::new_ret_no_self)]
38 pub fn new() -> SystemRunner {
39 Self::with_tokio_rt(|| {
40 crate::runtime::default_tokio_runtime()
41 .expect("Default Actix (Tokio) runtime could not be created.")
42 })
43 }
44
45 /// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
46 ///
47 /// The closure may return any type that can be converted into [`Runtime`], such as
48 /// `tokio::runtime::Runtime`, `Arc<tokio::runtime::Runtime>`, or
49 /// `&'static tokio::runtime::Runtime`.
50 ///
51 /// [tokio-runtime]: tokio::runtime::Runtime
52 /// [`Runtime`]: crate::Runtime
53 pub fn with_tokio_rt<F, R>(runtime_factory: F) -> SystemRunner
54 where
55 F: FnOnce() -> R,
56 R: Into<crate::runtime::Runtime>,
57 {
58 let (stop_tx, stop_rx) = watch::channel(None);
59 let (sys_tx, sys_rx) = mpsc::unbounded_channel();
60
61 let rt = runtime_factory().into();
62 let sys_arbiter = rt.block_on(async { Arbiter::in_new_system() });
63 let system = System::construct(sys_tx, sys_arbiter.clone());
64
65 system
66 .tx()
67 .send(SystemCommand::RegisterArbiter(usize::MAX, sys_arbiter))
68 .unwrap();
69
70 // init background system arbiter
71 let sys_ctrl = SystemController::new(sys_rx, stop_tx);
72 rt.spawn(sys_ctrl);
73
74 SystemRunner { rt, stop_rx }
75 }
76}
77
78impl System {
79 /// Constructs new system and registers it on the current thread.
80 pub(crate) fn construct(
81 sys_tx: mpsc::UnboundedSender<SystemCommand>,
82 arbiter_handle: ArbiterHandle,
83 ) -> Self {
84 let sys = System {
85 sys_tx,
86 arbiter_handle,
87 id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst),
88 };
89
90 System::set_current(sys.clone());
91
92 sys
93 }
94
95 /// Get current running system.
96 ///
97 /// # Panics
98 /// Panics if no system is registered on the current thread.
99 pub fn current() -> System {
100 CURRENT.with(|cell| match *cell.borrow() {
101 Some(ref sys) => sys.clone(),
102 None => panic!("System is not running"),
103 })
104 }
105
106 /// Try to get current running system.
107 ///
108 /// Returns `None` if no System has been started.
109 ///
110 /// Unlike [`current`](Self::current), this never panics.
111 pub fn try_current() -> Option<System> {
112 CURRENT.with(|cell| cell.borrow().clone())
113 }
114
115 /// Get handle to a the System's initial [Arbiter].
116 pub fn arbiter(&self) -> &ArbiterHandle {
117 &self.arbiter_handle
118 }
119
120 /// Check if there is a System registered on the current thread.
121 pub fn is_registered() -> bool {
122 CURRENT.with(|sys| sys.borrow().is_some())
123 }
124
125 /// Register given system on current thread.
126 #[doc(hidden)]
127 pub fn set_current(sys: System) {
128 CURRENT.with(|cell| {
129 *cell.borrow_mut() = Some(sys);
130 })
131 }
132
133 /// Numeric system identifier.
134 ///
135 /// Useful when using multiple Systems.
136 pub fn id(&self) -> usize {
137 self.id
138 }
139
140 /// Stop the system (with code 0).
141 pub fn stop(&self) {
142 self.stop_with_code(0)
143 }
144
145 /// Stop the system with a given exit code.
146 pub fn stop_with_code(&self, code: i32) {
147 let _ = self.sys_tx.send(SystemCommand::Exit(code));
148 }
149
150 pub(crate) fn tx(&self) -> &mpsc::UnboundedSender<SystemCommand> {
151 &self.sys_tx
152 }
153}
154
155/// Runner that keeps a [System]'s event loop alive until stop message is received.
156#[must_use = "A SystemRunner does nothing unless `run` is called."]
157#[derive(Debug)]
158pub struct SystemRunner {
159 rt: crate::runtime::Runtime,
160 stop_rx: watch::Receiver<Option<i32>>,
161}
162
163impl SystemRunner {
164 /// Starts event loop and will return once [System] is [stopped](System::stop).
165 pub fn run(self) -> io::Result<()> {
166 let exit_code = self.run_with_code()?;
167
168 match exit_code {
169 0 => Ok(()),
170 nonzero => Err(io::Error::other(format!("Non-zero exit code: {nonzero}"))),
171 }
172 }
173
174 /// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
175 pub fn run_with_code(self) -> io::Result<i32> {
176 let SystemRunner { rt, stop_rx, .. } = self;
177
178 // run loop
179 rt.block_on(wait_for_stop(stop_rx))
180 }
181
182 /// Retrieves a reference to the underlying [Actix runtime](crate::Runtime) associated with this
183 /// `SystemRunner` instance.
184 ///
185 /// The Actix runtime is responsible for managing the event loop for an Actix system and
186 /// executing asynchronous tasks. This method provides access to the runtime, allowing direct
187 /// interaction with its features.
188 ///
189 /// In a typical use case, you might need to share the same runtime between different
190 /// parts of your project. For example, some components might require a [`Runtime`] to spawn
191 /// tasks on the same runtime.
192 ///
193 /// Read more in the documentation for [`Runtime`].
194 ///
195 /// # Examples
196 ///
197 /// ```
198 /// let system_runner = actix_rt::System::new();
199 /// let actix_runtime = system_runner.runtime();
200 ///
201 /// // Use the runtime to spawn an async task or perform other operations
202 /// ```
203 ///
204 /// # Note
205 ///
206 /// While this method provides an immutable reference to the Actix runtime, which is safe to
207 /// share across threads, be aware that spawning blocking tasks on the Actix runtime could
208 /// potentially impact system performance. This is because the Actix runtime is responsible for
209 /// driving the system, and blocking tasks could delay other tasks in the run loop.
210 ///
211 /// [`Runtime`]: crate::Runtime
212 pub fn runtime(&self) -> &crate::runtime::Runtime {
213 &self.rt
214 }
215
216 /// Returns a future that resolves with the system's exit code when it is stopped.
217 ///
218 /// This can be used to react to a system stop signal while running a future with
219 /// [`SystemRunner::block_on`], such as when coordinating shutdown with `tokio::select!`.
220 ///
221 /// # Examples
222 /// ```no_run
223 /// use std::process::ExitCode;
224 /// use actix_rt::System;
225 ///
226 /// let sys = System::new();
227 /// let stop = sys.stop_future();
228 ///
229 /// let exit = sys.block_on(async move {
230 /// actix_rt::spawn(async {
231 /// System::current().stop_with_code(0);
232 /// });
233 ///
234 /// let code = stop.await.unwrap_or(1);
235 /// ExitCode::from(code as u8)
236 /// });
237 ///
238 /// # drop(exit);
239 /// ```
240 pub fn stop_future(&self) -> SystemStop {
241 SystemStop::new(self.stop_rx.clone())
242 }
243
244 /// Splits this runner into its runtime and a future that resolves when the system stops.
245 ///
246 /// After calling this method, [`SystemRunner::run`] and [`SystemRunner::run_with_code`] can no
247 /// longer be used.
248 pub fn into_parts(self) -> (crate::runtime::Runtime, SystemStop) {
249 let SystemRunner { rt, stop_rx } = self;
250 (rt, SystemStop::new(stop_rx))
251 }
252
253 /// Runs the provided future, blocking the current thread until the future completes.
254 #[track_caller]
255 #[inline]
256 pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
257 self.rt.block_on(fut)
258 }
259}
260
261/// Future that resolves with the exit code when a [`System`] is stopped.
262#[must_use = "SystemStop does nothing unless polled or awaited."]
263pub struct SystemStop {
264 inner: Pin<Box<dyn Future<Output = io::Result<i32>> + 'static>>,
265}
266
267impl SystemStop {
268 fn new(stop_rx: watch::Receiver<Option<i32>>) -> Self {
269 Self {
270 inner: Box::pin(wait_for_stop(stop_rx)),
271 }
272 }
273}
274
275impl Future for SystemStop {
276 type Output = io::Result<i32>;
277
278 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
279 self.inner.as_mut().poll(cx)
280 }
281}
282
283async fn wait_for_stop(mut stop_rx: watch::Receiver<Option<i32>>) -> io::Result<i32> {
284 loop {
285 if let Some(code) = *stop_rx.borrow() {
286 return Ok(code);
287 }
288
289 stop_rx.changed().await.map_err(io::Error::other)?;
290 }
291}
292
293#[derive(Debug)]
294pub(crate) enum SystemCommand {
295 Exit(i32),
296 RegisterArbiter(usize, ArbiterHandle),
297 DeregisterArbiter(usize),
298}
299
300/// There is one `SystemController` per [System]. It runs in the background, keeping track of
301/// [Arbiter]s and is able to distribute a system-wide stop command.
302#[derive(Debug)]
303pub(crate) struct SystemController {
304 stop_tx: Option<watch::Sender<Option<i32>>>,
305 cmd_rx: mpsc::UnboundedReceiver<SystemCommand>,
306 arbiters: HashMap<usize, ArbiterHandle>,
307}
308
309impl SystemController {
310 pub(crate) fn new(
311 cmd_rx: mpsc::UnboundedReceiver<SystemCommand>,
312 stop_tx: watch::Sender<Option<i32>>,
313 ) -> Self {
314 SystemController {
315 cmd_rx,
316 stop_tx: Some(stop_tx),
317 arbiters: HashMap::with_capacity(4),
318 }
319 }
320}
321
322impl Future for SystemController {
323 type Output = ();
324
325 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
326 // process all items currently buffered in channel
327 loop {
328 match ready!(self.cmd_rx.poll_recv(cx)) {
329 // channel closed; no more messages can be received
330 None => return Poll::Ready(()),
331
332 // process system command
333 Some(cmd) => match cmd {
334 SystemCommand::Exit(code) => {
335 // stop all arbiters
336 for arb in self.arbiters.values() {
337 arb.stop();
338 }
339
340 // stop event loop
341 // will only fire once
342 if let Some(stop_tx) = self.stop_tx.take() {
343 let _ = stop_tx.send(Some(code));
344 }
345 }
346
347 SystemCommand::RegisterArbiter(id, arb) => {
348 self.arbiters.insert(id, arb);
349 }
350
351 SystemCommand::DeregisterArbiter(id) => {
352 self.arbiters.remove(&id);
353 }
354 },
355 }
356 }
357 }
358}