1use fuchsia_sync::{Condvar, Mutex};
6use std::cell::UnsafeCell;
7use std::collections::hash_map::Entry;
8use std::collections::{HashMap, VecDeque};
9use std::marker::PhantomData;
10use std::mem::ManuallyDrop;
11use std::ops::Deref;
12use std::sync::{Arc, OnceLock, Weak};
13
14#[cfg(not(target_os = "fuchsia"))]
15use fuchsia_async::emulated_handle::zx_handle_t;
16#[cfg(target_os = "fuchsia")]
17use zx::sys::zx_handle_t;
18
19pub struct TempClonable<T: fidl::AsHandleRef>(ManuallyDrop<T>);
26
27impl<T: fidl::AsHandleRef> TempClonable<T> {
28 pub fn new(handle: T) -> Self {
30 Self(ManuallyDrop::new(handle))
31 }
32}
33
34impl<T: fidl::AsHandleRef> Deref for TempClonable<T> {
35 type Target = T;
36
37 fn deref(&self) -> &T {
38 &self.0
39 }
40}
41
42impl<T: fidl::AsHandleRef> TempClonable<T> {
43 pub fn temp_clone(&self) -> TempClone<T> {
49 assert!(!self.as_handle_ref().is_invalid());
50 let mut clones = clones().lock();
51 let raw_handle = self.0.as_handle_ref().raw_handle();
52 TempClone {
53 handle: match clones.entry(raw_handle) {
54 Entry::Occupied(mut o) => {
55 if let Some(clone) = o.get().upgrade() {
56 clone
57 } else {
58 let clone =
62 Arc::new(TempHandle { raw_handle, tombstone: UnsafeCell::new(false) });
63 *o.get_mut() = Arc::downgrade(&clone);
64 clone
65 }
66 }
67 Entry::Vacant(v) => {
68 let clone =
69 Arc::new(TempHandle { raw_handle, tombstone: UnsafeCell::new(false) });
70 v.insert(Arc::downgrade(&clone));
71 clone
72 }
73 },
74 marker: PhantomData,
75 }
76 }
77}
78
79impl<T: fidl::AsHandleRef> Drop for TempClonable<T> {
80 fn drop(&mut self) {
81 if let Some(handle) =
82 clones().lock().remove(&self.0.as_handle_ref().raw_handle()).and_then(|c| c.upgrade())
83 {
84 unsafe { *handle.tombstone.get() = true };
91 return;
92 }
93
94 unsafe { ManuallyDrop::drop(&mut self.0) }
97 }
98}
99
100type Clones = Mutex<HashMap<zx_handle_t, Weak<TempHandle>>>;
101
102fn clones() -> &'static Clones {
104 static CLONES: OnceLock<Clones> = OnceLock::new();
105 CLONES.get_or_init(|| Mutex::new(HashMap::new()))
106}
107
108pub struct TempClone<T> {
109 handle: Arc<TempHandle>,
110 marker: PhantomData<T>,
111}
112
113impl<T> Deref for TempClone<T> {
114 type Target = T;
115
116 fn deref(&self) -> &T {
117 unsafe { std::mem::transmute::<&zx_handle_t, &T>(&self.handle.raw_handle) }
119 }
120}
121
122struct TempHandle {
123 raw_handle: zx_handle_t,
124 tombstone: UnsafeCell<bool>,
125}
126
127unsafe impl Send for TempHandle {}
128unsafe impl Sync for TempHandle {}
129
130impl Drop for TempHandle {
131 fn drop(&mut self) {
132 if *self.tombstone.get_mut() {
133 unsafe { fidl::NullableHandle::from_raw(self.raw_handle) };
136 } else {
137 if let Entry::Occupied(o) = clones().lock().entry(self.raw_handle) {
138 if std::ptr::eq(o.get().as_ptr(), self) {
141 o.remove_entry();
142 }
143 }
144 }
145 }
146}
147
148pub async fn unblock<T: 'static + Send>(f: impl FnOnce() -> T + Send + 'static) -> T {
152 const NUM_THREADS: u8 = 2;
153
154 struct State {
155 queue: Mutex<VecDeque<Box<dyn FnOnce() + Send + 'static>>>,
156 cvar: Condvar,
157 }
158
159 static STATE: OnceLock<State> = OnceLock::new();
160
161 let mut start_threads = false;
162 let state = STATE.get_or_init(|| {
163 start_threads = true;
164 State { queue: Mutex::new(VecDeque::new()), cvar: Condvar::new() }
165 });
166
167 if start_threads {
168 for _ in 0..NUM_THREADS {
169 std::thread::spawn(|| {
170 loop {
171 let item = {
172 let mut queue = state.queue.lock();
173 loop {
174 if let Some(item) = queue.pop_front() {
175 break item;
176 }
177 state.cvar.wait(&mut queue);
178 }
179 };
180 item();
181 }
182 });
183 }
184 }
185
186 let (tx, rx) = futures::channel::oneshot::channel();
187 state.queue.lock().push_back(Box::new(move || {
188 let _ = tx.send(f());
189 }));
190 state.cvar.notify_one();
191
192 rx.await.unwrap()
193}
194
195#[cfg(target_os = "fuchsia")]
196#[cfg(test)]
197mod tests {
198 use super::{TempClonable, clones};
199
200 use std::sync::Arc;
201
202 #[test]
203 fn test_temp_clone() {
204 let parent_vmo = zx::Vmo::create(100).expect("create failed");
205
206 {
207 let temp_clone = {
208 let vmo = TempClonable::new(
209 parent_vmo
210 .create_child(zx::VmoChildOptions::REFERENCE, 0, 0)
211 .expect("create_child failed"),
212 );
213
214 vmo.write(b"foo", 0).expect("write failed");
215 {
216 let temp_clone2 = vmo.temp_clone();
218 assert_eq!(
219 &temp_clone2.read_to_vec::<u8>(0, 3).expect("read_to_vec failed"),
220 b"foo"
221 );
222 }
223
224 assert_eq!(&vmo.read_to_vec::<u8>(0, 3).expect("read_to_vec failed"), b"foo");
226
227 let vmo2 = TempClonable::new(
230 parent_vmo
231 .create_child(zx::VmoChildOptions::REFERENCE, 0, 0)
232 .expect("create_child failed"),
233 );
234 vmo2.temp_clone();
236
237 let _clone1 = vmo.temp_clone();
239
240 vmo.temp_clone()
242 };
243
244 assert_eq!(&temp_clone.read_to_vec::<u8>(0, 3).expect("read_to_vec failed"), b"foo");
247 }
248
249 parent_vmo
251 .wait_one(zx::Signals::VMO_ZERO_CHILDREN, zx::MonotonicInstant::INFINITE)
252 .expect("wait for zero children failed");
253 assert_eq!(parent_vmo.info().expect("info failed").num_children, 0);
254 assert!(clones().lock().is_empty());
255 }
256
257 #[test]
258 fn test_race() {
259 let parent_vmo = zx::Vmo::create(100).expect("create failed");
260
261 {
262 let vmo = Arc::new(TempClonable::new(
263 parent_vmo
264 .create_child(zx::VmoChildOptions::REFERENCE, 0, 0)
265 .expect("create_child failed"),
266 ));
267 vmo.write(b"foo", 0).expect("write failed");
268
269 let vmo_clone = vmo.clone();
270
271 let t1 = std::thread::spawn(move || {
272 for _ in 0..1000 {
273 assert_eq!(
274 &vmo.temp_clone().read_to_vec::<u8>(0, 3).expect("read_to_vec failed"),
275 b"foo"
276 );
277 }
278 });
279
280 let t2 = std::thread::spawn(move || {
281 for _ in 0..1000 {
282 assert_eq!(
283 &vmo_clone
284 .temp_clone()
285 .read_to_vec::<u8>(0, 3)
286 .expect("read_to_vec failed"),
287 b"foo"
288 );
289 }
290 });
291
292 let _ = t1.join();
293 let _ = t2.join();
294 }
295
296 parent_vmo
298 .wait_one(zx::Signals::VMO_ZERO_CHILDREN, zx::MonotonicInstant::INFINITE)
299 .expect("wait for zero children failed");
300 assert_eq!(parent_vmo.info().expect("info failed").num_children, 0);
301 assert!(clones().lock().is_empty());
302 }
303}