1use crate::callback_state::CallbackSharedState;
6use core::fmt;
7use core::future::Future;
8use core::pin::Pin;
9use core::ptr::NonNull;
10use core::sync::atomic::{AtomicI32, AtomicU32, Ordering};
11use core::task::{Context, Poll};
12use futures::task::AtomicWaker;
13use libasync_dispatcher::{DetectDispatcher, GetAsyncDispatcher};
14use libasync_sys::{async_begin_wait, async_cancel_wait, async_dispatcher, async_wait};
15use std::sync::Arc;
16use zx::Status;
17use zx::sys::{ZX_ERR_CANCELED, ZX_OK};
18
19struct WaitState {
21 async_dispatcher: NonNull<async_dispatcher>,
22 waker: AtomicWaker,
23 status: AtomicI32,
24 observed: AtomicU32,
25}
26
27unsafe impl Send for WaitState {}
29unsafe impl Sync for WaitState {}
30
31type SharedState = CallbackSharedState<async_wait, WaitState>;
32
33impl WaitState {
34 extern "C" fn call(
35 dispatcher: *mut async_dispatcher,
36 wait: *mut async_wait,
37 status: i32,
38 signal: *const zx::sys::zx_packet_signal_t,
39 ) {
40 debug_assert!(
41 status == ZX_OK || status == ZX_ERR_CANCELED,
42 "wait callback called with status other than ok or canceled: {}",
43 status
44 );
45
46 let state = unsafe { SharedState::from_raw_ptr(wait) };
48
49 debug_assert!(
50 dispatcher == state.async_dispatcher.as_ptr(),
51 "dispatcher does not match wait's dispatcher"
52 );
53
54 if status == ZX_OK {
55 debug_assert!(!signal.is_null(), "signal must not be null when status is ZX_OK");
56 let observed_signals = unsafe { (*signal).observed };
59 state.observed.store(observed_signals, Ordering::Relaxed);
62 }
63
64 state.status.store(status, Ordering::Release);
68
69 state.waker.wake();
70 }
71}
72
73pub trait DispatcherSignalExt {
75 fn on_signals<H: zx::AsHandleRef>(&self, handle: H, signals: zx::Signals) -> OnSignals<H>;
77
78 fn try_on_signals<H: zx::AsHandleRef>(
81 &self,
82 handle: H,
83 signals: zx::Signals,
84 ) -> Option<OnSignals<H>>;
85}
86
87impl<T> DispatcherSignalExt for T
88where
89 T: GetAsyncDispatcher,
90{
91 fn on_signals<H: zx::AsHandleRef>(&self, handle: H, signals: zx::Signals) -> OnSignals<H> {
92 self.try_on_signals(handle, signals).expect("No current dispatcher")
93 }
94
95 fn try_on_signals<H: zx::AsHandleRef>(
96 &self,
97 handle: H,
98 signals: zx::Signals,
99 ) -> Option<OnSignals<H>> {
100 let dispatcher = self.try_get_async_dispatcher()?;
101 Some(OnSignals::new_on(dispatcher, handle, signals))
102 }
103}
104
105pub struct OnSignals<H: zx::AsHandleRef> {
107 dispatcher: DetectDispatcher,
108 handle: Option<H>,
109 signals: zx::Signals,
110 state: Option<Arc<SharedState>>,
111}
112
113impl<H: zx::AsHandleRef> OnSignals<H> {
114 pub fn new(handle: H, signals: zx::Signals) -> Self {
117 Self { dispatcher: DetectDispatcher::default(), handle: Some(handle), signals, state: None }
118 }
119
120 pub fn new_on(dispatcher: impl GetAsyncDispatcher, handle: H, signals: zx::Signals) -> Self {
122 let async_disp = dispatcher.get_async_dispatcher();
123 Self {
124 dispatcher: DetectDispatcher::new(async_disp),
125 handle: Some(handle),
126 signals,
127 state: None,
128 }
129 }
130
131 pub fn take_handle(&mut self) -> Option<H> {
133 self.cancel_wait();
134 self.handle.take()
135 }
136
137 fn cancel_wait(&mut self) {
138 let Some(state) = self.state.take() else {
139 return;
140 };
141
142 if state.status.load(Ordering::Acquire) != Status::SHOULD_WAIT.into_raw() {
145 return;
147 }
148
149 let state_ptr = SharedState::as_raw_ptr(&state);
150 let status = unsafe { async_cancel_wait(state.async_dispatcher.as_ptr(), state_ptr) };
152 if Status::from_raw(status) == Status::OK {
153 unsafe { SharedState::release_raw_ptr(state_ptr) };
156 }
157 }
158}
159
160impl<H: zx::AsHandleRef + Unpin> Future for OnSignals<H> {
161 type Output = Result<zx::Signals, Status>;
162
163 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
164 let Some(handle) = self.handle.as_ref() else {
165 return Poll::Ready(Err(Status::BAD_STATE));
166 };
167
168 if let Some(state) = &self.state {
169 state.waker.register(cx.waker());
173
174 let status = state.status.load(Ordering::Acquire);
177 if status != Status::SHOULD_WAIT.into_raw() {
178 if status == ZX_OK {
179 let observed = state.observed.load(Ordering::Relaxed);
182 return Poll::Ready(Ok(zx::Signals::from_bits_truncate(observed)));
183 } else {
184 return Poll::Ready(Err(Status::from_raw(status)));
185 }
186 } else {
187 match handle
191 .as_handle_ref()
192 .wait_one(self.signals, zx::MonotonicInstant::INFINITE_PAST)
193 .to_result()
194 {
195 Ok(signals) => {
196 self.cancel_wait();
197 return Poll::Ready(Ok(signals));
198 }
199 Err(Status::TIMED_OUT) => {
200 return Poll::Pending;
201 }
202 Err(err) => {
203 self.cancel_wait();
204 return Poll::Ready(Err(err));
205 }
206 }
207 }
208 }
209
210 match handle
212 .as_handle_ref()
213 .wait_one(self.signals, zx::MonotonicInstant::INFINITE_PAST)
214 .to_result()
215 {
216 Ok(signals) => return Poll::Ready(Ok(signals)),
217 Err(Status::TIMED_OUT) => { }
218 Err(err) => return Poll::Ready(Err(err)),
219 }
220
221 let dispatcher = self.dispatcher.get_or_detect()?;
222 let async_dispatcher = dispatcher.as_ptr();
223
224 let wait = async_wait {
225 handler: Some(WaitState::call),
226 object: handle.as_handle_ref().raw_handle(),
227 trigger: self.signals.bits(),
228 options: 0,
229 ..Default::default()
230 };
231
232 let state = WaitState {
233 async_dispatcher,
234 waker: AtomicWaker::new(),
235 status: AtomicI32::new(Status::SHOULD_WAIT.into_raw()),
236 observed: AtomicU32::new(0),
237 };
238
239 let shared_state = SharedState::new(wait, state);
240 shared_state.waker.register(cx.waker());
241
242 let state_ptr = SharedState::make_raw_ptr(shared_state.clone());
243
244 let res = Status::ok(unsafe { async_begin_wait(async_dispatcher.as_ptr(), state_ptr) });
246 match res {
247 Ok(_) => {
248 self.state = Some(shared_state);
249 Poll::Pending
250 }
251 Err(err) => {
252 unsafe { SharedState::release_raw_ptr(state_ptr) };
255 Poll::Ready(Err(err))
256 }
257 }
258 }
259}
260
261impl<H: zx::AsHandleRef> Drop for OnSignals<H> {
262 fn drop(&mut self) {
263 self.cancel_wait();
264 }
265}
266
267impl<H: zx::AsHandleRef> zx::AsHandleRef for OnSignals<H> {
268 fn as_handle_ref(&self) -> zx::HandleRef<'_> {
269 self.handle.as_ref().expect("OnSignals dereferenced after handle was taken").as_handle_ref()
270 }
271}
272
273impl<H: zx::AsHandleRef> fmt::Debug for OnSignals<H> {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 write!(
276 f,
277 "OnSignals {{ handle: {:?}, signals: {:?} }}",
278 self.handle.as_ref().map(|h| h.as_handle_ref()),
279 self.signals
280 )
281 }
282}
283
284pub type OnSignalsRef<'a> = OnSignals<zx::HandleRef<'a>>;
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290 use fdf_env::test::spawn_in_driver;
291 use futures::{FutureExt, poll};
292 use libasync_dispatcher::CurrentDispatcher;
293 use std::sync::{LazyLock, mpsc};
294 use std::task::Waker;
295 use std::thread::sleep;
296 use std::time::Duration;
297
298 #[test]
299 fn wait_on_signals() {
300 spawn_in_driver("testing wait", async move {
301 let event = zx::Event::create();
302 let mut fut = CurrentDispatcher.on_signals(&event, zx::Signals::EVENT_SIGNALED);
303 assert_eq!(poll!(&mut fut), Poll::Pending);
304
305 event.signal(zx::Signals::empty(), zx::Signals::EVENT_SIGNALED).unwrap();
306
307 let res = fut.await;
308 assert!(res.is_ok());
309 assert!(res.unwrap().contains(zx::Signals::EVENT_SIGNALED));
310 });
311 }
312
313 #[test]
314 fn wait_on_already_signaled() {
315 spawn_in_driver("testing wait", async move {
316 let event = zx::Event::create();
317 event.signal(zx::Signals::empty(), zx::Signals::EVENT_SIGNALED).unwrap();
318
319 let fut = CurrentDispatcher.on_signals(&event, zx::Signals::EVENT_SIGNALED);
320
321 let res = fut.await;
322 assert!(res.is_ok());
323 assert!(res.unwrap().contains(zx::Signals::EVENT_SIGNALED));
324 });
325 }
326
327 #[test]
328 fn drop_after_poll() {
329 spawn_in_driver("testing wait", async move {
330 let event = zx::Event::create();
331 let mut fut = CurrentDispatcher.on_signals(&event, zx::Signals::EVENT_SIGNALED);
332 assert_eq!(poll!(&mut fut), Poll::Pending);
333 });
334 }
335
336 #[test]
337 fn dispatcher_shutdown_cancel() {
338 static EVENT: LazyLock<zx::Event> = LazyLock::new(zx::Event::create);
339
340 let (fut_tx, fut_rx) = mpsc::channel();
341 spawn_in_driver("testing wait", async move {
342 let mut fut = CurrentDispatcher.on_signals(&*EVENT, zx::Signals::EVENT_SIGNALED);
343 assert_eq!(poll!(&mut fut), Poll::Pending);
344 fut_tx.send(fut).unwrap();
345 });
346 let mut fut = fut_rx.recv().unwrap();
347 let waker = Waker::noop();
348 let mut context = Context::from_waker(waker);
349 loop {
350 let Poll::Ready(res) = fut.poll_unpin(&mut context) else {
351 sleep(Duration::from_millis(10));
352 continue;
353 };
354 assert_eq!(res, Err(Status::CANCELED));
355 break;
356 }
357 }
358
359 #[test]
360 fn test_take_handle() {
361 spawn_in_driver("testing wait", async move {
362 let event = zx::Event::create();
363 let mut fut = CurrentDispatcher.on_signals(event, zx::Signals::EVENT_SIGNALED);
364 assert_eq!(poll!(&mut fut), Poll::Pending);
365 let event = fut.take_handle().unwrap();
366 event.signal(zx::Signals::empty(), zx::Signals::EVENT_SIGNALED).unwrap();
367
368 assert!(fut.take_handle().is_none());
369 assert_eq!(poll!(&mut fut), Poll::Ready(Err(Status::BAD_STATE)));
370 assert_eq!(
371 event
372 .wait_one(zx::Signals::EVENT_SIGNALED, zx::MonotonicInstant::INFINITE_PAST)
373 .unwrap(),
374 zx::Signals::EVENT_SIGNALED
375 );
376 });
377 }
378}