1use std::sync::LazyLock;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8const FUTEX_WAITING: i32 = 0;
12const FUTEX_NOTIFIED: i32 = 1;
16const FUTEX_INTERRUPTED: i32 = 2;
20const FUTEX_USE_PORT: i32 = 3;
24
25const ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE: Ordering = Ordering::Relaxed;
34
35#[derive(Debug)]
46pub struct PortEvent {
47 futex: zx::Futex,
53 port: LazyLock<zx::Port>,
59 has_pending_user_packet: AtomicBool,
62}
63
64#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub enum NotifyKind {
67 Regular,
68 Interrupt,
69}
70
71#[derive(Debug, Eq, PartialEq)]
73pub enum PortWaitResult {
74 Signal { key: u64, observed: zx::Signals },
76 Notification { kind: NotifyKind },
78 TimedOut,
80}
81
82impl PortWaitResult {
83 const NOTIFY_REGULAR: Self = Self::Notification { kind: NotifyKind::Regular };
84 const NOTIFY_INTERRUPT: Self = Self::Notification { kind: NotifyKind::Interrupt };
85}
86
87impl PortEvent {
88 pub fn new() -> Self {
90 Self {
91 futex: zx::Futex::new(FUTEX_WAITING),
92 port: LazyLock::new(zx::Port::create),
93 has_pending_user_packet: Default::default(),
94 }
95 }
96
97 pub fn wait(&self, deadline: zx::MonotonicInstant) -> PortWaitResult {
99 self.wait_with_clean_delay(deadline, crate::STACK_CLEAN_DELAY)
100 }
101
102 fn wait_with_clean_delay(
103 &self,
104 deadline: zx::MonotonicInstant,
105 clean_delay: zx::MonotonicDuration,
106 ) -> PortWaitResult {
107 let mut clean_deadline: Option<zx::MonotonicInstant> = None;
108 let mut state = self.futex.load(ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE);
109 loop {
110 match state {
111 FUTEX_WAITING => {
112 if deadline <= zx::MonotonicInstant::ZERO {
116 return PortWaitResult::TimedOut;
117 }
118 let wait_deadline = *clean_deadline.get_or_insert_with(|| {
119 crate::initial_clean_deadline(deadline, clean_delay)
120 });
121 match self.futex.wait(FUTEX_WAITING, None, wait_deadline) {
122 Ok(()) | Err(zx::Status::BAD_STATE) => {
123 state =
124 self.futex.load(ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE);
125 }
126 Err(zx::Status::TIMED_OUT) => {
127 if wait_deadline < deadline {
128 starnix_stack::clean_stack();
129 clean_deadline = Some(deadline);
130 state = self
131 .futex
132 .load(ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE);
133 } else {
134 return PortWaitResult::TimedOut;
135 }
136 }
137 Err(e) => panic!("Unexpected error from zx_futex_wait: {e}"),
138 }
139 }
140 FUTEX_NOTIFIED | FUTEX_INTERRUPTED => {
141 match self.futex.compare_exchange(
142 state,
143 FUTEX_WAITING,
144 ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE,
145 ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE,
146 ) {
147 Ok(new_state) => {
148 debug_assert_eq!(new_state, state);
149 return if new_state == FUTEX_INTERRUPTED {
150 PortWaitResult::NOTIFY_INTERRUPT
151 } else {
152 PortWaitResult::NOTIFY_REGULAR
153 };
154 }
155 Err(new_state) => {
156 debug_assert_ne!(new_state, state);
157 state = new_state;
158 }
159 }
160 }
161 FUTEX_USE_PORT => {
162 break;
163 }
164 state => unreachable!("unexpected value = {state}"),
165 }
166 }
167
168 let mut clean_deadline =
169 clean_deadline.unwrap_or_else(|| crate::initial_clean_deadline(deadline, clean_delay));
170 let packet = loop {
171 match self.port.wait(clean_deadline) {
172 Ok(packet) => break Ok(packet),
173 Err(zx::Status::TIMED_OUT) if clean_deadline < deadline => {
174 starnix_stack::clean_stack();
175 clean_deadline = deadline;
176 }
177 Err(e) => break Err(e),
178 }
179 };
180 match packet {
181 Ok(packet) => match packet.status() {
182 zx::sys::ZX_OK => {
183 match packet.contents() {
184 zx::PacketContents::SignalOne(sigpkt) => PortWaitResult::Signal {
185 key: packet.key(),
186 observed: sigpkt.observed(),
187 },
188 zx::PacketContents::User(_) => {
189 assert!(
204 self.has_pending_user_packet
205 .swap(false, ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE)
206 );
207
208 PortWaitResult::NOTIFY_REGULAR
209 }
210 _contents => panic!("unexpected packet = {:?}", packet),
211 }
212 }
213 zx::sys::ZX_ERR_CANCELED => PortWaitResult::NOTIFY_INTERRUPT,
214 status => {
215 panic!("Unexpected status in port wait {}", status);
216 }
217 },
218 Err(zx::Status::TIMED_OUT) => PortWaitResult::TimedOut,
219 Err(e) => panic!("Unexpected error from port_wait: {e}"),
220 }
221 }
222
223 pub fn object_wait_async(
225 &self,
226 handle: &dyn zx::AsHandleRef,
227 key: u64,
228 signals: zx::Signals,
229 opts: zx::WaitAsyncOpts,
230 ) -> Result<(), zx::Status> {
231 match self.futex.swap(FUTEX_USE_PORT, ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE) {
232 FUTEX_WAITING => {
233 self.futex.wake_all();
234 }
235 state @ (FUTEX_NOTIFIED | FUTEX_INTERRUPTED) => {
236 self.queue_user_packet_data(if state == FUTEX_INTERRUPTED {
237 NotifyKind::Interrupt
238 } else {
239 NotifyKind::Regular
240 })
241 }
242 FUTEX_USE_PORT => {}
243 v => unreachable!("unexpected value = {v}"),
244 }
245
246 handle.as_handle_ref().wait_async(&self.port, key, signals, opts)
247 }
248
249 pub fn cancel(&self, key: u64) {
251 let _: Result<(), zx::Status> = self.port.cancel(key);
252 }
253
254 fn queue_user_packet_data(&self, kind: NotifyKind) {
260 let status = match kind {
261 NotifyKind::Interrupt => zx::sys::ZX_ERR_CANCELED,
262 NotifyKind::Regular => {
263 if self
264 .has_pending_user_packet
265 .swap(true, ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE)
266 {
267 return;
268 }
269
270 zx::sys::ZX_OK
271 }
272 };
273
274 let packet = zx::Packet::from_user_packet(0, status, zx::UserPacket::default());
275 self.port.queue(&packet).unwrap()
276 }
277
278 pub fn notify(&self, kind: NotifyKind) {
281 let futex_val = match kind {
282 NotifyKind::Interrupt => FUTEX_INTERRUPTED,
283 NotifyKind::Regular => FUTEX_NOTIFIED,
284 };
285
286 match self.futex.compare_exchange(
287 FUTEX_WAITING,
288 futex_val,
289 ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE,
290 ORDERING_FOR_ATOMICS_BETWEEN_NOTIFIER_AND_NOTIFEE,
291 ) {
292 Ok(observed) => {
293 debug_assert_eq!(observed, FUTEX_WAITING);
294 self.futex.wake_all();
295 }
296 Err(observed) => match observed {
297 FUTEX_WAITING => unreachable!("this should have passed"),
298 FUTEX_NOTIFIED | FUTEX_INTERRUPTED => {}
299 FUTEX_USE_PORT => {
300 self.queue_user_packet_data(kind);
301 }
302 observed => unreachable!("unexpected value = {observed}"),
303 },
304 }
305 }
306}
307
308#[cfg(test)]
309mod test {
310 use std::sync::Arc;
311
312 use test_case::test_case;
313
314 use super::*;
315
316 #[test]
317 fn test_signal_and_wait_block() {
318 const KEY: u64 = 1;
319 const ASSERTED_SIGNAL: zx::Signals = zx::Signals::USER_0;
320
321 let event = Arc::new(PortEvent::new());
322 let object = zx::Event::create();
323 assert_eq!(
324 event.object_wait_async(&object, KEY, ASSERTED_SIGNAL, zx::WaitAsyncOpts::empty()),
325 Ok(())
326 );
327
328 let event_clone = event.clone();
329 let thread = std::thread::spawn(move || {
330 assert_eq!(
331 event_clone.wait(zx::MonotonicInstant::INFINITE),
332 PortWaitResult::Signal { key: KEY, observed: ASSERTED_SIGNAL }
333 );
334 });
335
336 object.signal(zx::Signals::NONE, ASSERTED_SIGNAL).unwrap();
337 thread.join().expect("join thread");
338 }
339
340 #[test]
341 fn test_signal_then_wait_nonblock() {
342 const KEY: u64 = 2;
343 const ASSERTED_SIGNAL: zx::Signals = zx::Signals::USER_1;
344
345 let event = PortEvent::new();
346 let object = zx::Event::create();
347 assert_eq!(
348 event.object_wait_async(&object, KEY, ASSERTED_SIGNAL, zx::WaitAsyncOpts::empty()),
349 Ok(())
350 );
351 object.signal(zx::Signals::NONE, ASSERTED_SIGNAL).unwrap();
352
353 assert_eq!(
354 event.wait(zx::MonotonicInstant::INFINITE_PAST),
355 PortWaitResult::Signal { key: KEY, observed: ASSERTED_SIGNAL }
356 );
357 }
358
359 #[test]
360 fn test_signal_then_cancel_then_wait() {
361 const KEY: u64 = 3;
362 const ASSERTED_SIGNAL: zx::Signals = zx::Signals::USER_2;
363
364 let event = PortEvent::new();
365 let object = zx::Event::create();
366
367 assert_eq!(
368 event.object_wait_async(&object, KEY, ASSERTED_SIGNAL, zx::WaitAsyncOpts::empty()),
369 Ok(())
370 );
371 object.signal(zx::Signals::NONE, ASSERTED_SIGNAL).unwrap();
372
373 event.cancel(KEY);
374 assert_eq!(event.wait(zx::MonotonicInstant::INFINITE_PAST), PortWaitResult::TimedOut);
375 }
376
377 #[test_case(NotifyKind::Interrupt, true; "interrupt with object")]
378 #[test_case(NotifyKind::Regular, true; "not interrupt with object")]
379 #[test_case(NotifyKind::Interrupt, false; "interrupt without object")]
380 #[test_case(NotifyKind::Regular, false; "not interrupt without object")]
381 fn test_notify_and_wait_block(kind: NotifyKind, with_object: bool) {
382 const KEY: u64 = 4;
383
384 let event = Arc::new(PortEvent::new());
385 let object = zx::Event::create();
386 if with_object {
387 assert_eq!(
388 event.object_wait_async(
389 &object,
390 KEY,
391 zx::Signals::USER_3,
392 zx::WaitAsyncOpts::empty()
393 ),
394 Ok(())
395 );
396 }
397
398 let event_clone = event.clone();
399 let thread = std::thread::spawn(move || {
400 assert_eq!(
401 event_clone.wait(zx::MonotonicInstant::INFINITE),
402 PortWaitResult::Notification { kind }
403 );
404 });
405
406 event.notify(kind);
407 thread.join().expect("join thread");
408 }
409
410 #[test_case(NotifyKind::Interrupt, true; "interrupt with object")]
411 #[test_case(NotifyKind::Regular, true; "not interrupt with object")]
412 #[test_case(NotifyKind::Interrupt, false; "interrupt without object")]
413 #[test_case(NotifyKind::Regular, false; "not interrupt without object")]
414 fn test_notify_then_wait_nonblock(kind: NotifyKind, with_object: bool) {
415 const KEY: u64 = 5;
416
417 let event = PortEvent::new();
418 let object = zx::Event::create();
419 if with_object {
420 assert_eq!(
421 event.object_wait_async(
422 &object,
423 KEY,
424 zx::Signals::USER_4,
425 zx::WaitAsyncOpts::empty()
426 ),
427 Ok(())
428 );
429 }
430
431 event.notify(kind);
432 assert_eq!(
433 event.wait(zx::MonotonicInstant::INFINITE_PAST),
434 PortWaitResult::Notification { kind }
435 );
436 }
437
438 #[test_case(true, zx::MonotonicInstant::after(zx::MonotonicDuration::from_millis(100)); "blocking with object")]
439 #[test_case(false, zx::MonotonicInstant::after(zx::MonotonicDuration::from_millis(100)); "blocking without object")]
440 #[test_case(true, zx::MonotonicInstant::INFINITE_PAST; "non blocking with object")]
441 #[test_case(false, zx::MonotonicInstant::INFINITE_PAST; "non blocking without object")]
442 #[test_case(true, zx::MonotonicInstant::ZERO; "zero deadline with object")]
443 #[test_case(false, zx::MonotonicInstant::ZERO; "zero deadline without object")]
444 fn test_wait_timeout(with_object: bool, deadline: zx::MonotonicInstant) {
445 const KEY: u64 = 6;
446 const ASSERTED_SIGNAL: zx::Signals = zx::Signals::USER_5;
447
448 let event = PortEvent::new();
449 let object = zx::Event::create();
450
451 if with_object {
452 assert_eq!(
453 event.object_wait_async(&object, KEY, ASSERTED_SIGNAL, zx::WaitAsyncOpts::empty()),
454 Ok(())
455 );
456 }
457 assert_eq!(event.wait(deadline), PortWaitResult::TimedOut);
458 }
459
460 #[test_case(NotifyKind::Interrupt, true; "interrupt with object")]
461 #[test_case(NotifyKind::Regular, true; "not interrupt with object")]
462 #[test_case(NotifyKind::Interrupt, false; "interrupt without object")]
463 #[test_case(NotifyKind::Regular, false; "not interrupt without object")]
464 fn test_zero_deadline_timeout_then_notify(kind: NotifyKind, with_object: bool) {
465 const KEY: u64 = 9;
466
467 let event = PortEvent::new();
468 let object = zx::Event::create();
469 if with_object {
470 assert_eq!(
471 event.object_wait_async(
472 &object,
473 KEY,
474 zx::Signals::USER_4,
475 zx::WaitAsyncOpts::empty()
476 ),
477 Ok(())
478 );
479 }
480
481 assert_eq!(event.wait(zx::MonotonicInstant::ZERO), PortWaitResult::TimedOut);
482 event.notify(kind);
483 assert_eq!(event.wait(zx::MonotonicInstant::ZERO), PortWaitResult::Notification { kind });
484 assert_eq!(event.wait(zx::MonotonicInstant::ZERO), PortWaitResult::TimedOut);
485 }
486
487 #[test_case(true; "with object")]
488 #[test_case(false; "without object")]
489 fn test_wait_clean_stack_then_notify(with_object: bool) {
490 const KEY: u64 = 7;
491
492 let event = Arc::new(PortEvent::new());
493 let object = zx::Event::create();
494 if with_object {
495 assert_eq!(
496 event.object_wait_async(
497 &object,
498 KEY,
499 zx::Signals::USER_6,
500 zx::WaitAsyncOpts::empty()
501 ),
502 Ok(())
503 );
504 }
505
506 let event_clone = Arc::clone(&event);
507 let thread = std::thread::spawn(move || {
508 assert_eq!(
509 event_clone.wait_with_clean_delay(
510 zx::MonotonicInstant::INFINITE,
511 zx::MonotonicDuration::from_millis(10),
512 ),
513 PortWaitResult::NOTIFY_REGULAR
514 );
515 });
516
517 std::thread::sleep(std::time::Duration::from_millis(30));
518 event.notify(NotifyKind::Regular);
519 thread.join().expect("join thread");
520 }
521
522 #[test_case(true; "with object")]
523 #[test_case(false; "without object")]
524 fn test_wait_clean_stack_then_timeout(with_object: bool) {
525 const KEY: u64 = 8;
526
527 let event = PortEvent::new();
528 let object = zx::Event::create();
529 if with_object {
530 assert_eq!(
531 event.object_wait_async(
532 &object,
533 KEY,
534 zx::Signals::USER_7,
535 zx::WaitAsyncOpts::empty()
536 ),
537 Ok(())
538 );
539 }
540
541 let deadline = zx::MonotonicInstant::after(zx::MonotonicDuration::from_millis(30));
542 assert_eq!(
543 event.wait_with_clean_delay(deadline, zx::MonotonicDuration::from_millis(10)),
544 PortWaitResult::TimedOut
545 );
546 assert!(zx::MonotonicInstant::get() >= deadline);
547 }
548}