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, rc::Rc, thread};
4
5use async_channel::{Receiver, Sender, unbounded};
6use parking_lot::Mutex;
7
8use crate::metrics::{LatencyMetrics, LatencyStats, start_latency_sampler};
9use crate::{Handle, HashMap, Id, System};
10
11thread_local!(
12    static ADDR: RefCell<Option<Arbiter>> = const { RefCell::new(None) };
13    static STORAGE: RefCell<HashMap<TypeId, Rc<dyn Any>>> = RefCell::new(HashMap::default());
14    static ON_SHUTDOWN: RefCell<Vec<Box<dyn FnOnce()>>> = const { RefCell::new(Vec::new()) };
15);
16
17pub(super) static COUNT: AtomicUsize = AtomicUsize::new(99);
18
19pub(super) enum ArbiterCommand {
20    Stop,
21}
22
23/// An asynchronous execution environment running on one OS thread.
24///
25/// Creating an arbiter starts a thread with its own local event loop. Futures
26/// spawned on that event loop are not required to implement `Send`.
27pub struct Arbiter(pub(crate) Arc<ArbiterInner>);
28
29type OnCloseStorage = Arc<Mutex<Vec<Box<dyn Fn() + Send + Sync>>>>;
30
31pub(crate) struct ArbiterInner {
32    id: usize,
33    name: Arc<String>,
34    sys_id: usize,
35    hnd: Option<Handle>,
36    pub(crate) sender: Sender<ArbiterCommand>,
37    thread_handle: Mutex<Option<thread::JoinHandle<()>>>,
38    on_stop: OnCloseStorage,
39    running: AtomicBool,
40    pub(crate) latency: Arc<LatencyStats>,
41    #[cfg(target_os = "linux")]
42    tid: i32,
43    #[cfg(windows)]
44    thread: Option<crate::watchdog::ThreadHandle>,
45}
46
47impl fmt::Debug for Arbiter {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(f, "Arbiter({:?})", self.0.name.as_ref())
50    }
51}
52
53impl Clone for Arbiter {
54    fn clone(&self) -> Self {
55        Self(self.0.clone())
56    }
57}
58
59impl Default for Arbiter {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl Arbiter {
66    #[allow(clippy::borrowed_box)]
67    pub(super) fn new_system(id: usize, name: String) -> (Self, ArbiterController) {
68        let (tx, rx) = unbounded();
69
70        let aid = COUNT.fetch_add(1, Ordering::Relaxed);
71        let arb = Arbiter::with_sender(id, aid, Arc::new(name), tx, Arc::default());
72        ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
73        clear_storage();
74
75        (
76            arb,
77            ArbiterController {
78                rx,
79                sys: None,
80                stop: None,
81            },
82        )
83    }
84
85    /// Returns the arbiter running on the current thread.
86    ///
87    /// # Panics
88    ///
89    /// Panics if no arbiter is running on the current thread.
90    pub fn current() -> Arbiter {
91        ADDR.with(|cell| match *cell.borrow() {
92            Some(ref addr) => addr.clone(),
93            None => panic!("Arbiter is not running"),
94        })
95    }
96
97    /// Requests that the arbiter stop its event loop.
98    pub fn stop(&self) {
99        let _ = self.0.sender.try_send(ArbiterCommand::Stop);
100    }
101
102    /// Starts an arbiter on a new thread with an automatically generated name.
103    pub fn new() -> Arbiter {
104        let id = COUNT.fetch_add(1, Ordering::Relaxed);
105        Arbiter::start(id, format!("{}:arb:{}", System::current().name(), id), None)
106    }
107
108    /// Starts an arbiter on a new thread with the specified name.
109    pub fn with_name(name: String) -> Arbiter {
110        Arbiter::start(COUNT.fetch_add(1, Ordering::Relaxed), name, None)
111    }
112
113    /// Starts an arbiter on a new thread with the specified name.
114    ///
115    /// `init` runs on the new thread before the arbiter's runtime is created,
116    /// for example to set the thread's CPU affinity, so that the runtime's
117    /// memory is allocated on the NUMA node of that CPU.
118    pub fn with_init<F>(name: String, init: F) -> Arbiter
119    where
120        F: FnOnce() + Send + 'static,
121    {
122        Arbiter::start(
123            COUNT.fetch_add(1, Ordering::Relaxed),
124            name,
125            Some(Box::new(init)),
126        )
127    }
128
129    fn start(id: usize, name: String, init: Option<Box<dyn FnOnce() + Send>>) -> Arbiter {
130        let sys = System::current();
131        let name2 = Arc::new(name.clone());
132        let config = sys.config();
133        let (arb_tx, arb_rx) = unbounded();
134
135        let builder = if sys.config().stack_size > 0 {
136            thread::Builder::new()
137                .name(name)
138                .stack_size(sys.config().stack_size)
139        } else {
140            thread::Builder::new().name(name)
141        };
142
143        let name = name2.clone();
144        let sys_id = sys.id();
145        let (arb_hnd_tx, arb_hnd_rx) = oneshot::channel();
146
147        let handle = builder
148            .spawn(move || {
149                if let Some(init) = init {
150                    init();
151                }
152                let name3 = name2.clone();
153                log::info!("Starting {name3:?} arbiter");
154
155                let sys2 = sys.clone();
156                let (stop, stop_rx) = oneshot::channel();
157                clear_storage();
158
159                // `block_on` finalizes the arbiter once its event loop exits
160                let result = crate::driver::block_on(config.runner.as_ref(), async move {
161                    let arb = Arbiter::with_sender(sys_id.0, id, name2, arb_tx, Arc::default());
162                    sys.register_arbiter(arb.clone());
163                    // `Arbiter::latency()` is available once the arbiter is returned
164                    start_latency_sampler(&arb.0.latency, config.latency_interval);
165                    arb_hnd_tx
166                        .send(arb.clone())
167                        .expect("Controller thread has gone");
168
169                    // start arbiter controller
170                    crate::spawn_detached(
171                        ArbiterController {
172                            sys: None,
173                            stop: Some(stop),
174                            rx: arb_rx,
175                        }
176                        .run(sys),
177                    );
178                    ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
179
180                    // run loop
181                    let _ = stop_rx.await;
182                });
183
184                let cleanup = || {
185                    // unregister arbiter
186                    sys2.unregister_arbiter(Id(id));
187                    // skipped by `block_on` if the event loop panicked
188                    run_shutdown_callbacks();
189                    unsafe {
190                        remove_all_items();
191                    }
192                };
193                if result.is_err() {
194                    crate::abort_on_panic(cleanup);
195                } else {
196                    cleanup();
197                }
198
199                if let Err(e) = result {
200                    log::error!("Arbiter {name3:?} has panicked.");
201                    panic::resume_unwind(e);
202                }
203                log::info!("Arbiter {name3:?} has stopped");
204            })
205            .unwrap_or_else(|err| panic!("Cannot spawn an arbiter's thread {name:?}: {err:?}"));
206
207        let arb = arb_hnd_rx.recv().expect("Could not start new arbiter");
208        *arb.0.thread_handle.lock() = Some(handle);
209        arb
210    }
211
212    fn with_sender(
213        sys_id: usize,
214        id: usize,
215        name: Arc<String>,
216        sender: Sender<ArbiterCommand>,
217        on_stop: OnCloseStorage,
218    ) -> Self {
219        #[cfg(feature = "tokio")]
220        let hnd = { Handle::new() };
221
222        #[cfg(feature = "compio")]
223        let hnd = { Handle::new() };
224
225        #[cfg(all(not(feature = "compio"), not(feature = "tokio")))]
226        let hnd = { Handle::current() };
227
228        Self(Arc::new(ArbiterInner {
229            id,
230            sys_id,
231            name,
232            sender,
233            on_stop,
234            hnd: Some(hnd),
235            thread_handle: Mutex::new(None),
236            running: AtomicBool::new(true),
237            latency: Arc::default(),
238            #[cfg(target_os = "linux")]
239            #[allow(clippy::cast_possible_truncation)]
240            tid: unsafe { libc::syscall(libc::SYS_gettid) } as i32,
241            #[cfg(windows)]
242            thread: crate::watchdog::ThreadHandle::current(),
243        }))
244    }
245
246    /// Returns the arbiter identifier.
247    pub fn id(&self) -> Id {
248        Id(self.0.id)
249    }
250
251    #[cfg(target_os = "linux")]
252    /// TID of the arbiter
253    pub(crate) fn tid(&self) -> i32 {
254        self.0.tid
255    }
256
257    #[cfg(windows)]
258    /// Handle of the arbiter thread
259    pub(crate) fn thread(&self) -> Option<&crate::watchdog::ThreadHandle> {
260        self.0.thread.as_ref()
261    }
262
263    /// Returns the arbiter name.
264    pub fn name(&self) -> &str {
265        self.0.name.as_ref()
266    }
267
268    #[inline]
269    /// Returns a handle to the arbiter's runtime.
270    pub fn handle(&self) -> &Handle {
271        self.0.hnd.as_ref().unwrap()
272    }
273
274    /// Returns the arbiter's scheduling latency.
275    ///
276    /// Returns `None` if latency sampling is disabled, see
277    /// [`Builder::latency_interval`](crate::Builder::latency_interval).
278    pub fn latency(&self) -> Option<LatencyMetrics> {
279        self.0.latency.metrics()
280    }
281
282    #[inline]
283    /// Returns whether the arbiter is running.
284    pub fn is_running(&self) -> bool {
285        self.0.running.load(Ordering::Relaxed)
286    }
287
288    /// Returns a value from thread-local arbiter storage, inserting it if absent.
289    ///
290    /// If the storage has already been destroyed because the thread is
291    /// exiting, the value returned by `f` is not stored.
292    pub fn get_value<T, F>(f: F) -> T
293    where
294        T: Clone + 'static,
295        F: FnOnce() -> T,
296    {
297        let mut f = Some(f);
298        STORAGE
299            .try_with(|cell| {
300                let mut st = cell.borrow_mut();
301                if let Some(boxed) = st.get(&TypeId::of::<T>())
302                    && let Some(val) = (&**boxed as &(dyn Any + 'static)).downcast_ref::<T>()
303                {
304                    return val.clone();
305                }
306                let val = (f.take().unwrap())();
307                st.insert(TypeId::of::<T>(), Rc::new(val.clone()));
308                val
309            })
310            .unwrap_or_else(|_| (f.take().unwrap())())
311    }
312
313    /// Registers a callback to run when the current thread's arbiter shuts down.
314    ///
315    /// Callbacks run once, in registration order, on the arbiter's thread when
316    /// its stop is requested, by [`Arbiter::stop()`] or [`System::stop()`],
317    /// while the event loop is still running. An arbiter that ends without a
318    /// stop request, such as one driven by
319    /// [`SystemRunner::block_on()`](crate::SystemRunner::block_on), runs them
320    /// once its event loop has exited instead. A callback registered by
321    /// another callback runs in the same shutdown.
322    ///
323    /// If the thread is exiting and its thread-local storage has already been
324    /// destroyed, `f` is dropped without running. If `f` panics after the
325    /// event loop panicked, the process is aborted.
326    pub fn on_shutdown<F>(f: F)
327    where
328        F: FnOnce() + 'static,
329    {
330        let f: Box<dyn FnOnce()> = Box::new(f);
331        let _ = ON_SHUTDOWN.try_with(move |cell| cell.borrow_mut().push(f));
332    }
333
334    #[must_use]
335    /// Adds a callback to run after the arbiter stops.
336    ///
337    /// If `f` panics after the event loop panicked, the process is aborted.
338    pub fn on_stop<F>(self, f: F) -> Self
339    where
340        F: Fn() + Send + Sync + 'static,
341    {
342        self.0.on_stop.lock().push(Box::new(f));
343        self
344    }
345
346    /// Returns the arbiter of the current thread, if any.
347    pub(crate) fn try_current() -> Option<Arbiter> {
348        ADDR.try_with(|cell| cell.borrow().clone()).ok().flatten()
349    }
350
351    /// Marks the arbiter as stopped and runs its `on_stop` callbacks,
352    /// called once its event loop has exited.
353    pub(crate) fn finalize(&self) {
354        self.0.running.store(false, Ordering::Relaxed);
355        let on_stop = mem::take(&mut *self.0.on_stop.lock());
356        // a panic while unwinding aborts the process
357        for f in on_stop {
358            f();
359        }
360    }
361
362    /// Waits for the arbiter's thread to stop.
363    ///
364    /// This returns immediately for an arbiter that does not own a thread
365    /// handle, including the system's primary arbiter.
366    pub fn join(&mut self) -> thread::Result<()> {
367        if let Some(thread_handle) = self.0.thread_handle.lock().take() {
368            thread_handle.join()
369        } else {
370            Ok(())
371        }
372    }
373}
374
375impl Eq for Arbiter {}
376
377impl PartialEq for Arbiter {
378    fn eq(&self, other: &Self) -> bool {
379        self.0.id == other.0.id && self.0.sys_id == other.0.sys_id
380    }
381}
382
383pub(crate) struct ArbiterController {
384    sys: Option<System>,
385    rx: Receiver<ArbiterCommand>,
386    stop: Option<oneshot::Sender<i32>>,
387}
388
389impl ArbiterController {
390    pub(super) async fn run(mut self, sys: System) {
391        self.sys = Some(sys);
392        while let Ok(ArbiterCommand::Stop) = self.rx.recv().await {
393            // the system arbiter has no `stop`, `System::stop()`
394            // runs its callbacks
395            if let Some(stop) = self.stop.take() {
396                run_shutdown_callbacks();
397                let _ = stop.send(0);
398            }
399        }
400    }
401}
402
403/// Runs the callbacks registered with [`Arbiter::on_shutdown()`], including
404/// ones registered while they run.
405pub(crate) fn run_shutdown_callbacks() {
406    loop {
407        let callbacks = ON_SHUTDOWN
408            .try_with(|cell| mem::take(&mut *cell.borrow_mut()))
409            .unwrap_or_default();
410        if callbacks.is_empty() {
411            break;
412        }
413        for f in callbacks {
414            f();
415        }
416    }
417}
418
419/// Inserts a value into the current arbiter's thread-local storage.
420///
421/// If the storage has already been destroyed because the thread is
422/// exiting, the value is dropped.
423pub fn set_item<T: 'static>(item: T) {
424    let item: Rc<dyn Any> = Rc::new(item);
425    let old = STORAGE
426        .try_with(move |cell| cell.borrow_mut().insert(TypeId::of::<T>(), item))
427        .ok()
428        .flatten();
429    drop(old);
430}
431
432/// Returns a cloned value from the current arbiter's thread-local storage.
433///
434/// Returns `None` if the storage has already been destroyed because the
435/// thread is exiting.
436pub fn get_item<T: Clone + 'static>() -> Option<T> {
437    STORAGE
438        .try_with(move |cell| {
439            cell.borrow()
440                .get(&TypeId::of::<T>())
441                .and_then(|boxed| boxed.downcast_ref())
442                .cloned()
443        })
444        .ok()
445        .flatten()
446}
447
448/// Provides access to a value in the current arbiter's thread-local storage.
449///
450/// A default value is inserted if the requested type is not already present.
451/// If the storage has already been destroyed because the thread is exiting,
452/// `f` receives a temporary default value that is not stored.
453pub fn with_item<T: Default + 'static, F, R>(f: F) -> R
454where
455    F: FnOnce(&T) -> R,
456{
457    // `f` holds its own reference, so the value stays alive even if `f`
458    // replaces or removes it
459    let val = STORAGE
460        .try_with(|cell| {
461            let existing = cell.borrow().get(&TypeId::of::<T>()).cloned();
462            existing.unwrap_or_else(|| {
463                let val: Rc<dyn Any> = Rc::new(T::default());
464                cell.borrow_mut().insert(TypeId::of::<T>(), val.clone());
465                val
466            })
467        })
468        .ok();
469    match val {
470        Some(val) => f(val.downcast_ref::<T>().unwrap()),
471        None => f(&T::default()),
472    }
473}
474
475#[doc(hidden)]
476/// Remove all items from storage.
477///
478/// # Safety
479///
480/// All outstanding calls to [`with_item`] must have completed.
481pub unsafe fn remove_all_items() {
482    clear_storage();
483    System::remove_current();
484}
485
486/// Removes all items from the storage.
487///
488/// Each item is dropped outside of the storage borrow, so that its destructor
489/// may access the storage. Items it inserts are removed too.
490fn clear_storage() {
491    let _ = STORAGE.try_with(|cell| {
492        loop {
493            let mut items = cell.borrow_mut();
494            let Some(key) = items.keys().next().copied() else {
495                break;
496            };
497            let item = items.remove(&key);
498            drop(items);
499            drop(item);
500        }
501    });
502}
503
504#[cfg(test)]
505mod tests {
506    use std::cell::Cell;
507
508    use super::*;
509    use crate::testing::TestRunner;
510
511    #[derive(Clone, Default)]
512    struct Value(usize);
513
514    struct DropTx(Option<oneshot::Sender<thread::ThreadId>>);
515
516    impl Drop for DropTx {
517        fn drop(&mut self) {
518            let _ = self.0.take().unwrap().send(thread::current().id());
519        }
520    }
521
522    #[test]
523    #[should_panic(expected = "Arbiter is not running")]
524    fn current_without_arbiter() {
525        let _ = Arbiter::current();
526    }
527
528    #[test]
529    fn arbiter_with_init() {
530        thread_local! {
531            static INIT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
532        }
533
534        System::new("arb-init", TestRunner).block_on(async {
535            let (tx, rx) = std::sync::mpsc::channel();
536            let mut arb = Arbiter::with_init("init".to_string(), move || {
537                INIT.with(|init| init.set(true));
538                let _ = tx.send(thread::current().name().map(str::to_string));
539            });
540            assert_eq!(rx.recv().unwrap().as_deref(), Some("init"));
541
542            // `init` ran on the arbiter's thread
543            let (tx, rx) = oneshot::async_channel();
544            arb.handle().spawn(async move {
545                let _ = tx.send(INIT.with(std::cell::Cell::get));
546            });
547            assert!(rx.await.unwrap());
548            assert!(!INIT.with(std::cell::Cell::get));
549
550            arb.stop();
551            arb.join().unwrap();
552        });
553    }
554
555    #[test]
556    fn arbiter_api() {
557        System::new("arb-test", TestRunner).block_on(async {
558            let sys = System::current();
559            let cur = Arbiter::current();
560            assert_eq!(cur.name(), "arb-test");
561            assert_eq!(cur, sys.arbiter());
562            assert!(cur.is_running());
563            assert_eq!(format!("{cur:?}"), "Arbiter(\"arb-test\")");
564            // the primary arbiter does not own a thread
565            assert!(cur.clone().join().is_ok());
566
567            let stopped = Arc::new(AtomicUsize::new(0));
568            let stopped2 = stopped.clone();
569            let mut arb = Arbiter::with_name("named".to_string()).on_stop(move || {
570                stopped2.fetch_add(1, Ordering::Relaxed);
571            });
572            assert_eq!(arb.name(), "named");
573            assert!(arb.is_running());
574            assert_ne!(arb, cur);
575            sys.list_arbiters(|arbs| assert!(arbs.contains(&arb)));
576
577            // shutdown callbacks run on the arbiter's thread
578            let (reg_tx, reg_rx) = oneshot::async_channel();
579            let (tx, rx) = oneshot::channel();
580            arb.handle().spawn(async move {
581                let name = Arbiter::current().name().to_string();
582                Arbiter::on_shutdown(move || {
583                    let _ = tx.send(name);
584                });
585                let _ = reg_tx.send(());
586            });
587            reg_rx.await.unwrap();
588
589            arb.stop();
590            arb.join().unwrap();
591            assert!(!arb.is_running());
592            assert_eq!(stopped.load(Ordering::Relaxed), 1);
593            assert_eq!(rx.recv().unwrap(), "named");
594            sys.list_arbiters(|arbs| assert!(!arbs.contains(&arb)));
595
596            let mut arb = Arbiter::default();
597            assert!(arb.name().starts_with("arb-test:arb:"));
598            arb.stop();
599            arb.join().unwrap();
600        });
601    }
602
603    #[test]
604    fn local_handles_survive_runtime_shutdown() {
605        struct Local {
606            owner: thread::ThreadId,
607            drops: Rc<Cell<usize>>,
608        }
609        impl Drop for Local {
610            fn drop(&mut self) {
611                assert_eq!(thread::current().id(), self.owner);
612                self.drops.set(self.drops.get() + 1);
613            }
614        }
615
616        for consume in [false, true] {
617            let drops = Rc::new(Cell::new(0));
618            let value = Local {
619                owner: thread::current().id(),
620                drops: drops.clone(),
621            };
622            let guard = Local {
623                owner: thread::current().id(),
624                drops: drops.clone(),
625            };
626            let handles = Rc::new(Cell::new(None));
627            let slot = handles.clone();
628            crate::Runner::block_on(
629                &TestRunner,
630                Box::pin(async move {
631                    let completed = crate::spawn(async move { value });
632                    let cancelled = crate::spawn(async move {
633                        let _guard = guard;
634                        std::future::pending::<()>().await;
635                    });
636                    std::future::poll_fn(|cx| {
637                        if completed.is_finished() {
638                            std::task::Poll::Ready(())
639                        } else {
640                            cx.waker().wake_by_ref();
641                            std::task::Poll::Pending
642                        }
643                    })
644                    .await;
645                    slot.set(Some((completed, cancelled)));
646                }),
647            )
648            .unwrap();
649            let (completed, mut cancelled) = handles.take().unwrap();
650            assert!(completed.is_finished());
651            assert!(cancelled.is_finished());
652            assert_eq!(drops.get(), 1);
653            let mut cx = std::task::Context::from_waker(std::task::Waker::noop());
654            assert!(matches!(
655                std::pin::Pin::new(&mut cancelled).poll(&mut cx),
656                std::task::Poll::Ready(Err(_))
657            ));
658            drop(cancelled);
659            if consume {
660                let mut completed = completed;
661                let std::task::Poll::Ready(Ok(value)) =
662                    std::pin::Pin::new(&mut completed).poll(&mut cx)
663                else {
664                    panic!("completed local result lost during shutdown");
665                };
666                drop(completed);
667                assert_eq!(drops.get(), 1);
668                drop(value);
669            } else {
670                completed.cancel();
671            }
672            assert_eq!(drops.get(), 2);
673        }
674    }
675
676    #[test]
677    fn detached_remote_output_is_owner_local() {
678        struct Output {
679            owner: thread::ThreadId,
680            done: Option<oneshot::Sender<()>>,
681            _local: Rc<()>,
682        }
683        impl Drop for Output {
684            fn drop(&mut self) {
685                assert_eq!(thread::current().id(), self.owner);
686                let _ = self.done.take().unwrap().send(());
687            }
688        }
689        // The closure's return type verifies that detached spawning returns ().
690        fn submit(f: impl FnOnce()) {
691            f();
692        }
693
694        System::new("remote-output", TestRunner).block_on(async {
695            let mut arb = Arbiter::new();
696            let (tx, rx) = oneshot::async_channel();
697            let caller = thread::current().id();
698            let future = async move {
699                let owner = thread::current().id();
700                assert_ne!(owner, caller);
701                Output {
702                    owner,
703                    done: Some(tx),
704                    _local: Rc::new(()),
705                }
706            };
707            submit(|| arb.handle().spawn_detached(future));
708            rx.await.unwrap();
709            arb.stop();
710            arb.join().unwrap();
711        });
712    }
713
714    #[test]
715    fn remote_join_handle() {
716        System::new("remote-results", TestRunner).block_on(async {
717            let mut arb = Arbiter::new();
718            let task: crate::RemoteJoinHandle<_> =
719                arb.handle().spawn(async { thread::current().id() });
720            let id = task.await.unwrap();
721            assert_ne!(id, thread::current().id());
722
723            // spawns from one producer run in order on the arbiter thread
724            let order = Arc::new(Mutex::new(Vec::new()));
725            for i in 0..100 {
726                let order = order.clone();
727                arb.handle().spawn_detached(async move {
728                    order.lock().push((i, thread::current().id()));
729                });
730            }
731            arb.handle().spawn(async {}).await.unwrap();
732            let order = std::mem::take(&mut *order.lock());
733            assert_eq!(order.len(), 100);
734            assert!(
735                order
736                    .iter()
737                    .enumerate()
738                    .all(|(i, (n, t))| i == *n && *t == id)
739            );
740
741            // a cancelled remote task is dropped on the arbiter thread
742            let (tx, rx) = oneshot::async_channel();
743            let guard = DropTx(Some(tx));
744            let task = arb.handle().spawn(async move {
745                let _guard = guard;
746                std::future::pending::<()>().await;
747            });
748            assert!(!task.is_finished());
749            task.cancel();
750            assert_eq!(rx.await.unwrap(), id);
751
752            // the owner spawns locally
753            let local: crate::JoinHandle<_> = crate::spawn(async { Rc::new(42) });
754            assert_eq!(*local.await.unwrap(), 42);
755            let task: crate::RemoteJoinHandle<_> =
756                Handle::current().spawn(async { thread::current().id() });
757            assert_eq!(task.await.unwrap(), thread::current().id());
758
759            let (tx, rx) = oneshot::async_channel();
760            let guard = DropTx(Some(tx));
761            Handle::current()
762                .spawn(async move {
763                    let _guard = guard;
764                    std::future::pending::<()>().await;
765                })
766                .cancel();
767            assert_eq!(rx.await.unwrap(), thread::current().id());
768
769            for detach in [false, true] {
770                let (start, ready) = oneshot::async_channel();
771                let (done, completed) = oneshot::async_channel();
772                let task = Handle::current().spawn(async move {
773                    ready.await.unwrap();
774                    let _ = done.send(());
775                });
776                if detach {
777                    task.detach();
778                } else {
779                    drop(task);
780                }
781                start.send(()).unwrap();
782                completed.await.unwrap();
783            }
784
785            let (start, ready) = oneshot::async_channel();
786            let (done, completed) = oneshot::async_channel();
787            let task = arb.handle().spawn(async move {
788                ready.await.unwrap();
789                let _ = done.send(());
790            });
791            drop(task);
792            start.send(()).unwrap();
793            completed.await.unwrap();
794            arb.stop();
795            arb.join().unwrap();
796        });
797    }
798
799    #[test]
800    fn handle_spawn_shutdown_while_running() {
801        struct Token(Option<oneshot::Sender<()>>);
802        impl Drop for Token {
803            fn drop(&mut self) {
804                let _ = self.0.take().unwrap().send(());
805            }
806        }
807
808        System::new("shutdown-running", TestRunner).block_on(async {
809            let mut arb = Arbiter::new();
810            let resumed = Arc::new(AtomicBool::new(false));
811            let resumed2 = resumed.clone();
812            let (started_tx, started_rx) = oneshot::async_channel();
813            let (drop_tx, drop_rx) = oneshot::async_channel();
814            let task = arb.handle().spawn(async move {
815                let _token = Token(Some(drop_tx));
816                let _ = started_tx.send(());
817                std::future::pending::<()>().await;
818                resumed2.store(true, Ordering::Relaxed);
819            });
820            started_rx.await.unwrap();
821            arb.stop();
822            arb.join().unwrap();
823            assert!(task.await.is_err());
824            // the future is dropped on the arbiter thread
825            let mut drop_rx = std::pin::pin!(drop_rx);
826            let mut timeout =
827                std::pin::pin!(futures_timer::Delay::new(std::time::Duration::from_secs(5)));
828            std::future::poll_fn(|cx| {
829                if drop_rx.as_mut().poll(cx).is_ready() {
830                    std::task::Poll::Ready(())
831                } else if timeout.as_mut().poll(cx).is_ready() {
832                    panic!("shutdown task is not dropped");
833                } else {
834                    std::task::Poll::Pending
835                }
836            })
837            .await;
838            assert!(!resumed.load(Ordering::Relaxed));
839
840            // Rejected submissions close the result channel without running.
841            let task = arb.handle().spawn(async { panic!("rejected task polled") });
842            assert!(task.await.is_err());
843        });
844    }
845
846    #[cfg(all(not(feature = "tokio"), not(feature = "compio")))]
847    #[test]
848    fn panicking_arbiter() {
849        /// Returns a panic of the event loop as an error, like the ntex-net
850        /// runners.
851        struct CatchRunner;
852
853        impl crate::Runner for CatchRunner {
854            fn block_on(
855                &self,
856                fut: crate::BlockFuture,
857            ) -> Result<(), Box<dyn std::any::Any + Send>> {
858                panic::catch_unwind(panic::AssertUnwindSafe(|| TestRunner.block_on(fut)))?
859            }
860        }
861
862        fn check(runner: impl crate::Runner) {
863            System::new("arb-panic", runner).block_on(async {
864                let sys = System::current();
865                let mut arb = Arbiter::with_name("panics".to_string());
866                let (tx, rx) = oneshot::channel();
867                let task = arb.handle().spawn(async move {
868                    Arbiter::on_shutdown(move || {
869                        let _ = tx.send(());
870                    });
871                    panic!("arbiter boom");
872                });
873
874                // the panic of the event loop is returned by `join`
875                let err = arb.join().unwrap_err();
876                assert_eq!(err.downcast_ref::<&str>(), Some(&"arbiter boom"));
877                assert!(task.await.is_err());
878                // the arbiter is unregistered and its shutdown callbacks ran
879                assert!(rx.recv().is_ok());
880                assert!(!arb.is_running());
881                sys.list_arbiters(|arbs| assert!(!arbs.contains(&arb)));
882            });
883        }
884
885        check(CatchRunner);
886        // the panic unwinds through the runner
887        check(TestRunner);
888    }
889
890    #[cfg(all(not(feature = "tokio"), not(feature = "compio")))]
891    #[test]
892    #[cfg_attr(miri, ignore = "spawns a process")]
893    fn panicking_arbiter_cleanup_aborts() {
894        const CHILD: &str = "NTEX_RT_ARBITER_CLEANUP_CHILD";
895
896        struct CatchRunner;
897
898        impl crate::Runner for CatchRunner {
899            fn block_on(
900                &self,
901                fut: crate::BlockFuture,
902            ) -> Result<(), Box<dyn std::any::Any + Send>> {
903                panic::catch_unwind(panic::AssertUnwindSafe(|| TestRunner.block_on(fut)))?
904            }
905        }
906
907        if let Ok(case) = std::env::var(CHILD) {
908            System::new("arb-panic", CatchRunner).block_on(async move {
909                let mut arb = Arbiter::with_name("panics".to_string());
910                let on_stop = case == "on_stop";
911                arb.handle().spawn(async move {
912                    if on_stop {
913                        let _ = Arbiter::current().on_stop(|| panic!("cleanup panic"));
914                    } else {
915                        Arbiter::on_shutdown(|| panic!("cleanup panic"));
916                    }
917                    panic!("arbiter boom");
918                });
919                let _ = arb.join();
920            });
921            return;
922        }
923
924        for case in ["on_stop", "on_shutdown"] {
925            let out = std::process::Command::new(std::env::current_exe().unwrap())
926                .args([
927                    "arbiter::tests::panicking_arbiter_cleanup_aborts",
928                    "--exact",
929                ])
930                .args(["--nocapture", "--test-threads=1"])
931                .env(CHILD, case)
932                .output()
933                .unwrap();
934            let stderr = String::from_utf8_lossy(&out.stderr);
935            assert!(!out.status.success(), "{case}: {stderr}");
936            // a normal test failure exits with 101, an abort does not
937            assert_ne!(out.status.code(), Some(101), "{case}: {stderr}");
938            assert!(stderr.contains("arbiter boom"), "{case}: {stderr}");
939            assert!(stderr.contains("cleanup panic"), "{case}: {stderr}");
940        }
941    }
942
943    #[test]
944    fn arbiter_values() {
945        System::new("test", TestRunner).block_on(async {
946            assert_eq!(Arbiter::get_value(|| Value(1)).0, 1);
947            assert_eq!(Arbiter::get_value(|| Value(2)).0, 1);
948        });
949    }
950
951    #[test]
952    fn on_shutdown_runs_after_block_on() {
953        let called = Rc::new(Cell::new(0));
954        let called2 = called.clone();
955        System::new("test", TestRunner).block_on(async move {
956            Arbiter::on_shutdown(move || {
957                called2.set(called2.get() + 1);
958                // registered by a callback, runs in the same shutdown
959                Arbiter::on_shutdown(move || called2.set(called2.get() + 1));
960            });
961        });
962        assert_eq!(called.get(), 2);
963    }
964
965    fn use_storage() {
966        set_item(Value(1));
967        let _ = get_item::<Value>();
968        with_item::<Value, _, _>(|v| v.0);
969        Arbiter::get_value(|| Value(2));
970    }
971
972    struct UseOnDrop;
973
974    impl Drop for UseOnDrop {
975        fn drop(&mut self) {
976            use_storage();
977        }
978    }
979
980    thread_local!(static HOLD: RefCell<Option<UseOnDrop>> = const { RefCell::new(None) });
981
982    #[test]
983    fn storage_access_during_thread_exit() {
984        // item stored in STORAGE accesses STORAGE while it is destroyed
985        thread::spawn(|| set_item(UseOnDrop)).join().unwrap();
986
987        // other thread local accesses STORAGE, in both destruction orders
988        thread::spawn(|| {
989            HOLD.with(|h| *h.borrow_mut() = Some(UseOnDrop));
990            use_storage();
991        })
992        .join()
993        .unwrap();
994        thread::spawn(|| {
995            use_storage();
996            HOLD.with(|h| *h.borrow_mut() = Some(UseOnDrop));
997        })
998        .join()
999        .unwrap();
1000    }
1001
1002    #[test]
1003    fn with_item_value_outlives_replacement() {
1004        #[derive(Clone, Default)]
1005        struct Item(std::rc::Rc<Vec<u8>>);
1006
1007        thread::spawn(|| {
1008            set_item(Item(std::rc::Rc::new(vec![1; 64])));
1009            let len = with_item::<Item, _, _>(|item| {
1010                // replaces and frees the stored value while `f` holds its clone
1011                set_item(Item::default());
1012                unsafe { remove_all_items() };
1013                item.0.len()
1014            });
1015            assert_eq!(len, 64);
1016            assert!(get_item::<Item>().is_none());
1017            assert_eq!(with_item::<Item, _, _>(|item| item.0.len()), 0);
1018        })
1019        .join()
1020        .unwrap();
1021    }
1022
1023    #[test]
1024    fn remove_all_items_drops_outside_borrow() {
1025        struct Item;
1026
1027        impl Drop for Item {
1028            fn drop(&mut self) {
1029                let _ = get_item::<u32>();
1030                set_item(2u64);
1031            }
1032        }
1033
1034        thread::spawn(|| {
1035            set_item(Item);
1036            set_item(1u32);
1037            unsafe { remove_all_items() };
1038            assert!(get_item::<u32>().is_none());
1039            assert!(get_item::<u64>().is_none(), "item inserted by a destructor");
1040        })
1041        .join()
1042        .unwrap();
1043    }
1044
1045    #[test]
1046    fn storage_fallback_values() {
1047        struct Check;
1048
1049        impl Drop for Check {
1050            fn drop(&mut self) {
1051                set_item(Value(5));
1052                assert!(get_item::<Value>().is_none());
1053                assert_eq!(with_item::<Value, _, _>(|v| v.0), 0);
1054                assert_eq!(Arbiter::get_value(|| Value(3)).0, 3);
1055                assert_eq!(Arbiter::get_value(|| Value(4)).0, 4);
1056            }
1057        }
1058        thread::spawn(|| set_item(Check)).join().unwrap();
1059    }
1060}