1use crate::callback_state::CallbackSharedState;
6use core::fmt;
7use core::pin::Pin;
8use core::ptr::NonNull;
9use core::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, Ordering};
10use core::task::{Context, Poll};
11use futures::Stream;
12use futures::task::AtomicWaker;
13use libasync_dispatcher::{DetectDispatcher, GetAsyncDispatcher};
14use libasync_sys::{
15 async_bind_irq, async_dispatcher_t, async_irq, async_irq_t, async_state_t, async_unbind_irq,
16};
17use std::sync::Arc;
18use zx::sys::{ZX_ERR_CANCELED, ZX_OK, zx_packet_interrupt_t, zx_status_t};
19use zx::{
20 AsHandleRef, BootTimeline, Instant, Interrupt, InterruptKind, RealInterruptKind, Status,
21 Timeline,
22};
23
24struct IrqState {
26 async_dispatcher: NonNull<async_dispatcher_t>,
27 waker: AtomicWaker,
28 status: AtomicI32,
29 timestamp: AtomicI64,
30 raw_ptr_released: AtomicBool,
31}
32
33unsafe impl Send for IrqState {}
35unsafe impl Sync for IrqState {}
36
37type SharedState = CallbackSharedState<async_irq, IrqState>;
38
39impl IrqState {
40 unsafe extern "C" fn call(
41 dispatcher: *mut async_dispatcher_t,
42 irq: *mut async_irq_t,
43 status: zx_status_t,
44 signal: *const zx_packet_interrupt_t,
45 ) {
46 unsafe { Arc::increment_strong_count(irq as *const SharedState) };
50 let state = unsafe { Arc::from_raw(irq as *const SharedState) };
51
52 debug_assert!(
53 dispatcher == state.async_dispatcher.as_ptr(),
54 "dispatcher pointer mismatch in irq callback"
55 );
56
57 if status == ZX_OK {
58 debug_assert!(!signal.is_null(), "signal must not be null when status is ZX_OK");
59 let ts = unsafe { (*signal).timestamp };
61 state.timestamp.store(ts, Ordering::Relaxed);
62 state.status.store(ZX_OK, Ordering::Release);
63 state.waker.wake();
64 } else if status == ZX_ERR_CANCELED {
65 state.status.store(status, Ordering::Release);
66 state.waker.wake();
67
68 if state
69 .raw_ptr_released
70 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
71 .is_ok()
72 {
73 unsafe { SharedState::release_raw_ptr(irq) };
75 }
76 }
77 }
78}
79
80pub struct OnInterrupt<K: InterruptKind = RealInterruptKind, T: Timeline = BootTimeline> {
82 dispatcher: DetectDispatcher,
83 interrupt: Option<Interrupt<K, T>>,
84 state: Option<Arc<SharedState>>,
85}
86
87impl<K: InterruptKind, T: Timeline> OnInterrupt<K, T> {
88 pub fn new(interrupt: Interrupt<K, T>) -> Self {
90 Self { dispatcher: DetectDispatcher::default(), interrupt: Some(interrupt), state: None }
91 }
92
93 pub fn new_on(dispatcher: impl GetAsyncDispatcher, interrupt: Interrupt<K, T>) -> Self {
95 Self {
96 dispatcher: DetectDispatcher::new(dispatcher.get_async_dispatcher()),
97 interrupt: Some(interrupt),
98 state: None,
99 }
100 }
101
102 pub fn ack(&self) -> Result<(), Status> {
106 self.interrupt.as_ref().ok_or(Status::BAD_STATE)?.ack()
107 }
108
109 pub fn interrupt(&self) -> Option<&Interrupt<K, T>> {
111 self.interrupt.as_ref()
112 }
113
114 pub fn take_interrupt(&mut self) -> Option<Interrupt<K, T>> {
116 self.unbind();
117 self.interrupt.take()
118 }
119
120 fn bind(&mut self) -> Result<(), Status> {
121 let interrupt = self.interrupt.as_ref().ok_or(Status::BAD_STATE)?;
122 let dispatcher = self.dispatcher.get_or_detect()?;
123 let async_dispatcher = dispatcher.as_ptr();
124
125 let base = async_irq {
126 state: async_state_t::default(),
127 handler: Some(IrqState::call),
128 object: interrupt.raw_handle(),
129 };
130
131 let inner = IrqState {
132 async_dispatcher,
133 waker: AtomicWaker::new(),
134 status: AtomicI32::new(Status::SHOULD_WAIT.into_raw()),
135 timestamp: AtomicI64::new(0),
136 raw_ptr_released: AtomicBool::new(false),
137 };
138
139 let shared_state = SharedState::new(base, inner);
140 let raw_ptr = SharedState::make_raw_ptr(shared_state.clone());
141 let status = unsafe { async_bind_irq(async_dispatcher.as_ptr(), raw_ptr) };
143 if let Err(err) = Status::ok(status) {
144 unsafe { SharedState::release_raw_ptr(raw_ptr) };
146 return Err(err);
147 }
148
149 self.state = Some(shared_state);
150 Ok(())
151 }
152
153 fn unbind(&mut self) {
154 let Some(state) = self.state.take() else {
155 return;
156 };
157
158 let raw_ptr = SharedState::as_raw_ptr(&state);
159 let status = unsafe { async_unbind_irq(state.async_dispatcher.as_ptr(), raw_ptr) };
161
162 if Status::ok(status).is_ok()
163 && state
164 .raw_ptr_released
165 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
166 .is_ok()
167 {
168 unsafe { SharedState::release_raw_ptr(raw_ptr) };
170 }
171 }
174}
175
176impl<K: InterruptKind, T: Timeline> Stream for OnInterrupt<K, T> {
177 type Item = Result<Instant<T>, Status>;
178
179 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
180 let this = self.get_mut();
181
182 if this.interrupt.is_none() {
183 return Poll::Ready(None);
184 }
185
186 if this.state.is_none()
187 && let Err(err) = this.bind()
188 {
189 return Poll::Ready(Some(Err(err)));
190 }
191
192 let state = this.state.as_ref().unwrap();
193 state.waker.register(cx.waker());
194
195 match state.status.load(Ordering::Acquire) {
196 ZX_OK => {
197 state.status.store(Status::SHOULD_WAIT.into_raw(), Ordering::Release);
199 let ts = state.timestamp.load(Ordering::Relaxed);
200 Poll::Ready(Some(Ok(Instant::from_nanos(ts))))
201 }
202 ZX_ERR_CANCELED => {
203 this.unbind();
204 this.interrupt = None;
205 Poll::Ready(Some(Err(Status::CANCELED)))
206 }
207 s if s == Status::SHOULD_WAIT.into_raw() => Poll::Pending,
208 s => Poll::Ready(Some(Err(Status::err_from_raw(s)))),
209 }
210 }
211}
212
213impl<K: InterruptKind, T: Timeline> Drop for OnInterrupt<K, T> {
214 fn drop(&mut self) {
215 self.unbind();
216 }
217}
218
219impl<K: InterruptKind, T: Timeline> AsHandleRef for OnInterrupt<K, T> {
220 fn as_handle_ref(&self) -> zx::HandleRef<'_> {
221 self.as_ref().as_handle_ref()
222 }
223}
224
225impl<K: InterruptKind, T: Timeline> AsRef<Interrupt<K, T>> for OnInterrupt<K, T> {
226 fn as_ref(&self) -> &Interrupt<K, T> {
227 self.interrupt.as_ref().expect("OnInterrupt dereferenced after interrupt taken")
228 }
229}
230
231impl<K: InterruptKind, T: Timeline> Unpin for OnInterrupt<K, T> {}
232
233impl<K: InterruptKind, T: Timeline> fmt::Debug for OnInterrupt<K, T> {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 f.debug_struct("OnInterrupt")
236 .field("interrupt", &self.interrupt.as_ref().map(|i| i.as_handle_ref()))
237 .finish()
238 }
239}
240
241pub trait DispatcherInterruptExt {
243 fn on_interrupt<K: InterruptKind, T: Timeline>(
245 &self,
246 interrupt: Interrupt<K, T>,
247 ) -> OnInterrupt<K, T>;
248
249 fn try_on_interrupt<K: InterruptKind, T: Timeline>(
251 &self,
252 interrupt: Interrupt<K, T>,
253 ) -> Option<OnInterrupt<K, T>>;
254}
255
256impl<D: GetAsyncDispatcher> DispatcherInterruptExt for D {
257 fn on_interrupt<K: InterruptKind, T: Timeline>(
258 &self,
259 interrupt: Interrupt<K, T>,
260 ) -> OnInterrupt<K, T> {
261 self.try_on_interrupt(interrupt).expect("No current dispatcher")
262 }
263
264 fn try_on_interrupt<K: InterruptKind, T: Timeline>(
265 &self,
266 interrupt: Interrupt<K, T>,
267 ) -> Option<OnInterrupt<K, T>> {
268 let dispatcher = self.try_get_async_dispatcher()?;
269 Some(OnInterrupt::new_on(dispatcher, interrupt))
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276 use fdf_env::test::spawn_in_driver;
277 use futures::{StreamExt, poll};
278 use libasync_dispatcher::CurrentDispatcher;
279 use std::sync::mpsc;
280 use std::task::Waker;
281 use std::thread::sleep;
282 use std::time::Duration;
283 use zx::{
284 BootInstant, MonotonicInstant, MonotonicTimeline, VirtualInterrupt, VirtualInterruptKind,
285 };
286
287 #[test]
288 fn test_bind_and_receive_virtual_interrupt() {
289 spawn_in_driver("testing irq wait", async move {
290 let interrupt = VirtualInterrupt::create_virtual().unwrap();
291 let mut stream = CurrentDispatcher.on_interrupt(interrupt);
292 assert_eq!(poll!(stream.next()), Poll::Pending);
293
294 let trigger_time = BootInstant::from_nanos(12345);
295 stream.interrupt().unwrap().trigger(trigger_time).unwrap();
296
297 let res = stream.next().await;
298 assert_eq!(res, Some(Ok(trigger_time)));
299
300 stream.ack().unwrap();
301 });
302 }
303
304 #[test]
305 fn test_multiple_interrupts_sequential() {
306 spawn_in_driver("testing irq multi-shot", async move {
307 let interrupt = VirtualInterrupt::create_virtual().unwrap();
308 let mut stream = CurrentDispatcher.on_interrupt(interrupt);
309 assert_eq!(poll!(stream.next()), Poll::Pending);
310
311 for i in 1..=5 {
312 let trigger_time = BootInstant::from_nanos(i * 1000);
313 stream.interrupt().unwrap().trigger(trigger_time).unwrap();
314
315 let res = stream.next().await;
316 assert_eq!(res, Some(Ok(trigger_time)));
317
318 stream.ack().unwrap();
319 }
320 });
321 }
322
323 #[test]
324 fn test_take_interrupt() {
325 spawn_in_driver("testing take_interrupt", async move {
326 let interrupt = VirtualInterrupt::create_virtual().unwrap();
327 let mut stream = CurrentDispatcher.on_interrupt(interrupt);
328 assert_eq!(poll!(stream.next()), Poll::Pending);
329
330 let reclaimed_irq = stream.take_interrupt().unwrap();
331 assert_eq!(poll!(stream.next()), Poll::Ready(None));
332
333 let trigger_time = BootInstant::from_nanos(54321);
334 reclaimed_irq.trigger(trigger_time).unwrap();
335 assert_eq!(reclaimed_irq.wait().unwrap(), trigger_time);
336 });
337 }
338
339 #[test]
340 fn test_drop_while_bound() {
341 spawn_in_driver("testing drop while bound", async move {
342 let interrupt = VirtualInterrupt::create_virtual().unwrap();
343 let mut stream = CurrentDispatcher.on_interrupt(interrupt);
344 assert_eq!(poll!(stream.next()), Poll::Pending);
345 drop(stream);
346 });
347 }
348
349 #[test]
350 fn test_monotonic_timeline_interrupt() {
351 spawn_in_driver("testing monotonic timeline irq", async move {
352 let interrupt =
353 Interrupt::<VirtualInterruptKind, MonotonicTimeline>::create_virtual().unwrap();
354 let mut stream = CurrentDispatcher.on_interrupt(interrupt);
355 assert_eq!(poll!(stream.next()), Poll::Pending);
356
357 let trigger_time = MonotonicInstant::from_nanos(99999);
358 stream.interrupt().unwrap().trigger(trigger_time).unwrap();
359
360 let res = stream.next().await;
361 assert_eq!(res, Some(Ok(trigger_time)));
362
363 stream.ack().unwrap();
364 });
365 }
366
367 #[test]
368 fn test_dispatcher_shutdown_cancel() {
369 let (stream_tx, stream_rx) = mpsc::channel();
370 spawn_in_driver("testing irq shutdown", async move {
371 let interrupt = VirtualInterrupt::create_virtual().unwrap();
372 let mut stream = CurrentDispatcher.on_interrupt(interrupt);
373 assert_eq!(poll!(stream.next()), Poll::Pending);
374 stream_tx.send(stream).unwrap();
375 });
376
377 let mut stream = stream_rx.recv().unwrap();
378 let waker = Waker::noop();
379 let mut context = Context::from_waker(waker);
380 loop {
381 let Poll::Ready(res) = stream.poll_next_unpin(&mut context) else {
382 sleep(Duration::from_millis(10));
383 continue;
384 };
385 assert_eq!(res, Some(Err(Status::CANCELED)));
386 break;
387 }
388
389 assert_eq!(stream.poll_next_unpin(&mut context), Poll::Ready(None));
391 }
392}