Skip to main content

ntex_rt/
arbiter.rs

1#![allow(clippy::missing_panics_doc)]
2use std::sync::{Arc, atomic::AtomicBool, atomic::AtomicUsize, atomic::Ordering};
3use std::{any::Any, any::TypeId, cell::RefCell, fmt, mem, panic, pin::Pin, thread};
4
5use async_channel::{Receiver, Sender, unbounded};
6use parking_lot::Mutex;
7
8use crate::{Handle, HashMap, Id, System};
9
10thread_local!(
11    static ADDR: RefCell<Option<Arbiter>> = const { RefCell::new(None) };
12    static STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::default());
13);
14
15pub(super) static COUNT: AtomicUsize = AtomicUsize::new(99);
16
17pub(super) enum ArbiterCommand {
18    Stop,
19    #[allow(dead_code)]
20    Execute(Pin<Box<dyn Future<Output = ()> + Send>>),
21}
22
23/// Arbiters provide an asynchronous execution environment for actors, functions
24/// and futures.
25///
26/// When an Arbiter is created, it spawns a new OS thread, and
27/// hosts an event loop. Some Arbiter functions execute on the current thread.
28pub struct Arbiter(pub(crate) Arc<ArbiterInner>);
29
30type OnCloseStorage = Arc<Mutex<Vec<Box<dyn Fn() + Send + Sync>>>>;
31
32pub(crate) struct ArbiterInner {
33    id: usize,
34    name: Arc<String>,
35    sys_id: usize,
36    hnd: Option<Handle>,
37    pub(crate) sender: Sender<ArbiterCommand>,
38    thread_handle: Mutex<Option<thread::JoinHandle<()>>>,
39    on_stop: OnCloseStorage,
40    running: AtomicBool,
41    #[cfg(target_os = "linux")]
42    tid: i32,
43}
44
45impl fmt::Debug for Arbiter {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        write!(f, "Arbiter({:?})", self.0.name.as_ref())
48    }
49}
50
51impl Clone for Arbiter {
52    fn clone(&self) -> Self {
53        Self(self.0.clone())
54    }
55}
56
57impl Default for Arbiter {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl Arbiter {
64    #[allow(clippy::borrowed_box)]
65    pub(super) fn new_system(id: usize, name: String) -> (Self, ArbiterController) {
66        let (tx, rx) = unbounded();
67
68        let aid = COUNT.fetch_add(1, Ordering::Relaxed);
69        let arb = Arbiter::with_sender(id, aid, Arc::new(name), tx, Arc::default());
70        ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
71        STORAGE.with(|cell| cell.borrow_mut().clear());
72
73        (
74            arb,
75            ArbiterController {
76                rx,
77                sys: None,
78                stop: None,
79            },
80        )
81    }
82
83    /// Returns the current thread's arbiter's address
84    ///
85    /// # Panics
86    ///
87    /// Panics if Arbiter is not running
88    pub fn current() -> Arbiter {
89        ADDR.with(|cell| match *cell.borrow() {
90            Some(ref addr) => addr.clone(),
91            None => panic!("Arbiter is not running"),
92        })
93    }
94
95    /// Stop arbiter from continuing it's event loop.
96    pub fn stop(&self) {
97        let _ = self.0.sender.try_send(ArbiterCommand::Stop);
98    }
99
100    /// Spawn new thread and run runtime in spawned thread.
101    /// Returns address of newly created arbiter.
102    pub fn new() -> Arbiter {
103        let id = COUNT.load(Ordering::Relaxed) + 1;
104        Arbiter::with_name(format!("{}:arb:{}", System::current().name(), id))
105    }
106
107    /// Spawn new thread and run runtime in spawned thread
108    ///
109    /// Returns address of newly created arbiter.
110    pub fn with_name(name: String) -> Arbiter {
111        let id = COUNT.fetch_add(1, Ordering::Relaxed);
112        let sys = System::current();
113        let name2 = Arc::new(name.clone());
114        let config = sys.config();
115        let (arb_tx, arb_rx) = unbounded();
116
117        let builder = if sys.config().stack_size > 0 {
118            thread::Builder::new()
119                .name(name)
120                .stack_size(sys.config().stack_size)
121        } else {
122            thread::Builder::new().name(name)
123        };
124
125        let name = name2.clone();
126        let sys_id = sys.id();
127        let (arb_hnd_tx, arb_hnd_rx) = oneshot::channel();
128
129        let handle = builder
130            .spawn(move || {
131                let name3 = name2.clone();
132                log::info!("Starting {name3:?} arbiter");
133
134                let sys2 = sys.clone();
135                let (stop, stop_rx) = oneshot::channel();
136                STORAGE.with(|cell| cell.borrow_mut().clear());
137
138                let on_stop = Arc::new(Mutex::new(Vec::new()));
139                let on_stop2 = on_stop.clone();
140
141                let result = crate::driver::block_on(config.runner.as_ref(), async move {
142                    let arb = Arbiter::with_sender(sys_id.0, id, name2, arb_tx, on_stop);
143                    sys.register_arbiter(arb.clone());
144                    arb_hnd_tx
145                        .send(arb.clone())
146                        .expect("Controller thread has gone");
147
148                    // start arbiter controller
149                    crate::spawn(
150                        ArbiterController {
151                            sys: None,
152                            stop: Some(stop),
153                            rx: arb_rx,
154                        }
155                        .run(sys),
156                    );
157                    ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
158
159                    // run loop
160                    let _ = stop_rx.await;
161
162                    // mark as not running
163                    arb.0.running.store(false, Ordering::Relaxed);
164                });
165
166                let on_stop = mem::take(&mut *on_stop2.lock());
167                for f in on_stop {
168                    f();
169                }
170
171                // unregister arbiter
172                sys2.unregister_arbiter(Id(id));
173                unsafe {
174                    remove_all_items();
175                }
176
177                if let Err(e) = result {
178                    log::error!("Arbiter {name3:?} has panicked.");
179                    panic::resume_unwind(e);
180                }
181                log::info!("Arbiter {name3:?} has stopped");
182            })
183            .unwrap_or_else(|err| panic!("Cannot spawn an arbiter's thread {name:?}: {err:?}"));
184
185        let arb = arb_hnd_rx.recv().expect("Could not start new arbiter");
186        *arb.0.thread_handle.lock() = Some(handle);
187        arb
188    }
189
190    fn with_sender(
191        sys_id: usize,
192        id: usize,
193        name: Arc<String>,
194        sender: Sender<ArbiterCommand>,
195        on_stop: OnCloseStorage,
196    ) -> Self {
197        #[cfg(feature = "tokio")]
198        let hnd = { Handle::new(sender.clone()) };
199
200        #[cfg(feature = "compio")]
201        let hnd = { Handle::new(sender.clone()) };
202
203        #[cfg(all(not(feature = "compio"), not(feature = "tokio")))]
204        let hnd = { Handle::current() };
205
206        Self(Arc::new(ArbiterInner {
207            id,
208            sys_id,
209            name,
210            sender,
211            on_stop,
212            hnd: Some(hnd),
213            thread_handle: Mutex::new(None),
214            running: AtomicBool::new(true),
215            #[cfg(target_os = "linux")]
216            #[allow(clippy::cast_possible_truncation)]
217            tid: unsafe { libc::syscall(libc::SYS_gettid) } as i32,
218        }))
219    }
220
221    /// Id of the arbiter
222    pub fn id(&self) -> Id {
223        Id(self.0.id)
224    }
225
226    #[cfg(target_os = "linux")]
227    /// TID of the arbiter
228    pub(crate) fn tid(&self) -> i32 {
229        self.0.tid
230    }
231
232    /// Name of the arbiter
233    pub fn name(&self) -> &str {
234        self.0.name.as_ref()
235    }
236
237    #[inline]
238    /// Handle to a runtime
239    pub fn handle(&self) -> &Handle {
240        self.0.hnd.as_ref().unwrap()
241    }
242
243    #[inline]
244    /// Check if arbiter is running
245    pub fn is_running(&self) -> bool {
246        self.0.running.load(Ordering::Relaxed)
247    }
248
249    /// Get a type previously inserted to this runtime or create new one.
250    pub fn get_value<T, F>(f: F) -> T
251    where
252        T: Clone + 'static,
253        F: FnOnce() -> T,
254    {
255        STORAGE.with(move |cell| {
256            let mut st = cell.borrow_mut();
257            if let Some(boxed) = st.get(&TypeId::of::<T>())
258                && let Some(val) = (&**boxed as &(dyn Any + 'static)).downcast_ref::<T>()
259            {
260                return val.clone();
261            }
262            let val = f();
263            st.insert(TypeId::of::<T>(), Box::new(val.clone()));
264            val
265        })
266    }
267
268    #[must_use]
269    /// Add "on-stop" callback.
270    pub fn on_stop<F>(self, f: F) -> Self
271    where
272        F: Fn() + Send + Sync + 'static,
273    {
274        self.0.on_stop.lock().push(Box::new(f));
275        self
276    }
277
278    /// Wait for the event loop to stop by joining the underlying thread (if have Some).
279    pub fn join(&mut self) -> thread::Result<()> {
280        if let Some(thread_handle) = self.0.thread_handle.lock().take() {
281            thread_handle.join()
282        } else {
283            Ok(())
284        }
285    }
286}
287
288impl Eq for Arbiter {}
289
290impl PartialEq for Arbiter {
291    fn eq(&self, other: &Self) -> bool {
292        self.0.id == other.0.id && self.0.sys_id == other.0.sys_id
293    }
294}
295
296pub(crate) struct ArbiterController {
297    sys: Option<System>,
298    rx: Receiver<ArbiterCommand>,
299    stop: Option<oneshot::Sender<i32>>,
300}
301
302impl ArbiterController {
303    pub(super) async fn run(mut self, sys: System) {
304        self.sys = Some(sys);
305        loop {
306            match self.rx.recv().await {
307                Ok(ArbiterCommand::Stop) => {
308                    if let Some(stop) = self.stop.take() {
309                        let _ = stop.send(0);
310                    }
311                }
312                Ok(ArbiterCommand::Execute(fut)) => {
313                    crate::spawn(fut);
314                }
315                Err(_) => break,
316            }
317        }
318    }
319}
320
321/// Set item to current runtime's storage
322pub fn set_item<T: 'static>(item: T) {
323    STORAGE.with(move |cell| cell.borrow_mut().insert(TypeId::of::<T>(), Box::new(item)));
324}
325
326/// Get a reference to a type previously inserted on this runtime's storage
327pub fn get_item<T: Clone + 'static>() -> Option<T> {
328    STORAGE.with(move |cell| {
329        cell.borrow()
330            .get(&TypeId::of::<T>())
331            .and_then(|boxed| boxed.downcast_ref())
332            .cloned()
333    })
334}
335
336/// Get a reference to a type or create new if it doesnt exists
337pub fn with_item<T: Default + 'static, F, R>(f: F) -> R
338where
339    F: FnOnce(&T) -> R,
340{
341    STORAGE.with(move |cell| {
342        // SAFETY: value of T is stored in heap, manipulation
343        // with STORAGE are not affected location of T
344        let val: &T = unsafe {
345            let mut st = cell.borrow_mut();
346            if let Some(boxed) = st.get(&TypeId::of::<T>()) {
347                std::mem::transmute::<&T, &T>(boxed.downcast_ref::<T>().unwrap())
348            } else {
349                st.insert(TypeId::of::<T>(), Box::new(T::default()));
350                let boxed = st.get(&TypeId::of::<T>()).unwrap();
351                std::mem::transmute::<&T, &T>(boxed.downcast_ref::<T>().unwrap())
352            }
353        };
354        f(val)
355    })
356}
357
358#[doc(hidden)]
359/// Remove all items from storage.
360///
361/// # Safety
362///
363/// Must ensure that all outstading calls to `with_item` are completed.
364pub unsafe fn remove_all_items() {
365    STORAGE.with(move |cell| {
366        loop {
367            let mut items = cell.borrow_mut();
368            let Some(key) = items.keys().next().copied() else {
369                break;
370            };
371            items.remove(&key);
372            drop(items);
373        }
374    });
375    System::remove_current();
376}