Skip to main content

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