Skip to main content

zx/
wait.rs

1// Copyright 2024 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 crate::sys::zx_wait_item_t;
6use crate::{AsHandleRef, HandleRef, MonotonicInstant, Signals, Status, sys};
7
8/// A "wait item" containing a handle reference and information about what signals
9/// to wait on, and, on return from `object_wait_many`, which are pending.
10///
11/// Returned from `wait_item()` methods on handle newtypes.
12///
13/// ABI-compatible with `zx_wait_item_t`.
14#[repr(C)]
15#[derive(Debug)]
16pub struct WaitItem<'a> {
17    /// The handle to wait on.
18    handle: HandleRef<'a>,
19    /// A set of signals to wait for.
20    waiting_for: Signals,
21    /// The set of signals pending, on return of `object_wait_many`.
22    pending: Signals,
23}
24
25// Assert that WaitItem is ABI-equivalent to zx_wait_item_t. We don't use the crate's
26// static_assert_align!() macro here because it's hard to get that macro to work with lifetimes.
27static_assertions::assert_eq_size!(WaitItem<'_>, zx_wait_item_t);
28static_assertions::assert_eq_align!(WaitItem<'_>, zx_wait_item_t);
29static_assertions::const_assert_eq!(
30    std::mem::offset_of!(WaitItem<'_>, handle),
31    std::mem::offset_of!(zx_wait_item_t, handle),
32);
33static_assertions::const_assert_eq!(
34    std::mem::offset_of!(WaitItem<'_>, waiting_for),
35    std::mem::offset_of!(zx_wait_item_t, waitfor),
36);
37static_assertions::const_assert_eq!(
38    std::mem::offset_of!(WaitItem<'_>, pending),
39    std::mem::offset_of!(zx_wait_item_t, pending),
40);
41
42impl<'a> WaitItem<'a> {
43    /// Creates a new `WaitItem` for the given handle and signals.
44    pub(crate) fn new(handle: HandleRef<'a>, waiting_for: Signals) -> Self {
45        // SAFETY: call requires the same invariants that NullableHandleRef has for its handles.
46        let handle = unsafe { HandleRef::from_raw_handle(handle.raw_handle()) };
47        Self { handle, waiting_for, pending: Signals::empty() }
48    }
49
50    /// Returns a reference to the contained handle, if any.
51    pub fn handle(&self) -> HandleRef<'a> {
52        self.handle.clone()
53    }
54
55    /// Returns the signals that this WaitItem is asing the kernel to wait for.
56    pub fn waiting_for(&self) -> Signals {
57        self.waiting_for
58    }
59
60    /// Returns the set of signals pending as written by `object_wait_many`.
61    pub fn pending(&self) -> Signals {
62        self.pending
63    }
64}
65
66impl<'a> AsHandleRef for WaitItem<'a> {
67    fn as_handle_ref(&self) -> HandleRef<'a> {
68        self.handle()
69    }
70}
71
72/// Wait on multiple handles.
73/// The success return value is a bool indicating whether one or more of the
74/// provided handle references was closed during the wait.
75///
76/// Wraps the
77/// [zx_object_wait_many](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_wait_many.md)
78/// syscall.
79pub fn object_wait_many(
80    items: &mut [WaitItem<'_>],
81    deadline: MonotonicInstant,
82) -> Result<bool, Status> {
83    // SAFETY: WaitItem is ABI-compatible with zx_wait_item_t. The pointer is valid for the kernel
84    // to write to for the slice's whole length because we're guaranteed exclusivity by &mut.
85    let status = unsafe {
86        sys::zx_object_wait_many(
87            items.as_mut_ptr().cast::<zx_wait_item_t>(),
88            items.len(),
89            deadline.into_nanos(),
90        )
91    };
92    if status == sys::ZX_ERR_CANCELED {
93        return Ok(true);
94    }
95    Status::ok(status).map(|()| false)
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::{Duration, Event, WaitResult};
102
103    #[test]
104    fn wait_and_signal() {
105        let event = Event::create();
106        let ten_ms = Duration::from_millis(10);
107
108        // Waiting on it without setting any signal should time out.
109        assert_eq!(
110            event.wait_one(Signals::USER_0, MonotonicInstant::after(ten_ms)),
111            WaitResult::TimedOut(Signals::empty())
112        );
113
114        // If we set a signal, we should be able to wait for it.
115        assert!(event.signal(Signals::NONE, Signals::USER_0).is_ok());
116        assert_eq!(
117            event.wait_one(Signals::USER_0, MonotonicInstant::after(ten_ms)).unwrap(),
118            Signals::USER_0
119        );
120
121        // Should still work, signals aren't automatically cleared.
122        assert_eq!(
123            event.wait_one(Signals::USER_0, MonotonicInstant::after(ten_ms)).unwrap(),
124            Signals::USER_0
125        );
126
127        // Now clear it, and waiting should time out again.
128        assert!(event.signal(Signals::USER_0, Signals::NONE).is_ok());
129        assert_eq!(
130            event.wait_one(Signals::USER_0, MonotonicInstant::after(ten_ms)),
131            WaitResult::TimedOut(Signals::empty())
132        );
133    }
134
135    #[test]
136    fn wait_many_and_signal() {
137        let ten_ms = Duration::from_millis(10);
138        let e1 = Event::create();
139        let e2 = Event::create();
140
141        // Waiting on them now should time out.
142        let mut items = [e1.wait_item(Signals::USER_0), e2.wait_item(Signals::USER_1)];
143        assert_eq!(
144            object_wait_many(&mut items[..], MonotonicInstant::after(ten_ms)),
145            Err(Status::TIMED_OUT)
146        );
147        assert_eq!(items[0].pending(), Signals::NONE);
148        assert_eq!(items[1].pending(), Signals::NONE);
149
150        // Signal one object and it should return success.
151        assert!(e1.signal(Signals::NONE, Signals::USER_0).is_ok());
152        assert!(object_wait_many(&mut items, MonotonicInstant::after(ten_ms)).is_ok());
153        assert_eq!(items[0].pending(), Signals::USER_0);
154        assert_eq!(items[1].pending(), Signals::NONE);
155
156        // Signal the other and it should return both.
157        assert!(e2.signal(Signals::NONE, Signals::USER_1).is_ok());
158        assert!(object_wait_many(&mut items, MonotonicInstant::after(ten_ms)).is_ok());
159        assert_eq!(items[0].pending(), Signals::USER_0);
160        assert_eq!(items[1].pending(), Signals::USER_1);
161
162        // Clear signals on both; now it should time out again.
163        assert!(e1.signal(Signals::USER_0, Signals::NONE).is_ok());
164        assert!(e2.signal(Signals::USER_1, Signals::NONE).is_ok());
165        assert_eq!(
166            object_wait_many(&mut items, MonotonicInstant::after(ten_ms)),
167            Err(Status::TIMED_OUT)
168        );
169        assert_eq!(items[0].pending(), Signals::NONE);
170        assert_eq!(items[1].pending(), Signals::NONE);
171    }
172}