1use std::{
7 any::{type_name, Any},
8 collections::HashMap,
9 ops::Deref,
10 panic::Location,
11 sync::{
12 atomic::{AtomicUsize, Ordering},
13 Arc, Mutex, Weak,
14 },
15};
16
17use log::warn;
18
19static RESOURCE_ID: AtomicUsize = AtomicUsize::new(0);
20
21pub struct ResourceHandle<R> {
23 id: Option<usize>,
24 inner: Arc<(R, Mutex<HashMap<usize, &'static Location<'static>>>)>,
25}
26
27impl<R> ResourceHandle<R> {
28 pub fn new(res: R) -> Self {
30 Self {
31 id: None,
32 inner: Arc::new((res, Mutex::new(HashMap::new()))),
33 }
34 }
35
36 pub fn into_weak(self) -> WeakHandle<R> {
38 let inner = Arc::downgrade(&self.inner);
39 drop(self);
40 WeakHandle { inner }
41 }
42
43 pub fn try_unwrap(self) -> Option<R>
45 where
46 R: Any,
47 {
48 let inner = self.inner.clone();
49 drop(self);
50 match Arc::try_unwrap(inner) {
51 Ok((res, _)) => Some(res),
52 Err(inner) => {
53 let usages = inner
54 .1
55 .lock()
56 .unwrap()
57 .values()
58 .fold(String::new(), |s, loc| format!("{}\n- {}", s, loc));
59 warn!(
60 "Attempted to gain ownership resource `{}` but it is still being used. This is not, by itself, a \
61 problem but may indicate that a node task or event listener is not being stopped at the \
62 appropriate time during the shutdown process. Using arcane magic, we determined that the resource \
63 is still being used in the following places: {}",
64 type_name::<R>(),
65 usages,
66 );
67 None
68 }
69 }
70 }
71}
72
73impl<R> Clone for ResourceHandle<R> {
74 #[track_caller]
75 fn clone(&self) -> Self {
76 let new_id = RESOURCE_ID.fetch_add(1, Ordering::Relaxed);
77 self.inner.1.lock().unwrap().insert(new_id, Location::caller());
78 Self {
79 id: Some(new_id),
80 inner: self.inner.clone(),
81 }
82 }
83}
84
85impl<R> Deref for ResourceHandle<R> {
86 type Target = R;
87
88 fn deref(&self) -> &Self::Target {
89 &self.inner.0
90 }
91}
92
93impl<R> Drop for ResourceHandle<R> {
94 fn drop(&mut self) {
95 if let Some(id) = self.id {
96 self.inner.1.lock().unwrap().remove(&id);
97 }
98 }
99}
100
101pub struct WeakHandle<R> {
103 inner: Weak<(R, Mutex<HashMap<usize, &'static Location<'static>>>)>,
104}
105
106impl<R> WeakHandle<R> {
107 #[track_caller]
109 pub fn upgrade(&self) -> Option<ResourceHandle<R>> {
110 let new_id = RESOURCE_ID.fetch_add(1, Ordering::Relaxed);
111 let inner = self.inner.upgrade()?;
112 inner.1.lock().unwrap().insert(new_id, Location::caller());
113 Some(ResourceHandle {
114 id: Some(new_id),
115 inner,
116 })
117 }
118}
119
120impl<R> Clone for WeakHandle<R> {
121 fn clone(&self) -> Self {
122 Self {
123 inner: self.inner.clone(),
124 }
125 }
126}