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
23pub 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 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 pub fn stop(&self) {
99 let _ = self.0.sender.try_send(ArbiterCommand::Stop);
100 }
101
102 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 pub fn with_name(name: String) -> Arbiter {
110 Arbiter::start(COUNT.fetch_add(1, Ordering::Relaxed), name, None)
111 }
112
113 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 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 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 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 let _ = stop_rx.await;
182 });
183
184 let cleanup = || {
185 sys2.unregister_arbiter(Id(id));
187 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 pub fn id(&self) -> Id {
248 Id(self.0.id)
249 }
250
251 #[cfg(target_os = "linux")]
252 pub(crate) fn tid(&self) -> i32 {
254 self.0.tid
255 }
256
257 #[cfg(windows)]
258 pub(crate) fn thread(&self) -> Option<&crate::watchdog::ThreadHandle> {
260 self.0.thread.as_ref()
261 }
262
263 pub fn name(&self) -> &str {
265 self.0.name.as_ref()
266 }
267
268 #[inline]
269 pub fn handle(&self) -> &Handle {
271 self.0.hnd.as_ref().unwrap()
272 }
273
274 pub fn latency(&self) -> Option<LatencyMetrics> {
279 self.0.latency.metrics()
280 }
281
282 #[inline]
283 pub fn is_running(&self) -> bool {
285 self.0.running.load(Ordering::Relaxed)
286 }
287
288 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 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 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 pub(crate) fn try_current() -> Option<Arbiter> {
348 ADDR.try_with(|cell| cell.borrow().clone()).ok().flatten()
349 }
350
351 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 for f in on_stop {
358 f();
359 }
360 }
361
362 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 if let Some(stop) = self.stop.take() {
396 run_shutdown_callbacks();
397 let _ = stop.send(0);
398 }
399 }
400 }
401}
402
403pub(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
419pub 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
432pub 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
448pub fn with_item<T: Default + 'static, F, R>(f: F) -> R
454where
455 F: FnOnce(&T) -> R,
456{
457 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)]
476pub unsafe fn remove_all_items() {
482 clear_storage();
483 System::remove_current();
484}
485
486fn 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 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 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 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 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 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 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 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 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 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 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 let err = arb.join().unwrap_err();
876 assert_eq!(err.downcast_ref::<&str>(), Some(&"arbiter boom"));
877 assert!(task.await.is_err());
878 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 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 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 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 thread::spawn(|| set_item(UseOnDrop)).join().unwrap();
986
987 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 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}