Skip to main content

actix_rt/
arbiter.rs

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