zx/
handle.rs

1// Copyright 2018 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
5//! Type-safe bindings for Zircon handles.
6
7use crate::{
8    Koid, MonotonicInstant, Name, ObjectQuery, ObjectType, Port, Property, PropertyQuery, Rights,
9    Signals, Status, Topic, WaitAsyncOpts, ok, sys,
10};
11use std::marker::PhantomData;
12use std::mem::{ManuallyDrop, MaybeUninit};
13use std::num::NonZeroU32;
14use zerocopy::{FromBytes, Immutable, IntoBytes};
15
16/// Tuning constant for Handle::get_info_vec(). pub(crate) to support unit tests.
17pub(crate) const INFO_VEC_SIZE_INITIAL: usize = 16;
18
19/// An owned and valid Zircon
20/// [handle](https://fuchsia.dev/fuchsia-src/concepts/objects/handles) to a kernel object.
21///
22/// This type can be interconverted to and from more specific types. Those conversions are not
23/// enforced in the type system; attempting to use them will result in errors returned by the
24/// kernel. These conversions don't change the underlying representation, but do change the type and
25/// what operations are available.
26///
27/// # Lifecycle
28///
29/// This type closes the handle it owns when dropped.
30///
31/// # Layout
32///
33/// `Option<Handle>` is guaranteed to have the same layout and bit patterns as `zx_handle_t`.
34/// Unlike many types in this crate it does not implement `zerocopy` traits because those are not
35/// appropriate for types with real `Drop` implementations.
36#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
37#[repr(transparent)]
38pub struct Handle(NonZeroU32);
39
40// Ensure ABI-compatibility with zx_handle_t with the following static assertions. NonZeroU32 lets
41// Option store the None variant in the all-zeroes bit pattern, which is ZX_HANDLE_INVALID. By
42// banning use of ZX_HANDLE_INVALID inside a Handle, we ensure that Option<Handle> is ABI-compatible
43// with sys::zx_handle_t while providing statically checkable nullability.
44static_assertions::const_assert_eq!(sys::ZX_HANDLE_INVALID, 0);
45static_assertions::assert_eq_size!(Handle, sys::zx_handle_t);
46static_assertions::assert_eq_align!(Handle, sys::zx_handle_t);
47static_assertions::assert_eq_size!(Option<Handle>, sys::zx_handle_t);
48static_assertions::assert_eq_align!(Option<Handle>, sys::zx_handle_t);
49
50impl Handle {
51    /// Take exclusive ownership over a raw handle.
52    ///
53    /// # Safety
54    ///
55    /// `raw` must be a valid handle present in the current handle table that will not be closed by
56    /// another owner.
57    ///
58    /// # Panics
59    ///
60    /// If `ZX_HANDLE_INVALID` is passed.
61    pub unsafe fn from_raw(raw: sys::zx_handle_t) -> Self {
62        debug_assert!(Self::check_raw_valid(raw).is_ok());
63        Self(NonZeroU32::new(raw).unwrap())
64    }
65
66    /// Wraps the
67    /// [zx_handle_check_valid](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_check_valid.md)
68    /// syscall.
69    ///
70    /// Note that this does *not* guarantee that the handle is safe to pass to `Handle::from_raw`
71    /// in cases where another function may close the handle.
72    pub fn check_raw_valid(raw: sys::zx_handle_t) -> Result<(), Status> {
73        // SAFETY: basic FFI call.
74        ok(unsafe { sys::zx_handle_check_valid(raw) })
75    }
76
77    /// Returns the raw handle's integer value.
78    pub const fn raw_handle(&self) -> sys::zx_handle_t {
79        self.0.get()
80    }
81
82    /// Return the raw handle's integer value without closing it when `self` is dropped.
83    pub fn into_raw(self) -> sys::zx_handle_t {
84        let ret = self.0.get();
85        std::mem::forget(self);
86        ret
87    }
88
89    /// Wraps the
90    /// [`zx_handle_duplicate`](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_duplicate)
91    /// syscall.
92    pub fn duplicate(&self, rights: Rights) -> Result<Self, Status> {
93        let mut out = 0;
94        // SAFETY: basic FFI call.
95        let status =
96            unsafe { sys::zx_handle_duplicate(self.raw_handle(), rights.bits(), &mut out) };
97        ok(status)?;
98
99        // SAFETY: zx_handle_duplicate returns a valid handle that this function owns.
100        Ok(unsafe { Self::from_raw(out) })
101    }
102
103    /// Wraps the
104    /// [`zx_handle_replace`](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_replace)
105    /// syscall.
106    pub fn replace(self, rights: Rights) -> Result<Self, Status> {
107        let mut out = 0;
108
109        // SAFETY: basic FFI call.
110        let status = unsafe { sys::zx_handle_replace(self.into_raw(), rights.bits(), &mut out) };
111        ok(status)?;
112
113        // SAFETY: zx_handle_replace gives us a valid owned handle if the call succeeded.
114        unsafe { Ok(Self::from_raw(out)) }
115    }
116
117    /// Wraps the [`zx_object_signal`](https://fuchsia.dev/reference/syscalls/object_signal) syscall.
118    pub fn signal(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> {
119        // SAFETY: basic FFI call.
120        ok(unsafe { sys::zx_object_signal(self.raw_handle(), clear_mask.bits(), set_mask.bits()) })
121    }
122
123    /// Wraps the [`zx_object_wait_one`](https://fuchsia.dev/reference/syscalls/object_wait_one)
124    /// syscall.
125    pub fn wait_one(&self, signals: Signals, deadline: MonotonicInstant) -> WaitResult {
126        let mut pending = Signals::empty().bits();
127        // SAFETY: basic FFI call.
128        let status = unsafe {
129            sys::zx_object_wait_one(
130                self.raw_handle(),
131                signals.bits(),
132                deadline.into_nanos(),
133                &mut pending,
134            )
135        };
136        let signals = Signals::from_bits_truncate(pending);
137        match ok(status) {
138            Ok(()) => WaitResult::Ok(signals),
139            Err(Status::TIMED_OUT) => WaitResult::TimedOut(signals),
140            Err(Status::CANCELED) => WaitResult::Canceled(signals),
141            Err(e) => WaitResult::Err(e),
142        }
143    }
144
145    /// Wraps the [zx_object_wait_async](https://fuchsia.dev/reference/syscalls/object_wait_async)
146    /// syscall.
147    pub fn wait_async(
148        &self,
149        port: &Port,
150        key: u64,
151        signals: Signals,
152        options: WaitAsyncOpts,
153    ) -> Result<(), Status> {
154        // SAFETY: basic FFI call.
155        ok(unsafe {
156            sys::zx_object_wait_async(
157                self.raw_handle(),
158                port.raw_handle(),
159                key,
160                signals.bits(),
161                options.bits(),
162            )
163        })
164    }
165
166    /// Get the [Property::NAME] property for this object.
167    ///
168    /// Wraps a call to the
169    /// [zx_object_get_property](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_property.md)
170    /// syscall for the `ZX_PROP_NAME` property.
171    pub fn get_name(&self) -> Result<Name, Status> {
172        self.get_property::<NameProperty>()
173    }
174
175    /// Set the [Property::NAME] property for this object.
176    ///
177    /// The name's length must be less than [sys::ZX_MAX_NAME_LEN], i.e.
178    /// name.[to_bytes_with_nul()](CStr::to_bytes_with_nul()).len() <= [sys::ZX_MAX_NAME_LEN], or
179    /// Err([Status::INVALID_ARGS]) will be returned.
180    ///
181    /// Wraps a call to the
182    /// [`zx_object_get_property`](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_property.md)
183    /// syscall for the `ZX_PROP_NAME` property.
184    pub fn set_name(&self, name: &Name) -> Result<(), Status> {
185        self.set_property::<NameProperty>(&name)
186    }
187
188    /// Get a property on a zircon object
189    pub(crate) fn get_property<P: PropertyQuery>(&self) -> Result<P::PropTy, Status>
190    where
191        P::PropTy: FromBytes + Immutable,
192    {
193        let mut out = ::std::mem::MaybeUninit::<P::PropTy>::uninit();
194
195        // SAFETY: safe due to the contract on the P::PropTy type in the ObjectProperty trait.
196        let status = unsafe {
197            sys::zx_object_get_property(
198                self.raw_handle(),
199                *P::PROPERTY,
200                out.as_mut_ptr().cast::<u8>(),
201                std::mem::size_of::<P::PropTy>(),
202            )
203        };
204        Status::ok(status).map(|_| unsafe { out.assume_init() })
205    }
206
207    /// Set a property on a zircon object
208    pub(crate) fn set_property<P: PropertyQuery>(&self, val: &P::PropTy) -> Result<(), Status>
209    where
210        P::PropTy: IntoBytes + Immutable,
211    {
212        let status = unsafe {
213            sys::zx_object_set_property(
214                self.raw_handle(),
215                *P::PROPERTY,
216                std::ptr::from_ref(val).cast::<u8>(),
217                std::mem::size_of::<P::PropTy>(),
218            )
219        };
220        Status::ok(status)
221    }
222
223    /// Wraps the
224    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
225    /// syscall for the ZX_INFO_HANDLE_BASIC topic.
226    pub fn basic_info(&self) -> Result<HandleBasicInfo, Status> {
227        Ok(HandleBasicInfo::from(self.get_info_single::<HandleBasicInfoQuery>()?))
228    }
229
230    /// Wraps the
231    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
232    /// syscall for the ZX_INFO_HANDLE_COUNT topic.
233    pub fn count_info(&self) -> Result<HandleCountInfo, Status> {
234        Ok(HandleCountInfo::from(self.get_info_single::<HandleCountInfoQuery>()?))
235    }
236
237    /// Returns the koid (kernel object ID) for the object to which this handle refers.
238    pub fn koid(&self) -> Result<Koid, Status> {
239        self.basic_info().map(|info| info.koid)
240    }
241
242    /// Query information about a zircon object. Returns a valid slice and any remaining capacity on
243    /// success, along with a count of how many infos the kernel had available.
244    pub(crate) fn get_info<'a, Q: ObjectQuery>(
245        &self,
246        out: &'a mut [MaybeUninit<Q::InfoTy>],
247    ) -> Result<(&'a mut [Q::InfoTy], &'a mut [MaybeUninit<Q::InfoTy>], usize), Status>
248    where
249        Q::InfoTy: FromBytes + Immutable,
250    {
251        let mut actual = 0;
252        let mut avail = 0;
253
254        // SAFETY: The slice pointer is known valid to write to for `size_of_val` because it came
255        // from a mutable reference.
256        let status = unsafe {
257            sys::zx_object_get_info(
258                self.raw_handle(),
259                *Q::TOPIC,
260                out.as_mut_ptr().cast::<u8>(),
261                std::mem::size_of_val(out),
262                &mut actual,
263                &mut avail,
264            )
265        };
266        ok(status)?;
267
268        let (initialized, uninit) = out.split_at_mut(actual);
269
270        // TODO(https://fxbug.dev/352398385) switch to MaybeUninit::slice_assume_init_mut
271        // SAFETY: these values have been initialized by the kernel and implement the right zerocopy
272        // traits to be instantiated from arbitrary bytes.
273        let initialized: &mut [Q::InfoTy] = unsafe {
274            std::slice::from_raw_parts_mut(
275                initialized.as_mut_ptr().cast::<Q::InfoTy>(),
276                initialized.len(),
277            )
278        };
279
280        Ok((initialized, uninit, avail))
281    }
282
283    /// Query information about a zircon object, expecting only a single info in the return.
284    pub(crate) fn get_info_single<Q: ObjectQuery>(&self) -> Result<Q::InfoTy, Status>
285    where
286        Q::InfoTy: Copy + FromBytes + Immutable,
287    {
288        let mut info = MaybeUninit::<Q::InfoTy>::uninit();
289        let (info, _uninit, _avail) = self.get_info::<Q>(std::slice::from_mut(&mut info))?;
290        Ok(info[0])
291    }
292
293    /// Query multiple records of information about a zircon object.
294    /// Returns a vec of Q::InfoTy on success.
295    /// Intended for calls that return multiple small objects.
296    pub(crate) fn get_info_vec<Q: ObjectQuery>(&self) -> Result<Vec<Q::InfoTy>, Status> {
297        // Start with a few slots
298        let mut out = Vec::<Q::InfoTy>::with_capacity(INFO_VEC_SIZE_INITIAL);
299        loop {
300            let (init, _uninit, avail) = self.get_info::<Q>(out.spare_capacity_mut())?;
301            let num_initialized = init.len();
302            if num_initialized == avail {
303                // SAFETY: the kernel has initialized all of these values.
304                unsafe { out.set_len(num_initialized) };
305                return Ok(out);
306            } else {
307                if avail > out.capacity() {
308                    out.reserve_exact(avail - out.len());
309                }
310            }
311        }
312    }
313}
314
315impl Drop for Handle {
316    fn drop(&mut self) {
317        // SAFETY: basic FFI call.
318        unsafe { sys::zx_handle_close(self.0.get()) };
319    }
320}
321
322/// An object representing a Zircon
323/// [handle](https://fuchsia.dev/fuchsia-src/concepts/objects/handles).
324///
325/// Internally, it is represented as a 32-bit integer, but this wrapper enforces
326/// strict ownership semantics. The `Drop` implementation closes the handle.
327///
328/// This type represents the most general reference to a kernel object, and can
329/// be interconverted to and from more specific types. Those conversions are not
330/// enforced in the type system; attempting to use them will result in errors
331/// returned by the kernel. These conversions don't change the underlying
332/// representation, but do change the type and thus what operations are available.
333// TODO(https://fxbug.dev/465766514): remove
334#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
335#[repr(transparent)]
336pub struct NullableHandle(Option<Handle>);
337
338impl AsHandleRef for NullableHandle {
339    fn as_handle_ref(&self) -> HandleRef<'_> {
340        if let Some(inner) = &self.0 {
341            // SAFETY: inner is a guaranteed valid handle.
342            unsafe { Unowned::from_raw_handle(inner.raw_handle()) }
343        } else {
344            // SAFETY: ZX_HANDLE_INVALID is a valid handle for Unowned::from_raw_handle.
345            unsafe { Unowned::from_raw_handle(sys::ZX_HANDLE_INVALID) }
346        }
347    }
348}
349
350impl HandleBased for NullableHandle {
351    fn into_raw(self) -> sys::zx_handle_t {
352        if let Some(inner) = self.0 { inner.into_raw() } else { sys::ZX_HANDLE_INVALID }
353    }
354}
355
356impl NullableHandle {
357    /// Initialize a handle backed by ZX_HANDLE_INVALID, the only safe non-handle.
358    #[inline(always)]
359    pub const fn invalid() -> Self {
360        Self(None)
361    }
362
363    /// If a raw handle is obtained from some other source, this method converts
364    /// it into a type-safe owned handle.
365    ///
366    /// # Safety
367    ///
368    /// `raw` must either be a valid handle (i.e. not dangling), or
369    /// `ZX_HANDLE_INVALID`. If `raw` is a valid handle, then either:
370    /// - `raw` may be closed manually and the returned `NullableHandle` must not be
371    ///   dropped.
372    /// - Or `raw` must not be closed until the returned `NullableHandle` is dropped, at
373    ///   which time it will close `raw`.
374    pub const unsafe fn from_raw(raw: sys::zx_handle_t) -> Self {
375        // We need to manually construct the inner `Handle` because its constructor is not
376        // const since the only valid way to call this function in a `const` context is to pass
377        // `ZX_HANDLE_INVALID`.
378        if let Some(inner) = NonZeroU32::new(raw) { Self(Some(Handle(inner))) } else { Self(None) }
379    }
380
381    pub const fn raw_handle(&self) -> sys::zx_handle_t {
382        if let Some(inner) = &self.0 { inner.raw_handle() } else { sys::ZX_HANDLE_INVALID }
383    }
384
385    pub fn into_raw(self) -> sys::zx_handle_t {
386        self.0.map(Handle::into_raw).unwrap_or(sys::ZX_HANDLE_INVALID)
387    }
388
389    pub fn as_handle_ref(&self) -> HandleRef<'_> {
390        AsHandleRef::as_handle_ref(self)
391    }
392
393    pub const fn is_invalid(&self) -> bool {
394        self.0.is_none()
395    }
396
397    pub fn duplicate(&self, rights: Rights) -> Result<Self, Status> {
398        self.0
399            .as_ref()
400            .map(|h| h.duplicate(rights).map(|new| Self(Some(new))))
401            .unwrap_or(Err(Status::BAD_HANDLE))
402    }
403
404    pub fn replace(mut self, rights: Rights) -> Result<Self, Status> {
405        if let Some(inner) = self.0.take() {
406            let inner = inner.replace(rights)?;
407            Ok(Self(Some(inner)))
408        } else {
409            Ok(Self(None))
410        }
411    }
412
413    pub fn signal(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> {
414        self.0.as_ref().map(|h| h.signal(clear_mask, set_mask)).unwrap_or(Err(Status::BAD_HANDLE))
415    }
416
417    pub fn wait_one(&self, signals: Signals, deadline: MonotonicInstant) -> WaitResult {
418        self.0
419            .as_ref()
420            .map(|h| h.wait_one(signals, deadline))
421            .unwrap_or(WaitResult::Err(Status::BAD_HANDLE))
422    }
423
424    pub fn wait_async(
425        &self,
426        port: &Port,
427        key: u64,
428        signals: Signals,
429        options: WaitAsyncOpts,
430    ) -> Result<(), Status> {
431        self.0
432            .as_ref()
433            .map(|h| h.wait_async(port, key, signals, options))
434            .unwrap_or(Err(Status::BAD_HANDLE))
435    }
436
437    pub fn get_name(&self) -> Result<Name, Status> {
438        self.0.as_ref().map(Handle::get_name).unwrap_or(Err(Status::BAD_HANDLE))
439    }
440
441    pub fn set_name(&self, name: &Name) -> Result<(), Status> {
442        self.0.as_ref().map(|h| h.set_name(name)).unwrap_or(Err(Status::BAD_HANDLE))
443    }
444
445    pub fn basic_info(&self) -> Result<HandleBasicInfo, Status> {
446        self.0.as_ref().map(Handle::basic_info).unwrap_or(Err(Status::BAD_HANDLE))
447    }
448
449    pub fn count_info(&self) -> Result<HandleCountInfo, Status> {
450        self.0.as_ref().map(Handle::count_info).unwrap_or(Err(Status::BAD_HANDLE))
451    }
452
453    pub fn koid(&self) -> Result<Koid, Status> {
454        self.0.as_ref().map(Handle::koid).unwrap_or(Err(Status::BAD_HANDLE))
455    }
456
457    pub(crate) fn get_info<'a, Q: ObjectQuery>(
458        &self,
459        out: &'a mut [MaybeUninit<Q::InfoTy>],
460    ) -> Result<(&'a mut [Q::InfoTy], &'a mut [MaybeUninit<Q::InfoTy>], usize), Status>
461    where
462        Q::InfoTy: FromBytes + Immutable,
463    {
464        self.0.as_ref().map(|h| h.get_info::<Q>(out)).unwrap_or(Err(Status::BAD_HANDLE))
465    }
466
467    pub(crate) fn get_info_single<Q: ObjectQuery>(&self) -> Result<Q::InfoTy, Status>
468    where
469        Q::InfoTy: Copy + FromBytes + Immutable,
470    {
471        self.0.as_ref().map(|h| h.get_info_single::<Q>()).unwrap_or(Err(Status::BAD_HANDLE))
472    }
473
474    pub(crate) fn get_info_vec<Q: ObjectQuery>(&self) -> Result<Vec<Q::InfoTy>, Status> {
475        self.0.as_ref().map(|h| h.get_info_vec::<Q>()).unwrap_or(Err(Status::BAD_HANDLE))
476    }
477
478    pub(crate) fn get_property<P: PropertyQuery>(&self) -> Result<P::PropTy, Status>
479    where
480        P::PropTy: FromBytes + Immutable,
481    {
482        self.0.as_ref().map(|h| h.get_property::<P>()).unwrap_or(Err(Status::BAD_HANDLE))
483    }
484
485    pub(crate) fn set_property<P: PropertyQuery>(&self, val: &P::PropTy) -> Result<(), Status>
486    where
487        P::PropTy: IntoBytes + Immutable,
488    {
489        self.0.as_ref().map(|h| h.set_property::<P>(val)).unwrap_or(Err(Status::BAD_HANDLE))
490    }
491}
492
493struct NameProperty();
494unsafe impl PropertyQuery for NameProperty {
495    const PROPERTY: Property = Property::NAME;
496    // SAFETY: this type is correctly sized and the kernel guarantees that it will be
497    // null-terminated like the type requires.
498    type PropTy = Name;
499}
500
501/// A borrowed value of type `T`.
502///
503/// This is primarily used for working with borrowed values of `HandleBased` types.
504#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
505#[repr(transparent)]
506pub struct Unowned<'a, T> {
507    inner: ManuallyDrop<T>,
508    marker: PhantomData<&'a T>,
509}
510
511impl<'a, T: Into<NullableHandle>> ::std::ops::Deref for Unowned<'a, T> {
512    type Target = T;
513
514    fn deref(&self) -> &Self::Target {
515        &*self.inner
516    }
517}
518
519impl<'a, T: AsHandleRef> AsHandleRef for Unowned<'a, T> {
520    fn as_handle_ref(&self) -> HandleRef<'_> {
521        self.inner.as_handle_ref()
522    }
523}
524
525impl<T: HandleBased> Clone for Unowned<'_, T> {
526    fn clone(&self) -> Self {
527        unsafe { Self::from_raw_handle(self.inner.as_handle_ref().raw_handle()) }
528    }
529}
530
531pub type HandleRef<'a> = Unowned<'a, NullableHandle>;
532
533impl<'a, T: Into<NullableHandle>> Unowned<'a, T> {
534    /// Returns a new object that borrows the underyling handle.  This will work for any type that
535    /// implements `From<U>` where `U` is handle-like i.e. it implements `AsHandleRef` and
536    /// `From<Handle>`.
537    pub fn new<U: AsHandleRef + From<NullableHandle>>(inner: &'a U) -> Self
538    where
539        T: From<U>,
540    {
541        // SAFETY: This is safe because we are converting from &U to U to allow us to create T, and
542        // then when we drop, we convert T into a handle that we forget.
543        Unowned {
544            inner: ManuallyDrop::new(T::from(U::from(unsafe {
545                NullableHandle::from_raw(inner.as_handle_ref().raw_handle())
546            }))),
547            marker: PhantomData,
548        }
549    }
550}
551
552impl<'a, T: HandleBased> Unowned<'a, T> {
553    /// Create a `HandleRef` from a raw handle. Use this method when you are given a raw handle but
554    /// should not take ownership of it. Examples include process-global handles like the root
555    /// VMAR. This method should be called with an explicitly provided lifetime that must not
556    /// outlive the lifetime during which the handle is owned by the current process. It is unsafe
557    /// because most of the time, it is better to use a `Handle` to prevent leaking resources.
558    ///
559    /// # Safety
560    ///
561    /// `handle` must be a valid handle (i.e. not dangling), or
562    /// `ZX_HANDLE_INVALID`. If `handle` is a valid handle, then it must not be
563    /// closed for the lifetime `'a`.
564    pub unsafe fn from_raw_handle(handle: sys::zx_handle_t) -> Self {
565        Unowned {
566            inner: ManuallyDrop::new(T::from(unsafe { NullableHandle::from_raw(handle) })),
567            marker: PhantomData,
568        }
569    }
570
571    /// Returns the raw handle's integer value.
572    pub fn raw_handle(&self) -> sys::zx_handle_t {
573        NullableHandle::raw_handle(&*self.inner.as_handle_ref())
574    }
575}
576
577/// Result from `HandleRef::wait` and `AsHandleRef::wait_handle`. Conveys the
578/// result of the
579/// [zx_object_wait_one](https://fuchsia.dev/reference/syscalls/object_wait_one)
580/// syscall and the signals that were asserted on the object when the syscall
581/// completed.
582#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
583pub enum WaitResult {
584    /// The syscall completed with `ZX_OK` and the provided signals were observed.
585    Ok(Signals),
586
587    /// The syscall completed with `ZX_ERR_TIMED_OUT` and the provided signals
588    /// were observed. These signals may reflect state changes that occurred
589    /// after the deadline passed, but before the syscall returned.
590    TimedOut(Signals),
591
592    /// The syscall completed with `ZX_ERR_CANCELED` and the provided signals
593    /// were observed. The signals will include `ZX_SIGNAL_HANDLE_CLOSED`.
594    ///
595    /// Note that the state of these signals may be racy and difficult to
596    /// interpret. Often, the correct behavior in this case is to treat this as
597    /// an error.
598    Canceled(Signals),
599
600    /// The syscall completed with a status other than `ZX_OK`, `ZX_ERR_TIMED_OUT`,
601    /// or `ZX_ERR_CANCELED`. No signals are returned in this scenario.
602    Err(Status),
603}
604
605impl WaitResult {
606    /// Convert this `WaitResult` into a `Result<Signals, Status>`. The signals
607    /// are discarded in all cases except `WaitResult::Ok`.
608    pub const fn to_result(self) -> Result<Signals, Status> {
609        match self {
610            WaitResult::Ok(signals) => Ok(signals),
611            WaitResult::TimedOut(_signals) => Err(Status::TIMED_OUT),
612            WaitResult::Canceled(_signals) => Err(Status::CANCELED),
613            WaitResult::Err(status) => Err(status),
614        }
615    }
616
617    // The following definitions are all copied from `std::result::Result`. They
618    // allow a `WaitResult` to be treated like a `Result` in many circumstance. All
619    // simply delegate to `to_result()`.
620
621    #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"]
622    #[inline]
623    pub const fn is_ok(&self) -> bool {
624        self.to_result().is_ok()
625    }
626
627    #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"]
628    #[inline]
629    pub const fn is_err(&self) -> bool {
630        self.to_result().is_err()
631    }
632
633    #[inline]
634    pub fn map<U, F: FnOnce(Signals) -> U>(self, op: F) -> Result<U, Status> {
635        self.to_result().map(op)
636    }
637
638    #[inline]
639    pub fn map_err<F, O: FnOnce(Status) -> F>(self, op: O) -> Result<Signals, F> {
640        self.to_result().map_err(op)
641    }
642
643    #[inline]
644    #[track_caller]
645    pub fn expect(self, msg: &str) -> Signals {
646        self.to_result().expect(msg)
647    }
648
649    #[inline]
650    #[track_caller]
651    pub fn expect_err(self, msg: &str) -> Status {
652        self.to_result().expect_err(msg)
653    }
654
655    #[inline(always)]
656    #[track_caller]
657    pub fn unwrap(self) -> Signals {
658        self.to_result().unwrap()
659    }
660}
661
662impl<'a> Unowned<'a, NullableHandle> {
663    /// Convert this HandleRef to one of a specific type.
664    pub fn cast<T: HandleBased>(self) -> Unowned<'a, T> {
665        // SAFETY: this function's guarantees are upheld by the self input.
666        unsafe { Unowned::from_raw_handle(self.raw_handle()) }
667    }
668}
669
670/// A trait to get a reference to the underlying handle of an object.
671pub trait AsHandleRef {
672    /// Get a reference to the handle. One important use of such a reference is
673    /// for `object_wait_many`.
674    fn as_handle_ref(&self) -> HandleRef<'_>;
675}
676
677impl<T: AsHandleRef> AsHandleRef for &T {
678    fn as_handle_ref(&self) -> HandleRef<'_> {
679        (*self).as_handle_ref()
680    }
681}
682
683/// A trait implemented by all handle-based types.
684///
685/// Note: it is reasonable for user-defined objects wrapping a handle to implement
686/// this trait. For example, a specific interface in some protocol might be
687/// represented as a newtype of `Channel`, and implement the `as_handle_ref`
688/// method and the `From<Handle>` trait to facilitate conversion from and to the
689/// interface.
690pub trait HandleBased: AsHandleRef + From<NullableHandle> + Into<NullableHandle> {
691    /// Duplicate a handle, possibly reducing the rights available. Wraps the
692    /// [zx_handle_duplicate](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_duplicate.md)
693    /// syscall.
694    fn duplicate_handle(&self, rights: Rights) -> Result<Self, Status> {
695        self.as_handle_ref().duplicate(rights).map(|handle| Self::from(handle))
696    }
697
698    /// Create a replacement for a handle, possibly reducing the rights available. This invalidates
699    /// the original handle. Wraps the
700    /// [zx_handle_replace](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_replace.md)
701    /// syscall.
702    fn replace_handle(self, rights: Rights) -> Result<Self, Status> {
703        <Self as Into<NullableHandle>>::into(self).replace(rights).map(|handle| Self::from(handle))
704    }
705
706    /// Converts the value into its inner handle.
707    ///
708    /// This is a convenience function which simply forwards to the `Into` trait.
709    fn into_handle(self) -> NullableHandle {
710        self.into()
711    }
712
713    /// Converts the handle into it's raw representation.
714    ///
715    /// The caller takes ownership over the raw handle, and must close or transfer it to avoid a handle leak.
716    fn into_raw(self) -> sys::zx_handle_t {
717        self.into_handle().into_raw()
718    }
719
720    /// Creates an instance of this type from a handle.
721    ///
722    /// This is a convenience function which simply forwards to the `From` trait.
723    fn from_handle(handle: NullableHandle) -> Self {
724        Self::from(handle)
725    }
726
727    fn is_invalid_handle(&self) -> bool {
728        self.as_handle_ref().is_invalid()
729    }
730}
731
732/// A trait implemented by all handles for objects which have a peer.
733pub trait Peered: HandleBased {
734    /// Set and clear userspace-accessible signal bits on the object's peer. Wraps the
735    /// [zx_object_signal_peer][osp] syscall.
736    ///
737    /// [osp]: https://fuchsia.dev/fuchsia-src/reference/syscalls/object_signal_peer.md
738    fn signal_peer(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> {
739        let handle = self.as_handle_ref().raw_handle();
740        let status =
741            unsafe { sys::zx_object_signal_peer(handle, clear_mask.bits(), set_mask.bits()) };
742        ok(status)
743    }
744
745    /// Returns true if the handle has received the `PEER_CLOSED` signal.
746    ///
747    /// # Errors
748    ///
749    /// See https://fuchsia.dev/reference/syscalls/object_wait_one?hl=en#errors for a full list of
750    /// errors. Note that `Status::TIMED_OUT` errors are converted to `Ok(false)` and all other
751    /// errors are propagated.
752    fn is_closed(&self) -> Result<bool, Status> {
753        match self
754            .as_handle_ref()
755            .wait_one(Signals::OBJECT_PEER_CLOSED, MonotonicInstant::INFINITE_PAST)
756        {
757            WaitResult::Ok(signals) => Ok(signals.contains(Signals::OBJECT_PEER_CLOSED)),
758            WaitResult::TimedOut(_) => Ok(false),
759            WaitResult::Canceled(_) => Err(Status::CANCELED),
760            WaitResult::Err(e) => Err(e),
761        }
762    }
763}
764
765/// Basic information about a handle.
766///
767/// Wrapper for data returned from [Handle::basic_info()].
768#[derive(Debug, Copy, Clone, Eq, PartialEq)]
769pub struct HandleBasicInfo {
770    pub koid: Koid,
771    pub rights: Rights,
772    pub object_type: ObjectType,
773    pub related_koid: Koid,
774}
775
776impl Default for HandleBasicInfo {
777    fn default() -> Self {
778        Self::from(sys::zx_info_handle_basic_t::default())
779    }
780}
781
782impl From<sys::zx_info_handle_basic_t> for HandleBasicInfo {
783    fn from(info: sys::zx_info_handle_basic_t) -> Self {
784        let sys::zx_info_handle_basic_t { koid, rights, type_, related_koid, .. } = info;
785
786        // Note lossy conversion of Rights and HandleProperty here if either of those types are out
787        // of date or incomplete.
788        HandleBasicInfo {
789            koid: Koid::from_raw(koid),
790            rights: Rights::from_bits_truncate(rights),
791            object_type: ObjectType::from_raw(type_),
792            related_koid: Koid::from_raw(related_koid),
793        }
794    }
795}
796
797// zx_info_handle_basic_t is able to be safely replaced with a byte representation and is a PoD
798// type.
799struct HandleBasicInfoQuery;
800unsafe impl ObjectQuery for HandleBasicInfoQuery {
801    const TOPIC: Topic = Topic::HANDLE_BASIC;
802    type InfoTy = sys::zx_info_handle_basic_t;
803}
804
805sys::zx_info_handle_count_t!(HandleCountInfo);
806
807impl From<sys::zx_info_handle_count_t> for HandleCountInfo {
808    fn from(sys::zx_info_handle_count_t { handle_count }: sys::zx_info_handle_count_t) -> Self {
809        HandleCountInfo { handle_count }
810    }
811}
812
813// zx_info_handle_count_t is able to be safely replaced with a byte representation and is a PoD
814// type.
815struct HandleCountInfoQuery;
816unsafe impl ObjectQuery for HandleCountInfoQuery {
817    const TOPIC: Topic = Topic::HANDLE_COUNT;
818    type InfoTy = sys::zx_info_handle_count_t;
819}
820
821/// Handle operation.
822#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
823pub enum HandleOp<'a> {
824    Move(NullableHandle),
825    Duplicate(HandleRef<'a>),
826}
827
828/// Operation to perform on handles during write. ABI-compatible with `zx_handle_disposition_t`.
829#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
830#[repr(C)]
831pub struct HandleDisposition<'a> {
832    // Must be either ZX_HANDLE_OP_MOVE or ZX_HANDLE_OP_DUPLICATE.
833    operation: sys::zx_handle_op_t,
834    // ZX_HANDLE_OP_MOVE==owned, ZX_HANDLE_OP_DUPLICATE==borrowed.
835    handle: sys::zx_handle_t,
836    // Preserve a borrowed handle's lifetime. Does not occupy any layout.
837    _handle_lifetime: std::marker::PhantomData<&'a ()>,
838
839    pub object_type: ObjectType,
840    pub rights: Rights,
841    pub result: Status,
842}
843
844static_assertions::assert_eq_size!(HandleDisposition<'_>, sys::zx_handle_disposition_t);
845static_assertions::const_assert_eq!(
846    std::mem::offset_of!(HandleDisposition<'_>, operation),
847    std::mem::offset_of!(sys::zx_handle_disposition_t, operation)
848);
849static_assertions::const_assert_eq!(
850    std::mem::offset_of!(HandleDisposition<'_>, handle),
851    std::mem::offset_of!(sys::zx_handle_disposition_t, handle)
852);
853static_assertions::const_assert_eq!(
854    std::mem::offset_of!(HandleDisposition<'_>, object_type),
855    std::mem::offset_of!(sys::zx_handle_disposition_t, type_)
856);
857static_assertions::const_assert_eq!(
858    std::mem::offset_of!(HandleDisposition<'_>, rights),
859    std::mem::offset_of!(sys::zx_handle_disposition_t, rights)
860);
861static_assertions::const_assert_eq!(
862    std::mem::offset_of!(HandleDisposition<'_>, result),
863    std::mem::offset_of!(sys::zx_handle_disposition_t, result)
864);
865
866impl<'a> HandleDisposition<'a> {
867    #[inline]
868    pub fn new(
869        handle_op: HandleOp<'a>,
870        object_type: ObjectType,
871        rights: Rights,
872        status: Status,
873    ) -> Self {
874        let (operation, handle) = match handle_op {
875            HandleOp::Move(h) => (sys::ZX_HANDLE_OP_MOVE, h.into_raw()),
876            HandleOp::Duplicate(h) => (sys::ZX_HANDLE_OP_DUPLICATE, h.raw_handle()),
877        };
878
879        Self {
880            operation,
881            handle,
882            _handle_lifetime: std::marker::PhantomData,
883            object_type,
884            rights: rights,
885            result: status,
886        }
887    }
888
889    pub fn raw_handle(&self) -> sys::zx_handle_t {
890        self.handle
891    }
892
893    pub fn is_move(&self) -> bool {
894        self.operation == sys::ZX_HANDLE_OP_MOVE
895    }
896
897    pub fn is_duplicate(&self) -> bool {
898        self.operation == sys::ZX_HANDLE_OP_DUPLICATE
899    }
900
901    pub fn take_op(&mut self) -> HandleOp<'a> {
902        match self.operation {
903            sys::ZX_HANDLE_OP_MOVE => {
904                // SAFETY: this is guaranteed to be a valid handle number by a combination of this
905                // type's public API and the kernel's guarantees.
906                HandleOp::Move(unsafe {
907                    NullableHandle::from_raw(std::mem::replace(
908                        &mut self.handle,
909                        sys::ZX_HANDLE_INVALID,
910                    ))
911                })
912            }
913            sys::ZX_HANDLE_OP_DUPLICATE => {
914                // SAFETY: this is guaranteed to be a valid handle number by a combination of this
915                // type's public API and the kernel's guarantees.
916                HandleOp::Duplicate(Unowned {
917                    inner: ManuallyDrop::new(unsafe { NullableHandle::from_raw(self.handle) }),
918                    marker: PhantomData,
919                })
920            }
921            _ => unreachable!(),
922        }
923    }
924
925    pub fn into_raw(mut self) -> sys::zx_handle_disposition_t {
926        match self.take_op() {
927            HandleOp::Move(mut handle) => sys::zx_handle_disposition_t {
928                operation: sys::ZX_HANDLE_OP_MOVE,
929                handle: std::mem::replace(&mut handle, NullableHandle::invalid()).into_raw(),
930                type_: self.object_type.into_raw(),
931                rights: self.rights.bits(),
932                result: self.result.into_raw(),
933            },
934            HandleOp::Duplicate(handle_ref) => sys::zx_handle_disposition_t {
935                operation: sys::ZX_HANDLE_OP_DUPLICATE,
936                handle: handle_ref.raw_handle(),
937                type_: self.object_type.into_raw(),
938                rights: self.rights.bits(),
939                result: self.result.into_raw(),
940            },
941        }
942    }
943}
944
945impl<'a> Drop for HandleDisposition<'a> {
946    fn drop(&mut self) {
947        // Ensure we clean up owned handle variants.
948        if self.operation == sys::ZX_HANDLE_OP_MOVE {
949            unsafe { drop(NullableHandle::from_raw(self.handle)) };
950        }
951    }
952}
953
954/// Information on handles that were read.
955///
956/// ABI-compatible with zx_handle_info_t.
957#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
958#[repr(C)]
959pub struct HandleInfo {
960    pub handle: NullableHandle,
961    pub object_type: ObjectType,
962    pub rights: Rights,
963
964    // Necessary for ABI compatibility with zx_handle_info_t.
965    pub(crate) _unused: u32,
966}
967
968static_assertions::assert_eq_size!(HandleInfo, sys::zx_handle_info_t);
969static_assertions::const_assert_eq!(
970    std::mem::offset_of!(HandleInfo, handle),
971    std::mem::offset_of!(sys::zx_handle_info_t, handle)
972);
973static_assertions::const_assert_eq!(
974    std::mem::offset_of!(HandleInfo, object_type),
975    std::mem::offset_of!(sys::zx_handle_info_t, ty)
976);
977static_assertions::const_assert_eq!(
978    std::mem::offset_of!(HandleInfo, rights),
979    std::mem::offset_of!(sys::zx_handle_info_t, rights)
980);
981static_assertions::const_assert_eq!(
982    std::mem::offset_of!(HandleInfo, _unused),
983    std::mem::offset_of!(sys::zx_handle_info_t, unused)
984);
985
986impl HandleInfo {
987    /// Make a new `HandleInfo`.
988    pub const fn new(handle: NullableHandle, object_type: ObjectType, rights: Rights) -> Self {
989        Self { handle, object_type, rights, _unused: 0 }
990    }
991
992    /// # Safety
993    ///
994    /// See [`Handle::from_raw`] for requirements about the validity and closing
995    /// of `raw.handle`.
996    ///
997    /// Note that while `raw.ty` _should_ correspond to the type of the handle,
998    /// that this is not required for safety.
999    pub const unsafe fn from_raw(raw: sys::zx_handle_info_t) -> HandleInfo {
1000        HandleInfo::new(
1001            // SAFETY: invariants to not double-close are upheld by the caller.
1002            unsafe { NullableHandle::from_raw(raw.handle) },
1003            ObjectType::from_raw(raw.ty),
1004            Rights::from_bits_retain(raw.rights),
1005        )
1006    }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011    use super::*;
1012    // The unit tests are built with a different crate name, but fuchsia_runtime returns a "real"
1013    // zx::Vmar that we need to use.
1014    use zx::{
1015        Channel, HandleBased, HandleDisposition, HandleInfo, HandleOp, Name, NullableHandle,
1016        ObjectType, Rights, Vmo,
1017    };
1018    use zx_sys as sys;
1019
1020    #[test]
1021    fn into_raw() {
1022        let vmo = Vmo::create(1).unwrap();
1023        let h = vmo.into_raw();
1024        let vmo2 = Vmo::from(unsafe { NullableHandle::from_raw(h) });
1025        assert!(vmo2.write(b"1", 0).is_ok());
1026    }
1027
1028    #[test]
1029    fn check_raw_valid() {
1030        assert!(Handle::check_raw_valid(sys::ZX_HANDLE_INVALID).is_err());
1031        let vmo = Vmo::create(1).unwrap();
1032        let vmo_raw = vmo.raw_handle();
1033        assert!(Handle::check_raw_valid(vmo_raw).is_ok());
1034        drop(vmo);
1035        assert!(Handle::check_raw_valid(vmo_raw).is_err());
1036    }
1037
1038    /// Test duplication by means of a VMO
1039    #[test]
1040    fn duplicate() {
1041        let hello_length: usize = 5;
1042
1043        // Create a VMO and write some data to it.
1044        let vmo = Vmo::create(hello_length as u64).unwrap();
1045        assert!(vmo.write(b"hello", 0).is_ok());
1046
1047        // Replace, reducing rights to read.
1048        let readonly_vmo = vmo.duplicate_handle(Rights::READ).unwrap();
1049        // Make sure we can read but not write.
1050        let mut read_vec = vec![0; hello_length];
1051        assert!(readonly_vmo.read(&mut read_vec, 0).is_ok());
1052        assert_eq!(read_vec, b"hello");
1053        assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED));
1054
1055        // Write new data to the original handle, and read it from the new handle
1056        assert!(vmo.write(b"bye", 0).is_ok());
1057        assert!(readonly_vmo.read(&mut read_vec, 0).is_ok());
1058        assert_eq!(read_vec, b"byelo");
1059    }
1060
1061    // Test replace by means of a VMO
1062    #[test]
1063    fn replace() {
1064        let hello_length: usize = 5;
1065
1066        // Create a VMO and write some data to it.
1067        let vmo = Vmo::create(hello_length as u64).unwrap();
1068        assert!(vmo.write(b"hello", 0).is_ok());
1069
1070        // Replace, reducing rights to read.
1071        let readonly_vmo = vmo.replace_handle(Rights::READ).unwrap();
1072        // Make sure we can read but not write.
1073        let mut read_vec = vec![0; hello_length];
1074        assert!(readonly_vmo.read(&mut read_vec, 0).is_ok());
1075        assert_eq!(read_vec, b"hello");
1076        assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED));
1077    }
1078
1079    #[test]
1080    fn set_get_name() {
1081        // We need some concrete object to exercise the AsHandleRef<'_> set/get_name functions.
1082        let vmo = Vmo::create(1).unwrap();
1083        let short_name = Name::new("v").unwrap();
1084        assert!(vmo.set_name(&short_name).is_ok());
1085        assert_eq!(vmo.get_name().unwrap(), short_name);
1086    }
1087
1088    #[test]
1089    fn set_get_max_len_name() {
1090        let vmo = Vmo::create(1).unwrap();
1091        let max_len_name = Name::new("a_great_maximum_length_vmo_name").unwrap(); // 31 bytes
1092        assert!(vmo.set_name(&max_len_name).is_ok());
1093        assert_eq!(vmo.get_name().unwrap(), max_len_name);
1094    }
1095
1096    #[test]
1097    fn basic_info_channel() {
1098        let (side1, side2) = Channel::create();
1099        let info1 = side1.basic_info().expect("side1 basic_info failed");
1100        let info2 = side2.basic_info().expect("side2 basic_info failed");
1101
1102        assert_eq!(info1.koid, info2.related_koid);
1103        assert_eq!(info2.koid, info1.related_koid);
1104
1105        for info in &[info1, info2] {
1106            assert!(info.koid.raw_koid() >= sys::ZX_KOID_FIRST);
1107            assert_eq!(info.object_type, ObjectType::CHANNEL);
1108            assert!(info.rights.contains(Rights::READ | Rights::WRITE | Rights::WAIT));
1109        }
1110
1111        let side1_repl = side1.replace_handle(Rights::READ).expect("side1 replace_handle failed");
1112        let info1_repl = side1_repl.basic_info().expect("side1_repl basic_info failed");
1113        assert_eq!(info1_repl.koid, info1.koid);
1114        assert_eq!(info1_repl.rights, Rights::READ);
1115    }
1116
1117    #[test]
1118    fn basic_info_vmar() {
1119        // VMARs aren't waitable.
1120        let root_vmar = fuchsia_runtime::vmar_root_self();
1121        let info = root_vmar.basic_info().expect("vmar basic_info failed");
1122        assert_eq!(info.object_type, ObjectType::VMAR);
1123        assert!(!info.rights.contains(Rights::WAIT));
1124    }
1125
1126    #[test]
1127    fn count_info() {
1128        let vmo0 = Vmo::create(1).unwrap();
1129        let count_info = vmo0.count_info().expect("vmo0 count_info failed");
1130        assert_eq!(count_info.handle_count, 1);
1131
1132        let vmo1 = vmo0.duplicate_handle(Rights::SAME_RIGHTS).expect("vmo duplicate_handle failed");
1133        let count_info = vmo1.count_info().expect("vmo1 count_info failed");
1134        assert_eq!(count_info.handle_count, 2);
1135    }
1136
1137    #[test]
1138    fn raw_handle_disposition() {
1139        const RAW_HANDLE: sys::zx_handle_t = 1;
1140        let hd = HandleDisposition::new(
1141            HandleOp::Move(unsafe { NullableHandle::from_raw(RAW_HANDLE) }),
1142            ObjectType::VMO,
1143            Rights::EXECUTE,
1144            Status::OK,
1145        );
1146        let raw_hd = hd.into_raw();
1147        assert_eq!(raw_hd.operation, sys::ZX_HANDLE_OP_MOVE);
1148        assert_eq!(raw_hd.handle, RAW_HANDLE);
1149        assert_eq!(raw_hd.rights, sys::ZX_RIGHT_EXECUTE);
1150        assert_eq!(raw_hd.type_, sys::ZX_OBJ_TYPE_VMO);
1151        assert_eq!(raw_hd.result, sys::ZX_OK);
1152    }
1153
1154    #[test]
1155    fn regression_nullable_handle_into_raw_recursion() {
1156        let h = NullableHandle::invalid();
1157        // This should not stack overflow
1158        assert_eq!(h.into_raw(), sys::ZX_HANDLE_INVALID);
1159
1160        let vmo = Vmo::create(1).unwrap();
1161        let raw = vmo.raw_handle();
1162        let h = vmo.into_handle();
1163        // This should not stack overflow
1164        assert_eq!(h.into_raw(), raw);
1165    }
1166
1167    #[test]
1168    fn handle_info_from_raw() {
1169        const RAW_HANDLE: sys::zx_handle_t = 1;
1170        let raw_hi = sys::zx_handle_info_t {
1171            handle: RAW_HANDLE,
1172            ty: sys::ZX_OBJ_TYPE_VMO,
1173            rights: sys::ZX_RIGHT_EXECUTE,
1174            unused: 128,
1175        };
1176        let hi = unsafe { HandleInfo::from_raw(raw_hi) };
1177        assert_eq!(hi.handle.into_raw(), RAW_HANDLE);
1178        assert_eq!(hi.object_type, ObjectType::VMO);
1179        assert_eq!(hi.rights, Rights::EXECUTE);
1180    }
1181
1182    #[test]
1183    fn basic_peer_closed() {
1184        let (lhs, rhs) = crate::EventPair::create();
1185        assert!(!lhs.is_closed().unwrap());
1186        assert!(!rhs.is_closed().unwrap());
1187        drop(rhs);
1188        assert!(lhs.is_closed().unwrap());
1189    }
1190}