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    Decode, DecodeError, Encodable, EncodableOption, Encode, EncodeError, EncodeOption, FromWire,
14    FromWireOption, Slot, 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 Decoded<'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(mut slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
163        munge!(let Self { encoded } = slot.as_mut());
164
165        match **encoded {
166            0 => (),
167            u32::MAX => {
168                let handle = decoder.take_raw_handle()?;
169                munge!(let Self { mut decoded } = slot);
170                decoded.write(handle);
171            }
172            e => return Err(DecodeError::InvalidHandlePresence(e)),
173        }
174        Ok(())
175    }
176}
177
178/// An optional Zircon handle.
179#[derive(Debug)]
180#[repr(transparent)]
181pub struct WireOptionalHandle {
182    pub(crate) handle: WireHandle,
183}
184
185unsafe impl Wire for WireOptionalHandle {
186    type Decoded<'de> = Self;
187
188    #[inline]
189    fn zero_padding(out: &mut MaybeUninit<Self>) {
190        munge!(let Self { handle } = out);
191        WireHandle::zero_padding(handle);
192    }
193}
194
195impl WireOptionalHandle {
196    /// Encodes a handle as present in a slot.
197    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
198        munge!(let Self { handle } = out);
199        WireHandle::set_encoded_present(handle);
200    }
201
202    /// Encodes a handle as absent in an output.
203    pub fn set_encoded_absent(out: &mut MaybeUninit<Self>) {
204        munge!(let Self { handle: WireHandle { encoded } } = out);
205        encoded.write(WireU32(0));
206    }
207
208    /// Returns whether a handle is present.
209    pub fn is_some(&self) -> bool {
210        !self.handle.is_invalid()
211    }
212
213    /// Returns whether a handle is absent.
214    pub fn is_none(&self) -> bool {
215        self.handle.is_invalid()
216    }
217
218    /// Returns the underlying [`zx_handle_t`], if any.
219    #[inline]
220    pub fn as_raw_handle(&self) -> Option<u32> {
221        self.is_some().then(|| self.handle.as_raw_handle())
222    }
223}
224
225unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for WireOptionalHandle {
226    fn decode(mut slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
227        munge!(let Self { handle } = slot.as_mut());
228        WireHandle::decode(handle, decoder)
229    }
230}
231
232impl Encodable for Handle {
233    type Encoded = WireHandle;
234}
235
236unsafe impl<E: HandleEncoder + ?Sized> Encode<E> for Handle {
237    fn encode(
238        self,
239        encoder: &mut E,
240        out: &mut MaybeUninit<Self::Encoded>,
241    ) -> Result<(), EncodeError> {
242        if self.client.upgrade().is_none() {
243            Err(EncodeError::InvalidRequiredHandle)
244        } else {
245            encoder.push_handle(self)?;
246            WireHandle::set_encoded_present(out);
247            Ok(())
248        }
249    }
250}
251
252impl FromWire<WireHandle> for Handle {
253    fn from_wire(mut wire: WireHandle) -> Self {
254        wire.take_handle()
255    }
256}
257
258impl EncodableOption for Handle {
259    type EncodedOption = WireOptionalHandle;
260}
261
262unsafe impl<E: HandleEncoder + ?Sized> EncodeOption<E> for Handle {
263    fn encode_option(
264        this: Option<Self>,
265        encoder: &mut E,
266        out: &mut MaybeUninit<Self::EncodedOption>,
267    ) -> Result<(), EncodeError> {
268        if let Some(handle) = this {
269            encoder.push_handle(handle)?;
270            WireOptionalHandle::set_encoded_present(out);
271        } else {
272            WireOptionalHandle::set_encoded_absent(out);
273        }
274        Ok(())
275    }
276}
277
278impl FromWireOption<WireOptionalHandle> for Handle {
279    fn from_wire_option(mut wire: WireOptionalHandle) -> Option<Self> {
280        if wire.handle.is_invalid() { None } else { Some(wire.handle.take_handle()) }
281    }
282}