Skip to main content

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}