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    /// 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}