fdomain_client/fidl_next/
wire_handle.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::cell::UnsafeCell;
6use std::fmt;
7use std::mem::MaybeUninit;
8use std::sync::RwLock;
9use std::sync::atomic::{AtomicPtr, Ordering};
10
11use super::codec::{HandleDecoder, HandleEncoder};
12use fidl_next_codec::{
13    Constrained, Decode, DecodeError, Encode, EncodeError, EncodeOption, FromWire, FromWireOption,
14    Slot, Unconstrained, Wire, WireU32, munge,
15};
16
17use crate::{Client, Handle};
18
19struct HandleAssoc {
20    hid: UnsafeCell<u32>,
21    client: AtomicPtr<Client>,
22}
23
24// SAFETY: We use the atomic pointer to synchronize access to the hid field.
25unsafe impl Send for HandleAssoc {}
26unsafe impl Sync for HandleAssoc {}
27
28const HANDLE_CLIENT_ASSOC_START_SIZE: usize = 32;
29static HANDLE_CLIENT_ASSOC: RwLock<&'static [HandleAssoc]> = RwLock::new(&[]);
30
31/// An FDomain handle.
32#[repr(C, align(4))]
33pub union WireHandle {
34    encoded: WireU32,
35    decoded: u32,
36}
37
38impl From<Handle> for WireHandle {
39    fn from(mut handle: Handle) -> WireHandle {
40        let id = handle.id;
41        let client = std::mem::replace(&mut handle.client, std::sync::Weak::new());
42        let ptr = client.into_raw() as *mut Client;
43
44        loop {
45            let table = HANDLE_CLIENT_ASSOC.read().unwrap();
46
47            for (got_id, entry) in table.iter().enumerate() {
48                let got_id: u32 = got_id.try_into().expect("Handle table overflowed u32");
49                if entry
50                    .client
51                    .compare_exchange(
52                        std::ptr::null_mut(),
53                        ptr,
54                        Ordering::Acquire,
55                        Ordering::Relaxed,
56                    )
57                    .is_ok()
58                {
59                    // SAFETY: If we were able to populate the client field then
60                    // we own this slot and it is ours to write.
61                    unsafe {
62                        *entry.hid.get() = id;
63                        return WireHandle { decoded: got_id + 1 };
64                    }
65                }
66            }
67
68            std::mem::drop(table);
69            let mut table = HANDLE_CLIENT_ASSOC.write().unwrap();
70            let new_len = std::cmp::max(table.len() * 2, HANDLE_CLIENT_ASSOC_START_SIZE);
71
72            let new = std::iter::repeat_with(|| HandleAssoc {
73                hid: UnsafeCell::new(0),
74                client: AtomicPtr::new(std::ptr::null_mut()),
75            })
76            .take(new_len)
77            .collect::<Box<[_]>>();
78
79            let new = Box::leak(new);
80            let old = std::mem::replace(&mut *table, new);
81
82            if old.len() > 0 {
83                // SAFETY: If this isn't the zero-length starting slice then it was
84                // leaked just above in a previous call/iteration.
85                unsafe { drop(Box::from_raw(old as *const [HandleAssoc] as *mut [HandleAssoc])) }
86            }
87        }
88    }
89}
90
91impl Drop for WireHandle {
92    fn drop(&mut self) {
93        drop(self.take_handle());
94    }
95}
96
97unsafe impl Wire for WireHandle {
98    type Owned<'de> = Self;
99
100    #[inline]
101    fn zero_padding(_: &mut MaybeUninit<Self>) {
102        // Wire handles have no padding
103    }
104}
105
106impl WireHandle {
107    /// Encodes a handle as present in an output.
108    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
109        munge!(let Self { encoded } = out);
110        encoded.write(WireU32(u32::MAX));
111    }
112
113    /// Returns whether the underlying u32 is invalid.
114    pub fn is_invalid(&self) -> bool {
115        self.as_raw_handle() == 0
116    }
117
118    pub fn invalidate(&mut self) {
119        self.decoded = 0;
120    }
121
122    /// Returns the underlying `u1`.
123    #[inline]
124    pub fn as_raw_handle(&self) -> u32 {
125        unsafe { self.decoded }
126    }
127
128    /// Takes the raw handle out of the handle table.
129    pub(crate) fn take_handle(&mut self) -> Handle {
130        // SAFETY: `WireHandle` is always a valid index into the association table,
131        // and the handle value is always in the association table.
132        unsafe {
133            let pos = self.decoded as usize;
134            self.decoded = 0;
135            let Some(pos) = pos.checked_sub(1) else {
136                return Handle::invalid();
137            };
138            let (id, ptr) = {
139                let table = HANDLE_CLIENT_ASSOC.read().unwrap();
140                let entry = &table[pos];
141                // We have to read the hid first as when we swap out the client
142                // that is when we mark the slot free.
143                let hid = *entry.hid.get();
144                let ptr = entry.client.swap(std::ptr::null_mut(), Ordering::Release);
145                (hid, ptr)
146            };
147
148            let client = std::sync::Weak::from_raw(ptr);
149
150            Handle { id, client }
151        }
152    }
153}
154
155impl fmt::Debug for WireHandle {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        self.as_raw_handle().fmt(f)
158    }
159}
160
161unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for WireHandle {
162    fn decode(
163        mut slot: Slot<'_, Self>,
164        decoder: &mut D,
165        _: <Self as Constrained>::Constraint,
166    ) -> Result<(), DecodeError> {
167        munge!(let Self { encoded } = slot.as_mut());
168
169        match **encoded {
170            0 => (),
171            u32::MAX => {
172                let handle = decoder.take_raw_handle()?;
173                munge!(let Self { mut decoded } = slot);
174                decoded.write(handle);
175            }
176            e => return Err(DecodeError::InvalidHandlePresence(e)),
177        }
178        Ok(())
179    }
180}
181
182impl Unconstrained for WireHandle {}
183
184/// An optional Zircon handle.
185#[derive(Debug)]
186#[repr(transparent)]
187pub struct WireOptionalHandle {
188    pub(crate) handle: WireHandle,
189}
190
191unsafe impl Wire for WireOptionalHandle {
192    type Owned<'de> = Self;
193
194    #[inline]
195    fn zero_padding(out: &mut MaybeUninit<Self>) {
196        munge!(let Self { handle } = out);
197        WireHandle::zero_padding(handle);
198    }
199}
200
201impl WireOptionalHandle {
202    /// Encodes a handle as present in a slot.
203    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
204        munge!(let Self { handle } = out);
205        WireHandle::set_encoded_present(handle);
206    }
207
208    /// Encodes a handle as absent in an output.
209    pub fn set_encoded_absent(out: &mut MaybeUninit<Self>) {
210        munge!(let Self { handle: WireHandle { encoded } } = out);
211        encoded.write(WireU32(0));
212    }
213
214    /// Returns whether a handle is present.
215    pub fn is_some(&self) -> bool {
216        !self.handle.is_invalid()
217    }
218
219    /// Returns whether a handle is absent.
220    pub fn is_none(&self) -> bool {
221        self.handle.is_invalid()
222    }
223
224    /// Returns the underlying [`zx_handle_t`], if any.
225    #[inline]
226    pub fn as_raw_handle(&self) -> Option<u32> {
227        self.is_some().then(|| self.handle.as_raw_handle())
228    }
229}
230
231unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for WireOptionalHandle {
232    fn decode(
233        mut slot: Slot<'_, Self>,
234        decoder: &mut D,
235        constraint: <Self as Constrained>::Constraint,
236    ) -> Result<(), DecodeError> {
237        munge!(let Self { handle } = slot.as_mut());
238        WireHandle::decode(handle, decoder, constraint)
239    }
240}
241
242impl Unconstrained for WireOptionalHandle {}
243
244unsafe impl<E: HandleEncoder + ?Sized> Encode<WireHandle, E> for Handle {
245    fn encode(
246        self,
247        encoder: &mut E,
248        out: &mut MaybeUninit<WireHandle>,
249        _: (),
250    ) -> Result<(), EncodeError> {
251        if self.client.upgrade().is_none() {
252            Err(EncodeError::InvalidRequiredHandle)
253        } else {
254            encoder.push_handle(self)?;
255            WireHandle::set_encoded_present(out);
256            Ok(())
257        }
258    }
259}
260
261impl FromWire<WireHandle> for Handle {
262    fn from_wire(mut wire: WireHandle) -> Self {
263        wire.take_handle()
264    }
265}
266
267unsafe impl<E: HandleEncoder + ?Sized> EncodeOption<WireOptionalHandle, E> for Handle {
268    fn encode_option(
269        this: Option<Self>,
270        encoder: &mut E,
271        out: &mut MaybeUninit<WireOptionalHandle>,
272        _: (),
273    ) -> Result<(), EncodeError> {
274        if let Some(handle) = this {
275            encoder.push_handle(handle)?;
276            WireOptionalHandle::set_encoded_present(out);
277        } else {
278            WireOptionalHandle::set_encoded_absent(out);
279        }
280        Ok(())
281    }
282}
283
284impl FromWireOption<WireOptionalHandle> for Handle {
285    fn from_wire_option(mut wire: WireOptionalHandle) -> Option<Self> {
286        if wire.handle.is_invalid() { None } else { Some(wire.handle.take_handle()) }
287    }
288}