1use crate::mm::memory::MemoryObject;
6use crate::mm::{CompareExchangeResult, MemoryManager, ProtectionFlags};
7use crate::task::{
8 CurrentTask, EventHandler, NormalPriority, SignalHandler, SignalHandlerInner, Task, Waiter,
9};
10use futures::channel::oneshot;
11use rustc_hash::FxHasher;
12use starnix_sync::{FutexTableStateLock, InterruptibleEvent, LockDepMutex, ordered_lock};
13use starnix_types::futex_address::FutexAddress;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::user_address::UserAddress;
16use starnix_uapi::{FUTEX_BITSET_MATCH_ANY, FUTEX_TID_MASK, FUTEX_WAITERS, errno, error};
17use std::cell::Cell;
18use std::hash::{Hash, Hasher};
19use std::marker::PhantomPinned;
20use std::ptr::NonNull;
21use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU64, AtomicUsize, Ordering};
22use std::sync::{Arc, Weak};
23
24const NUM_BUCKETS: usize = 1024;
26
27const RT_MUTEX_MASK_WORDS: usize = NUM_BUCKETS / 64;
29
30const MAX_RT_MUTEX_CHAIN_DEPTH: usize = 16;
32
33#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
35pub struct FutexKeyValue {
36 id_and_tag: u64,
39 offset: u64,
42}
43
44impl FutexKeyValue {
45 #[inline]
46 fn private(mm: &Arc<MemoryManager>, addr: FutexAddress) -> Self {
47 let mm_ptr = Arc::as_ptr(mm) as usize as u64;
53 debug_assert_eq!(mm_ptr & 1, 0);
54 Self { id_and_tag: mm_ptr | 1, offset: addr.ptr() as u64 }
55 }
56
57 #[inline]
58 fn shared(memory: &MemoryObject, offset: u64) -> Self {
59 Self { id_and_tag: memory.get_koid().raw_koid() << 1, offset }
62 }
63}
64
65pub struct FutexTable {
73 buckets: Box<[LockDepMutex<FutexBucket, FutexTableStateLock>; NUM_BUCKETS]>,
74 active_rt_mutex_waiters: AtomicUsize,
75 rt_mutex_bucket_mask: [AtomicU64; RT_MUTEX_MASK_WORDS],
76}
77
78impl Default for FutexTable {
79 fn default() -> Self {
80 let buckets: Vec<LockDepMutex<FutexBucket, FutexTableStateLock>> =
81 std::iter::repeat_with(|| LockDepMutex::new(FutexBucket::default()))
82 .take(NUM_BUCKETS)
83 .collect();
84 Self {
85 buckets: buckets.into_boxed_slice().try_into().unwrap_or_else(|_| unreachable!()),
86 active_rt_mutex_waiters: AtomicUsize::new(0),
87 rt_mutex_bucket_mask: std::array::from_fn(|_| AtomicU64::new(0)),
88 }
89 }
90}
91
92impl FutexTable {
93 #[inline]
94 fn set_rt_mutex_bucket_active(&self, b: usize) {
95 self.rt_mutex_bucket_mask[b / 64].fetch_or(1u64 << (b % 64), Ordering::Relaxed);
96 }
97
98 #[inline]
99 fn clear_rt_mutex_bucket_active(&self, b: usize) {
100 self.rt_mutex_bucket_mask[b / 64].fetch_and(!(1u64 << (b % 64)), Ordering::Relaxed);
101 }
102
103 #[inline]
104 fn on_rt_mutex_waiter_removed(&self, b: usize, bucket: &FutexBucket) {
105 if bucket.rt_mutex_waiters.head.is_none() {
106 self.clear_rt_mutex_bucket_active(b);
107 }
108 self.active_rt_mutex_waiters.fetch_sub(1, Ordering::Relaxed);
109 }
110
111 fn pop_rt_mutex_waiter(
112 &self,
113 b: usize,
114 bucket: &mut FutexBucket,
115 key: &FutexKeyValue,
116 ) -> Option<UnattachedWaiter> {
117 let popped = bucket.pop_rt_mutex_waiter(key);
118 if popped.is_some() {
119 self.on_rt_mutex_waiter_removed(b, bucket);
120 }
121 popped
122 }
123
124 #[inline]
125 fn bucket_index(key: &FutexKeyValue) -> usize {
126 let mut hasher = FxHasher::default();
127 key.hash(&mut hasher);
128 (hasher.finish() as usize) & (NUM_BUCKETS - 1)
129 }
130
131 pub fn wait_boot<Key: FutexKey>(
135 &self,
136 current_task: &CurrentTask,
137 addr: UserAddress,
138 value: u32,
139 mask: u32,
140 deadline: zx::BootInstant,
141 timer_slack: zx::BootDuration,
142 ) -> Result<(), Errno> {
143 let addr = FutexAddress::try_from(addr)?;
144 let mm = current_task.mm()?;
145 if mm.atomic_load_u32_acquire(addr)? != value {
150 return error!(EAGAIN);
151 }
152 let key = Key::get(&mm, addr)?;
153 if deadline != zx::BootInstant::INFINITE && deadline <= zx::BootInstant::get() {
155 return error!(ETIMEDOUT);
156 }
157 let b = Self::bucket_index(&key);
158 let prio = task_futex_priority(current_task);
159
160 let node = WaiterNode::new_stack_boot(key, mask, prio, b as u16);
161 let timer = zx::BootTimer::create_with_slack_mode(zx::TimerSlackMode::Late);
162 let signal_handler = SignalHandler {
163 inner: SignalHandlerInner::None,
164 event_handler: EventHandler::None,
165 err_code: Some(errno!(ETIMEDOUT)),
166 };
167 node.waiter()
168 .wake_on_zircon_signals(&timer, zx::Signals::TIMER_SIGNALED, signal_handler)
169 .expect("wait can only fail in OOM conditions");
170 timer
171 .set(deadline, timer_slack)
172 .expect("timer set cannot fail with valid handles and slack");
173
174 let mut bucket = self.buckets[b].lock();
175 let loaded_value = mm.atomic_load_u32_acquire(addr)?;
180 if value != loaded_value {
181 return error!(EAGAIN);
182 }
183
184 let queued_guard = StackWaiterGuard::insert_waiter(self, &mut bucket, &node);
185 std::mem::drop(bucket);
186
187 let result = node.waiter().wait(current_task);
188 queued_guard.resolve(result)
189 }
190
191 pub fn wait<Key: FutexKey>(
195 &self,
196 current_task: &CurrentTask,
197 addr: UserAddress,
198 value: u32,
199 mask: u32,
200 deadline: zx::MonotonicInstant,
201 ) -> Result<(), Errno> {
202 let addr = FutexAddress::try_from(addr)?;
203 let mm = current_task.mm()?;
204 if mm.atomic_load_u32_acquire(addr)? != value {
209 return error!(EAGAIN);
210 }
211
212 let key = Key::get(&mm, addr)?;
213 if deadline != zx::MonotonicInstant::INFINITE && deadline <= zx::MonotonicInstant::get() {
215 return error!(ETIMEDOUT);
216 }
217 let b = Self::bucket_index(&key);
218 let prio = task_futex_priority(current_task);
219 let node = WaiterNode::new_stack_event(key, mask, prio, 0, b as u16);
220 let event_guard = node.event().begin_wait();
221
222 let mut bucket = self.buckets[b].lock();
223 let loaded_value = mm.atomic_load_u32_acquire(addr)?;
228 if value != loaded_value {
229 return error!(EAGAIN);
230 }
231
232 let queued_guard = StackWaiterGuard::insert_waiter(self, &mut bucket, &node);
233 std::mem::drop(bucket);
234
235 let result = current_task.block_until(event_guard, deadline);
236 queued_guard.resolve(result)
237 }
238
239 pub fn wake<Key: FutexKey>(
244 &self,
245 task: &Task,
246 addr: UserAddress,
247 count: usize,
248 mask: u32,
249 ) -> Result<usize, Errno> {
250 let addr = FutexAddress::try_from(addr)?;
251 let mm = task.mm()?;
252 let key = Key::get(&mm, addr)?;
253 let b = Self::bucket_index(&key);
254 let woken = self.buckets[b].lock().wake(&key, count, mask);
255 Ok(woken)
256 }
257
258 pub fn requeue<Key: FutexKey>(
262 &self,
263 current_task: &CurrentTask,
264 addr: UserAddress,
265 wake_count: usize,
266 requeue_count: usize,
267 new_addr: UserAddress,
268 expected_value: Option<u32>,
269 ) -> Result<usize, Errno> {
270 let addr = FutexAddress::try_from(addr)?;
271 let new_addr = FutexAddress::try_from(new_addr)?;
272 let mm = current_task.mm()?;
273 if expected_value.is_some() {
274 let _ = mm.atomic_load_u32_acquire(addr)?;
279 }
280 let key = Key::get(&mm, addr)?;
281 let new_key = Key::get(&mm, new_addr)?;
282 self.requeue_by_key(key, new_key, wake_count, requeue_count, || {
283 if let Some(expected) = expected_value {
284 let value = mm.atomic_load_u32_acquire(addr)?;
287 if value != expected {
288 return error!(EAGAIN);
289 }
290 }
291 Ok(())
292 })
293 }
294
295 fn requeue_by_key(
296 &self,
297 key: FutexKeyValue,
298 new_key: FutexKeyValue,
299 wake_count: usize,
300 requeue_count: usize,
301 check_expected: impl FnOnce() -> Result<(), Errno>,
302 ) -> Result<usize, Errno> {
303 let b1 = Self::bucket_index(&key);
304 let b2 = Self::bucket_index(&new_key);
305 if b1 == b2 {
306 let mut bucket = self.buckets[b1].lock();
307 check_expected()?;
308 let woken = bucket.wake(&key, wake_count, FUTEX_BITSET_MATCH_ANY);
310 let (moved, count) = bucket.take_for_requeue(&key, requeue_count);
312 bucket.requeue_from(moved, new_key, b1 as u16);
313 Ok(woken + count)
314 } else {
315 let (mut bucket1, mut bucket2) = ordered_lock(&self.buckets[b1], &self.buckets[b2]);
316 check_expected()?;
317 let woken = bucket1.wake(&key, wake_count, FUTEX_BITSET_MATCH_ANY);
319 let (moved, count) = bucket1.take_for_requeue(&key, requeue_count);
321 bucket2.requeue_from(moved, new_key, b2 as u16);
322 Ok(woken + count)
323 }
324 }
325
326 pub fn get_rt_mutex_priority_boost(&self, owner_tid: u32) -> Option<i64> {
329 let mut visited = [0u32; MAX_RT_MUTEX_CHAIN_DEPTH];
330 self.get_rt_mutex_priority_boost_inner(owner_tid, 0, &mut visited)
331 }
332
333 fn get_rt_mutex_priority_boost_inner(
334 &self,
335 owner_tid: u32,
336 depth: usize,
337 visited: &mut [u32; MAX_RT_MUTEX_CHAIN_DEPTH],
338 ) -> Option<i64> {
339 if depth >= MAX_RT_MUTEX_CHAIN_DEPTH
340 || self.active_rt_mutex_waiters.load(Ordering::Acquire) == 0
341 {
342 return None;
343 }
344 visited[depth] = owner_tid;
345 let mut min_prio: Option<i64> = None;
346 for (word_idx, mask_word) in self.rt_mutex_bucket_mask.iter().enumerate() {
347 let mut mask = mask_word.load(Ordering::Relaxed);
348 while mask != 0 {
349 let bit = mask.trailing_zeros() as usize;
350 mask &= mask - 1;
351 let b = word_idx * 64 + bit;
352 if let Some(bucket_top) = self.get_rt_mutex_top_waiter(b, owner_tid, depth, visited)
353 {
354 min_prio =
355 Some(min_prio.map_or(bucket_top, |cur| std::cmp::min(cur, bucket_top)));
356 }
357 }
358 }
359 min_prio
360 }
361
362 fn get_rt_mutex_top_waiter(
366 &self,
367 b: usize,
368 owner_tid: u32,
369 depth: usize,
370 visited: &mut [u32; MAX_RT_MUTEX_CHAIN_DEPTH],
371 ) -> Option<i64> {
372 let waiters = self.buckets[b].lock().get_rt_mutex_waiters(owner_tid);
373 if waiters.is_empty() {
374 return None;
375 }
376 let mut top_per_key: Vec<(FutexKeyValue, (i64, i64))> = Vec::new();
377 for (key, mut task_prio, waiter_tid) in waiters {
378 if visited[..=depth].contains(&waiter_tid) {
379 continue;
380 }
381 if let Some(transitive_boost) =
382 self.get_rt_mutex_priority_boost_inner(waiter_tid, depth + 1, visited)
383 {
384 task_prio = std::cmp::min(task_prio, transitive_boost);
385 }
386 let waiter_prio = rt_mutex_waiter_prio_from_task_prio(task_prio);
387 if let Some((_, cur)) = top_per_key.iter_mut().find(|(k, _)| *k == key) {
388 if waiter_prio < cur.0 {
389 *cur = (waiter_prio, task_prio);
390 }
391 } else {
392 top_per_key.push((key, (waiter_prio, task_prio)));
393 }
394 }
395 top_per_key.into_iter().map(|(_, (_, task_prio))| task_prio).min()
396 }
397
398 pub fn lock_pi<Key: FutexKey>(
402 &self,
403 current_task: &CurrentTask,
404 addr: UserAddress,
405 deadline: zx::MonotonicInstant,
406 ) -> Result<(), Errno> {
407 let addr = FutexAddress::try_from(addr)?;
408 let mm = current_task.mm()?;
409 let tid = current_task.get_tid() as u32;
410 if let Ok(current) = mm.atomic_load_u32_relaxed(addr) {
416 if current & FUTEX_TID_MASK == tid {
417 return error!(EDEADLOCK);
418 }
419 if current == 0 {
420 if matches!(
421 mm.atomic_compare_exchange_u32_acq_rel(addr, 0, tid),
422 CompareExchangeResult::Success
423 ) {
424 return Ok(());
425 }
426 } else {
427 let _ = mm.atomic_compare_exchange_u32_acq_rel(addr, current, current);
428 }
429 }
430 let priority = current_task.scheduler_state.lock().task_priority();
431 let key = Key::get(&mm, addr)?;
432 let b = Self::bucket_index(&key);
433 let node = WaiterNode::new_stack_rt_mutex(key, tid, priority, b as u16);
434 let event_guard = node.event().begin_wait();
435
436 let mut bucket = self.buckets[b].lock();
437 let mut current_value = mm.atomic_load_u32_relaxed(addr)?;
443 let new_owner_tid = loop {
444 let new_owner_tid = current_value & FUTEX_TID_MASK;
445 if new_owner_tid == tid {
446 return error!(EDEADLOCK);
453 }
454
455 if current_value == 0 {
456 match mm.atomic_compare_exchange_weak_u32_acq_rel(addr, current_value, tid) {
459 CompareExchangeResult::Success => return Ok(()),
460 CompareExchangeResult::Stale { observed } => {
461 current_value = observed;
462 continue;
463 }
464 CompareExchangeResult::Error(e) => return Err(e),
465 }
466 }
467
468 let target_value = current_value | FUTEX_WAITERS;
471 match mm.atomic_compare_exchange_u32_acq_rel(addr, current_value, target_value) {
472 CompareExchangeResult::Success => (),
473 CompareExchangeResult::Stale { observed } => {
474 current_value = observed;
475 continue;
476 }
477 CompareExchangeResult::Error(e) => return Err(e),
478 }
479 break new_owner_tid;
480 };
481
482 node.owner_tid.set(new_owner_tid);
483 let queued_guard = StackWaiterGuard::insert_rt_mutex(self, &mut bucket, &node);
484 std::mem::drop(bucket);
485
486 let result = current_task
490 .get_task(new_owner_tid as i32)
491 .ok()
492 .and_then(|o| o.running_state().unwrap().thread.get().map(|t| Arc::clone(&t.thread)))
493 .map_or_else(
494 || error!(ESRCH),
495 |owner| current_task.block_with_owner_until(event_guard, &owner, deadline),
496 );
497
498 queued_guard.resolve(result)
503 }
504
505 pub fn unlock_pi<Key: FutexKey>(
509 &self,
510 current_task: &CurrentTask,
511 addr: UserAddress,
512 ) -> Result<(), Errno> {
513 let addr = FutexAddress::try_from(addr)?;
514 let mm = current_task.mm()?;
515 if let Ok(current) = mm.atomic_load_u32_relaxed(addr) {
521 let _ = mm.atomic_compare_exchange_u32_acq_rel(addr, current, current);
522 }
523 let tid = current_task.get_tid() as u32;
524 let key = Key::get(&mm, addr)?;
525 let b = Self::bucket_index(&key);
526 let mut bucket = self.buckets[b].lock();
527
528 let expected_value = mm.atomic_load_u32_relaxed(addr)?;
532 if expected_value & FUTEX_TID_MASK != tid {
533 return error!(EPERM);
538 }
539
540 let target_value = bucket.peek_rt_mutex_target_value(&key);
541
542 let handoff = mm.atomic_compare_exchange_u32_acq_rel(addr, expected_value, target_value);
545 handoff_result(handoff)?;
546
547 if let Some(node) = self.pop_rt_mutex_waiter(b, &mut bucket, &key) {
548 unsafe {
550 bucket.wake_and_mark_woken(node);
551 }
552 }
553
554 Ok(())
555 }
556
557 pub fn external_wait(
567 &self,
568 memory: MemoryObject,
569 offset: u64,
570 value: u32,
571 mask: u32,
572 ) -> Result<(Arc<()>, oneshot::Receiver<()>), Errno> {
573 let key = FutexKeyValue::shared(&memory, offset);
574 let b = Self::bucket_index(&key);
575 let mut bucket = self.buckets[b].lock();
576 bucket.prune_stale_external();
577 Self::external_check_futex_value(&memory, offset, value)?;
579
580 let token = Arc::new(());
581 let (sender, receiver) = oneshot::channel::<()>();
582 let waiter =
583 UnattachedWaiter::new_external(key, mask, b as u16, Arc::downgrade(&token), sender);
584 bucket.waiters.insert_by_prio(waiter);
585 Ok((token, receiver))
586 }
587
588 pub fn external_wake(
593 &self,
594 memory: MemoryObject,
595 offset: u64,
596 count: usize,
597 mask: u32,
598 ) -> Result<usize, Errno> {
599 let key = FutexKeyValue::shared(&memory, offset);
600 let b = Self::bucket_index(&key);
601 let woken = self.buckets[b].lock().wake(&key, count, mask);
602 Ok(woken)
603 }
604
605 pub fn external_requeue(
606 &self,
607 first_memory: MemoryObject,
608 first_offset: u64,
609 second_memory: Option<MemoryObject>,
610 second_offset: u64,
611 wake_count: usize,
612 requeue_count: usize,
613 expected_value: Option<u32>,
614 ) -> Result<usize, Errno> {
615 let first_key = FutexKeyValue::shared(&first_memory, first_offset);
616 let second_key = match second_memory.as_ref() {
617 Some(second_memory) => FutexKeyValue::shared(second_memory, second_offset),
618 None => FutexKeyValue::shared(&first_memory, second_offset),
619 };
620 self.requeue_by_key(first_key, second_key, wake_count, requeue_count, || {
621 if let Some(expected) = expected_value {
622 Self::external_check_futex_value(&first_memory, first_offset, expected)?;
624 }
625 Ok(())
626 })
627 }
628
629 fn external_check_futex_value(
630 memory: &MemoryObject,
631 offset: u64,
632 value: u32,
633 ) -> Result<(), Errno> {
634 let loaded_value = {
635 let mut buf = [0u8; 4];
637 memory.read(&mut buf, offset).map_err(|_| errno!(EINVAL))?;
638 u32::from_ne_bytes(buf)
639 };
640 if loaded_value != value {
641 return error!(EAGAIN);
642 }
643 Ok(())
644 }
645}
646
647fn handoff_result(result: CompareExchangeResult<u32>) -> Result<(), Errno> {
649 match result {
650 CompareExchangeResult::Success => Ok(()),
651 CompareExchangeResult::Stale { .. } => error!(EINVAL),
660 CompareExchangeResult::Error(_) => error!(EACCES),
664 }
665}
666
667pub trait FutexKey: Sized {
668 fn get(mm: &Arc<MemoryManager>, addr: FutexAddress) -> Result<FutexKeyValue, Errno>;
669}
670
671pub struct PrivateFutexKey;
672
673impl FutexKey for PrivateFutexKey {
674 fn get(mm: &Arc<MemoryManager>, addr: FutexAddress) -> Result<FutexKeyValue, Errno> {
675 Ok(FutexKeyValue::private(mm, addr))
676 }
677}
678
679pub struct SharedFutexKey;
680
681impl FutexKey for SharedFutexKey {
682 fn get(mm: &Arc<MemoryManager>, addr: FutexAddress) -> Result<FutexKeyValue, Errno> {
683 let (memory, offset) = mm.get_mapping_memory(addr.into(), ProtectionFlags::READ)?;
684 Ok(FutexKeyValue::shared(&memory, offset))
685 }
686}
687
688fn task_futex_priority(task: &Task) -> u8 {
691 task.scheduler_state.lock().futex_priority()
692}
693
694#[inline]
697fn rt_mutex_waiter_prio_from_task_prio(task_prio: i64) -> i64 {
698 if task_prio < 0 { task_prio } else { 20 }
699}
700
701enum WaiterKind {
702 StackEvent(InterruptibleEvent),
704 StackBoot(Waiter),
706 External { token: Weak<()>, sender: oneshot::Sender<()> },
708}
709
710struct WaiterNode {
716 prev: Cell<Option<NonNull<WaiterNode>>>,
717 next: Cell<Option<NonNull<WaiterNode>>>,
718 key: Cell<FutexKeyValue>,
719 mask: u32,
720 prio: u8,
723 task_prio: i64,
726 owner_tid: Cell<u32>,
728 attached: Cell<bool>,
729 woken: AtomicBool,
730 bucket_idx: AtomicU16,
731 tid: Cell<u32>,
733 kind: WaiterKind,
734 _pin: PhantomPinned,
735}
736
737impl WaiterNode {
738 fn new_stack_event(key: FutexKeyValue, mask: u32, prio: u8, tid: u32, bucket_idx: u16) -> Self {
739 Self {
740 prev: Cell::new(None),
741 next: Cell::new(None),
742 key: Cell::new(key),
743 mask,
744 prio,
745 task_prio: 20,
746 owner_tid: Cell::new(0),
747 attached: Cell::new(false),
748 woken: AtomicBool::new(false),
749 bucket_idx: AtomicU16::new(bucket_idx),
750 tid: Cell::new(tid),
751 kind: WaiterKind::StackEvent(InterruptibleEvent::default()),
752 _pin: PhantomPinned,
753 }
754 }
755
756 fn new_stack_rt_mutex(key: FutexKeyValue, tid: u32, task_prio: i64, bucket_idx: u16) -> Self {
757 Self {
758 prev: Cell::new(None),
759 next: Cell::new(None),
760 key: Cell::new(key),
761 mask: u32::MAX,
762 prio: 0,
763 task_prio,
764 owner_tid: Cell::new(0),
765 attached: Cell::new(false),
766 woken: AtomicBool::new(false),
767 bucket_idx: AtomicU16::new(bucket_idx),
768 tid: Cell::new(tid),
769 kind: WaiterKind::StackEvent(InterruptibleEvent::default()),
770 _pin: PhantomPinned,
771 }
772 }
773
774 fn new_stack_boot(key: FutexKeyValue, mask: u32, prio: u8, bucket_idx: u16) -> Self {
775 Self {
776 prev: Cell::new(None),
777 next: Cell::new(None),
778 key: Cell::new(key),
779 mask,
780 prio,
781 task_prio: 20,
782 owner_tid: Cell::new(0),
783 attached: Cell::new(false),
784 woken: AtomicBool::new(false),
785 bucket_idx: AtomicU16::new(bucket_idx),
786 tid: Cell::new(0),
787 kind: WaiterKind::StackBoot(Waiter::new()),
788 _pin: PhantomPinned,
789 }
790 }
791
792 fn new_external(
793 key: FutexKeyValue,
794 mask: u32,
795 bucket_idx: u16,
796 token: Weak<()>,
797 sender: oneshot::Sender<()>,
798 ) -> Self {
799 Self {
800 prev: Cell::new(None),
801 next: Cell::new(None),
802 key: Cell::new(key),
803 mask,
804 prio: NormalPriority::default().raw_priority(),
805 task_prio: 20,
806 owner_tid: Cell::new(0),
807 attached: Cell::new(false),
808 woken: AtomicBool::new(false),
809 bucket_idx: AtomicU16::new(bucket_idx),
810 tid: Cell::new(0),
811 kind: WaiterKind::External { token, sender },
812 _pin: PhantomPinned,
813 }
814 }
815
816 fn rt_mutex_waiter_prio(&self) -> i64 {
821 rt_mutex_waiter_prio_from_task_prio(self.task_prio)
822 }
823
824 fn event(&self) -> &InterruptibleEvent {
825 match &self.kind {
826 WaiterKind::StackEvent(event) => event,
827 _ => unreachable!(),
828 }
829 }
830
831 fn waiter(&self) -> &Waiter {
832 match &self.kind {
833 WaiterKind::StackBoot(waiter) => waiter,
834 _ => unreachable!(),
835 }
836 }
837
838 fn is_stale_external(&self) -> bool {
839 matches!(&self.kind, WaiterKind::External { token, .. } if token.strong_count() == 0)
840 }
841}
842
843#[must_use = "an UnattachedWaiter must be inserted, woken, or freed"]
851struct UnattachedWaiter(NonNull<WaiterNode>);
852
853impl UnattachedWaiter {
854 fn new_external(
856 key: FutexKeyValue,
857 mask: u32,
858 bucket_idx: u16,
859 token: Weak<()>,
860 sender: oneshot::Sender<()>,
861 ) -> Self {
862 Self(Box::into_non_null(Box::new(WaiterNode::new_external(
863 key, mask, bucket_idx, token, sender,
864 ))))
865 }
866
867 unsafe fn from_stack(node: &WaiterNode) -> Self {
875 assert!(!node.attached.get(), "WaiterNode is already attached");
876 Self(NonNull::from(node))
877 }
878
879 fn into_raw(self) -> NonNull<WaiterNode> {
880 std::mem::ManuallyDrop::new(self).0
881 }
882
883 fn set_requeued(&mut self, _bucket: &mut FutexBucket, new_key: FutexKeyValue, new_bucket: u16) {
885 unsafe {
887 let node = self.0.as_ref();
888 node.key.set(new_key);
889 node.bucket_idx.store(new_bucket, Ordering::Relaxed);
890 }
891 }
892
893 fn tid(&self) -> u32 {
894 unsafe { self.0.as_ref().tid.get() }
896 }
897
898 fn set_tid(&mut self, _bucket: &mut FutexBucket, tid: u32) {
899 unsafe {
901 self.0.as_ref().tid.set(tid);
902 }
903 }
904
905 fn into_removed_stack_node(self) {
908 let _ = self.into_raw();
910 }
911}
912
913impl Drop for UnattachedWaiter {
914 fn drop(&mut self) {
915 debug_assert!(false, "UnattachedWaiter dropped without being consumed");
916 }
917}
918
919struct WaiterList {
921 head: Option<NonNull<WaiterNode>>,
922 tail: Option<NonNull<WaiterNode>>,
923}
924
925unsafe impl Send for WaiterList {}
928
929impl Default for WaiterList {
930 fn default() -> Self {
931 Self { head: None, tail: None }
932 }
933}
934
935impl Drop for WaiterList {
936 fn drop(&mut self) {
937 let mut cur = self.head;
939 while let Some(node_ptr) = cur {
940 unsafe {
942 let is_external = {
943 let node = node_ptr.as_ref();
944 cur = node.next.get();
945 matches!(node.kind, WaiterKind::External { .. })
946 };
947 debug_assert!(
948 is_external,
949 "stack-allocated WaiterNode must not remain linked when WaiterList is dropped"
950 );
951 if is_external {
952 drop(Box::from_raw(node_ptr.as_ptr()));
953 }
954 }
955 }
956 }
957}
958
959impl WaiterList {
960 #[cfg(test)]
961 fn is_empty(&self) -> bool {
962 self.head.is_none()
963 }
964
965 #[cfg(test)]
966 fn len(&self) -> usize {
967 let mut count = 0;
968 let mut cur = self.head;
969 while let Some(node_ptr) = cur {
970 count += 1;
971 cur = unsafe { node_ptr.as_ref().next.get() };
973 }
974 count
975 }
976
977 fn push_back(&mut self, waiter: UnattachedWaiter) {
978 let node_ptr = waiter.into_raw();
979 unsafe {
982 let node = node_ptr.as_ref();
983 let was_attached = node.attached.replace(true);
984 debug_assert!(!was_attached);
985 node.prev.set(self.tail);
986 node.next.set(None);
987 if let Some(tail) = self.tail {
988 tail.as_ref().next.set(Some(node_ptr));
989 } else {
990 self.head = Some(node_ptr);
991 }
992 self.tail = Some(node_ptr);
993 }
994 }
995
996 fn push_front(&mut self, waiter: UnattachedWaiter) {
997 let node_ptr = waiter.into_raw();
998 unsafe {
1001 let node = node_ptr.as_ref();
1002 let was_attached = node.attached.replace(true);
1003 debug_assert!(!was_attached);
1004 node.prev.set(None);
1005 node.next.set(self.head);
1006 if let Some(head) = self.head {
1007 head.as_ref().prev.set(Some(node_ptr));
1008 } else {
1009 self.tail = Some(node_ptr);
1010 }
1011 self.head = Some(node_ptr);
1012 }
1013 }
1014
1015 fn pop_front(&mut self) -> Option<UnattachedWaiter> {
1016 let head = self.head?;
1017 Some(unsafe { self.unlink(head) })
1019 }
1020
1021 fn insert_by_prio(&mut self, waiter: UnattachedWaiter) {
1024 let node_ptr = waiter.0;
1025 unsafe {
1028 let node = node_ptr.as_ref();
1029 let prio = node.prio;
1030 let mut cur = self.tail;
1031 while let Some(c) = cur {
1032 if c.as_ref().prio >= prio {
1033 let _ = waiter.into_raw();
1035 let was_attached = node.attached.replace(true);
1036 debug_assert!(!was_attached);
1037 let next = c.as_ref().next.get();
1038 node.prev.set(Some(c));
1039 node.next.set(next);
1040 c.as_ref().next.set(Some(node_ptr));
1041 if let Some(n) = next {
1042 n.as_ref().prev.set(Some(node_ptr));
1043 } else {
1044 self.tail = Some(node_ptr);
1045 }
1046 return;
1047 }
1048 cur = c.as_ref().prev.get();
1049 }
1050 }
1051 self.push_front(waiter);
1053 }
1054
1055 fn insert_rt_mutex_by_prio(&mut self, waiter: UnattachedWaiter) {
1059 let node_ptr = waiter.0;
1060 unsafe {
1063 let node = node_ptr.as_ref();
1064 let waiter_prio = node.rt_mutex_waiter_prio();
1065 let mut cur = self.tail;
1066 while let Some(c) = cur {
1067 if c.as_ref().rt_mutex_waiter_prio() <= waiter_prio {
1068 let _ = waiter.into_raw();
1070 let was_attached = node.attached.replace(true);
1071 debug_assert!(!was_attached);
1072 let next = c.as_ref().next.get();
1073 node.prev.set(Some(c));
1074 node.next.set(next);
1075 c.as_ref().next.set(Some(node_ptr));
1076 if let Some(n) = next {
1077 n.as_ref().prev.set(Some(node_ptr));
1078 } else {
1079 self.tail = Some(node_ptr);
1080 }
1081 return;
1082 }
1083 cur = c.as_ref().prev.get();
1084 }
1085 }
1086 self.push_front(waiter);
1089 }
1090
1091 unsafe fn unlink(&mut self, node_ptr: NonNull<WaiterNode>) -> UnattachedWaiter {
1096 unsafe {
1098 let node = node_ptr.as_ref();
1099 let was_attached = node.attached.replace(false);
1100 debug_assert!(was_attached);
1101 let prev = node.prev.replace(None);
1102 let next = node.next.replace(None);
1103 if let Some(p) = prev {
1104 p.as_ref().next.set(next);
1105 } else {
1106 self.head = next;
1107 }
1108 if let Some(n) = next {
1109 n.as_ref().prev.set(prev);
1110 } else {
1111 self.tail = prev;
1112 }
1113 }
1114 UnattachedWaiter(node_ptr)
1115 }
1116}
1117
1118#[repr(align(64))]
1119struct FutexBucket {
1120 waiters: WaiterList,
1121 rt_mutex_waiters: WaiterList,
1122}
1123
1124impl Default for FutexBucket {
1125 fn default() -> Self {
1126 Self { waiters: WaiterList::default(), rt_mutex_waiters: WaiterList::default() }
1127 }
1128}
1129
1130impl FutexBucket {
1131 unsafe fn wake_and_mark_woken(&mut self, waiter: UnattachedWaiter) -> bool {
1141 let node_ptr = waiter.into_raw();
1142 unsafe {
1148 let is_external = matches!(node_ptr.as_ref().kind, WaiterKind::External { .. });
1149 if is_external {
1150 let boxed = Box::from_raw(node_ptr.as_ptr());
1151 if let WaiterKind::External { sender, .. } = boxed.kind {
1152 sender.send(()).is_ok()
1153 } else {
1154 unreachable!()
1155 }
1156 } else {
1157 let woken_ptr = &raw const (*node_ptr.as_ptr()).woken;
1158 {
1159 let node = node_ptr.as_ref();
1160 match &node.kind {
1161 WaiterKind::StackEvent(event) => event.notify(),
1162 WaiterKind::StackBoot(waiter) => waiter.notify(),
1163 WaiterKind::External { .. } => unreachable!(),
1164 }
1165 }
1166 (*woken_ptr).store(true, Ordering::Release);
1167 true
1172 }
1173 }
1174 }
1175
1176 unsafe fn free_stale_external(&mut self, waiter: UnattachedWaiter) {
1181 let node_ptr = waiter.into_raw();
1182 unsafe {
1184 debug_assert!(matches!(node_ptr.as_ref().kind, WaiterKind::External { .. }));
1185 drop(Box::from_raw(node_ptr.as_ptr()));
1186 }
1187 }
1188
1189 fn prune_stale_external(&mut self) {
1190 let mut cur = self.waiters.head;
1191 while let Some(node_ptr) = cur {
1192 let is_stale = unsafe {
1194 let node = node_ptr.as_ref();
1195 cur = node.next.get();
1196 node.is_stale_external()
1197 };
1198 if is_stale {
1199 unsafe {
1201 let unattached = self.waiters.unlink(node_ptr);
1202 self.free_stale_external(unattached);
1203 }
1204 }
1205 }
1206 }
1207
1208 fn wake(&mut self, key: &FutexKeyValue, count: usize, mask: u32) -> usize {
1209 let mut woken = 0;
1210 let mut cur = self.waiters.head;
1211 while let Some(node_ptr) = cur {
1212 if woken >= count {
1213 break;
1214 }
1215 let (is_stale, matches) = unsafe {
1218 let node = node_ptr.as_ref();
1219 cur = node.next.get();
1220 (node.is_stale_external(), node.key.get() == *key && (node.mask & mask) != 0)
1221 };
1222
1223 if is_stale {
1224 unsafe {
1226 let unattached = self.waiters.unlink(node_ptr);
1227 self.free_stale_external(unattached);
1228 }
1229 continue;
1230 }
1231
1232 if matches {
1233 let did_wake = unsafe {
1235 let unattached = self.waiters.unlink(node_ptr);
1236 self.wake_and_mark_woken(unattached)
1237 };
1238 if did_wake {
1241 woken += 1;
1242 }
1243 }
1244 }
1245 woken
1246 }
1247
1248 fn take_for_requeue(
1249 &mut self,
1250 key: &FutexKeyValue,
1251 requeue_count: usize,
1252 ) -> (WaiterList, usize) {
1253 let mut out = WaiterList::default();
1254 let mut moved_count = 0;
1255 let mut cur = self.waiters.head;
1256 while let Some(node_ptr) = cur {
1257 if moved_count >= requeue_count {
1258 break;
1259 }
1260 let (is_stale, matches) = unsafe {
1263 let node = node_ptr.as_ref();
1264 cur = node.next.get();
1265 (node.is_stale_external(), node.key.get() == *key)
1266 };
1267
1268 if is_stale {
1269 unsafe {
1271 let unattached = self.waiters.unlink(node_ptr);
1272 self.free_stale_external(unattached);
1273 }
1274 continue;
1275 }
1276
1277 if matches {
1278 let unattached = unsafe { self.waiters.unlink(node_ptr) };
1280 out.push_back(unattached);
1281 moved_count += 1;
1282 }
1283 }
1284 (out, moved_count)
1285 }
1286
1287 fn requeue_from(&mut self, mut moved: WaiterList, new_key: FutexKeyValue, new_bucket_idx: u16) {
1288 while let Some(mut waiter) = moved.pop_front() {
1289 waiter.set_requeued(self, new_key, new_bucket_idx);
1290 self.waiters.insert_by_prio(waiter);
1291 }
1292 }
1293
1294 fn peek_rt_mutex_target_value(&self, key: &FutexKeyValue) -> u32 {
1298 let mut cur = self.rt_mutex_waiters.head;
1299 let mut first_tid = None;
1300 while let Some(node_ptr) = cur {
1301 let matched_tid = unsafe {
1303 let node = node_ptr.as_ref();
1304 cur = node.next.get();
1305 (node.key.get() == *key).then(|| node.tid.get() & FUTEX_TID_MASK)
1306 };
1307 if let Some(tid) = matched_tid {
1308 if let Some(first) = first_tid {
1309 return first | FUTEX_WAITERS;
1310 }
1311 first_tid = Some(tid);
1312 }
1313 }
1314 first_tid.unwrap_or(0)
1315 }
1316
1317 fn pop_rt_mutex_waiter(&mut self, key: &FutexKeyValue) -> Option<UnattachedWaiter> {
1319 let mut cur = self.rt_mutex_waiters.head;
1320 let mut popped = None;
1321 let mut second_match = None;
1322 while let Some(node_ptr) = cur {
1323 let matches = unsafe {
1325 let node = node_ptr.as_ref();
1326 cur = node.next.get();
1327 node.key.get() == *key
1328 };
1329 if matches {
1330 if popped.is_none() {
1331 popped = Some(unsafe { self.rt_mutex_waiters.unlink(node_ptr) });
1333 } else {
1334 second_match = Some(node_ptr);
1335 break;
1336 }
1337 }
1338 }
1339 if let Some(ref mut waiter) = popped {
1340 let new_owner_tid = waiter.tid() & FUTEX_TID_MASK;
1341 if let Some(second_ptr) = second_match {
1342 waiter.set_tid(self, new_owner_tid | FUTEX_WAITERS);
1343 let mut cur = Some(second_ptr);
1344 while let Some(node_ptr) = cur {
1345 unsafe {
1347 let node = node_ptr.as_ref();
1348 cur = node.next.get();
1349 if node.key.get() == *key {
1350 node.owner_tid.set(new_owner_tid);
1351 }
1352 }
1353 }
1354 }
1355 }
1356 popped
1357 }
1358
1359 fn get_rt_mutex_waiters(&self, owner_tid: u32) -> Vec<(FutexKeyValue, i64, u32)> {
1362 let mut waiters = Vec::new();
1363 let mut cur = self.rt_mutex_waiters.head;
1364 while let Some(node_ptr) = cur {
1365 unsafe {
1367 let node = node_ptr.as_ref();
1368 cur = node.next.get();
1369 if node.owner_tid.get() == owner_tid {
1370 waiters.push((node.key.get(), node.task_prio, node.tid.get() & FUTEX_TID_MASK));
1371 }
1372 }
1373 }
1374 waiters
1375 }
1376}
1377
1378struct StackWaiterGuard<'a> {
1382 table: &'a FutexTable,
1383 node: &'a WaiterNode,
1384 is_rt_mutex: bool,
1385 active: bool,
1386}
1387
1388impl<'a> StackWaiterGuard<'a> {
1389 fn insert_waiter(
1390 table: &'a FutexTable,
1391 bucket: &mut FutexBucket,
1392 node: &'a WaiterNode,
1393 ) -> Self {
1394 let unattached = unsafe { UnattachedWaiter::from_stack(node) };
1397 bucket.waiters.insert_by_prio(unattached);
1398 Self { table, node, is_rt_mutex: false, active: true }
1399 }
1400
1401 fn insert_rt_mutex(
1402 table: &'a FutexTable,
1403 bucket: &mut FutexBucket,
1404 node: &'a WaiterNode,
1405 ) -> Self {
1406 let b = node.bucket_idx.load(Ordering::Relaxed) as usize;
1407 let was_empty = bucket.rt_mutex_waiters.head.is_none();
1408 let unattached = unsafe { UnattachedWaiter::from_stack(node) };
1411 bucket.rt_mutex_waiters.insert_rt_mutex_by_prio(unattached);
1412 if was_empty {
1413 table.set_rt_mutex_bucket_active(b);
1414 }
1415 table.active_rt_mutex_waiters.fetch_add(1, Ordering::Release);
1416 Self { table, node, is_rt_mutex: true, active: true }
1417 }
1418
1419 fn unlink_or_confirm_woken(&self) -> bool {
1424 if self.node.woken.load(Ordering::Acquire) {
1428 return false;
1429 }
1430
1431 loop {
1436 let b = self.node.bucket_idx.load(Ordering::Relaxed) as usize;
1437 let mut bucket = self.table.buckets[b].lock();
1438 if self.node.bucket_idx.load(Ordering::Relaxed) as usize != b {
1439 continue;
1441 }
1442
1443 if self.node.woken.load(Ordering::Acquire) {
1444 return false;
1445 }
1446
1447 let list =
1448 if self.is_rt_mutex { &mut bucket.rt_mutex_waiters } else { &mut bucket.waiters };
1449 let unattached = unsafe { list.unlink(NonNull::from(self.node)) };
1452 unattached.into_removed_stack_node();
1453 if self.is_rt_mutex {
1454 self.table.on_rt_mutex_waiter_removed(b, &bucket);
1455 }
1456 return true;
1457 }
1458 }
1459
1460 fn resolve(mut self, result: Result<(), Errno>) -> Result<(), Errno> {
1469 self.active = false;
1470 let was_still_queued = self.unlink_or_confirm_woken();
1471 match result {
1472 Ok(()) => {
1473 debug_assert!(!was_still_queued);
1474 Ok(())
1475 }
1476 Err(e) => {
1477 if was_still_queued {
1478 Err(e)
1479 } else {
1480 Ok(())
1481 }
1482 }
1483 }
1484 }
1485}
1486
1487impl Drop for StackWaiterGuard<'_> {
1488 fn drop(&mut self) {
1489 if self.active {
1490 let _ = self.unlink_or_confirm_woken();
1491 }
1492 }
1493}
1494#[cfg(test)]
1495mod tests {
1496 use super::*;
1497 use starnix_uapi::restricted_aspace::RESTRICTED_ASPACE_BASE;
1498 use starnix_uapi::user_address::UserAddress;
1499
1500 fn test_key(offset: usize) -> FutexKeyValue {
1501 let addr =
1502 FutexAddress::try_from(UserAddress::from((RESTRICTED_ASPACE_BASE + offset) as u64))
1503 .unwrap();
1504 FutexKeyValue { id_and_tag: 1, offset: addr.ptr() as u64 }
1505 }
1506
1507 fn wake_key(table: &FutexTable, key: &FutexKeyValue, count: usize, mask: u32) -> usize {
1508 let b = FutexTable::bucket_index(key);
1509 table.buckets[b].lock().wake(key, count, mask)
1510 }
1511
1512 #[fuchsia::test]
1513 fn test_remove_waiter_simple() {
1514 let table = FutexTable::default();
1515 let key = test_key(0x1000);
1516 let b = FutexTable::bucket_index(&key);
1517 let node = WaiterNode::new_stack_event(key, u32::MAX, 0, 0, b as u16);
1518
1519 let guard = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b].lock(), &node);
1520 assert_eq!(table.buckets[b].lock().waiters.len(), 1);
1521
1522 assert_eq!(guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1523 assert!(table.buckets[b].lock().waiters.is_empty());
1524 }
1525
1526 #[fuchsia::test]
1527 fn test_remove_waiter_requeued() {
1528 let table = FutexTable::default();
1529 let key1 = test_key(0x1000);
1530 let b1 = FutexTable::bucket_index(&key1);
1531 let key2 = (2..4096)
1533 .map(|i| test_key(i * 0x1000))
1534 .find(|k| FutexTable::bucket_index(k) == b1)
1535 .unwrap();
1536
1537 let node = WaiterNode::new_stack_event(key1, u32::MAX, 0, 0, b1 as u16);
1538 let guard = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b1].lock(), &node);
1539
1540 let requeued = table.requeue_by_key(key1, key2, 0, 1, || Ok(())).unwrap();
1541 assert_eq!(requeued, 1);
1542 assert_eq!(table.buckets[b1].lock().waiters.len(), 1);
1543
1544 assert_eq!(guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1545 assert!(table.buckets[b1].lock().waiters.is_empty());
1546 }
1547
1548 #[fuchsia::test]
1549 fn test_remove_waiter_requeued_across_buckets() {
1550 let table = FutexTable::default();
1551 let key1 = test_key(0x1000);
1552 let b1 = FutexTable::bucket_index(&key1);
1554 let key2 = (2..1024)
1555 .map(|i| test_key(i * 0x1000))
1556 .find(|k| FutexTable::bucket_index(k) != b1)
1557 .unwrap();
1558 let b2 = FutexTable::bucket_index(&key2);
1559
1560 let node = WaiterNode::new_stack_event(key1, u32::MAX, 0, 0, b1 as u16);
1561 let guard = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b1].lock(), &node);
1562
1563 let requeued = table.requeue_by_key(key1, key2, 0, 1, || Ok(())).unwrap();
1564 assert_eq!(requeued, 1);
1565 assert!(table.buckets[b1].lock().waiters.is_empty());
1566 assert_eq!(table.buckets[b2].lock().waiters.len(), 1);
1567
1568 assert_eq!(guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1569 assert!(table.buckets[b2].lock().waiters.is_empty());
1570 }
1571
1572 #[fuchsia::test]
1578 fn test_wake_racing_with_interruption_is_not_lost() {
1579 let table = FutexTable::default();
1580 let key = test_key(0x1000);
1581 let b = FutexTable::bucket_index(&key);
1582 let node = WaiterNode::new_stack_event(key, u32::MAX, 0, 0, b as u16);
1583 let _event_guard = node.event().begin_wait();
1584
1585 let guard = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b].lock(), &node);
1586
1587 node.event().interrupt();
1589
1590 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
1592
1593 assert_eq!(guard.resolve(error!(EINTR)), Ok(()));
1596 }
1597
1598 #[fuchsia::test]
1600 fn test_wake_racing_with_boot_waiter_removal_is_not_lost() {
1601 let table = FutexTable::default();
1602 let key = test_key(0x1000);
1603 let b = FutexTable::bucket_index(&key);
1604 let node = WaiterNode::new_stack_boot(key, u32::MAX, 0, b as u16);
1605
1606 let guard = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b].lock(), &node);
1607
1608 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
1611 assert_eq!(guard.resolve(error!(ETIMEDOUT)), Ok(()));
1612 }
1613
1614 #[fuchsia::test]
1617 fn test_remove_waiter_ignores_other_waiter_kinds() {
1618 let table = FutexTable::default();
1619 let key = test_key(0x1000);
1620 let b = FutexTable::bucket_index(&key);
1621
1622 let event_node = WaiterNode::new_stack_event(key, u32::MAX, 0, 0, b as u16);
1623 let boot_node = WaiterNode::new_stack_boot(key, u32::MAX, 0, b as u16);
1624
1625 let (event_guard, boot_guard) = {
1626 let mut bucket = table.buckets[b].lock();
1627 (
1628 StackWaiterGuard::insert_waiter(&table, &mut bucket, &event_node),
1629 StackWaiterGuard::insert_waiter(&table, &mut bucket, &boot_node),
1630 )
1631 };
1632 assert_eq!(table.buckets[b].lock().waiters.len(), 2);
1633
1634 assert_eq!(event_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1635 assert_eq!(table.buckets[b].lock().waiters.len(), 1);
1636
1637 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
1638 assert_eq!(boot_guard.resolve(error!(ETIMEDOUT)), Ok(()));
1639 assert!(table.buckets[b].lock().waiters.is_empty());
1640 }
1641
1642 #[fuchsia::test]
1643 fn test_remove_rt_mutex_waiter() {
1644 let table = FutexTable::default();
1645 let key = test_key(0x1000);
1646 let b = FutexTable::bucket_index(&key);
1647 let node = WaiterNode::new_stack_rt_mutex(key, 1, 20, b as u16);
1648
1649 let guard = StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b].lock(), &node);
1650 assert_eq!(table.buckets[b].lock().rt_mutex_waiters.len(), 1);
1651
1652 assert_eq!(guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1653 assert!(table.buckets[b].lock().rt_mutex_waiters.is_empty());
1654 }
1655
1656 #[fuchsia::test]
1661 fn test_remove_rt_mutex_waiter_reports_whether_it_was_queued() {
1662 let table = FutexTable::default();
1663 let key = test_key(0x1000);
1664 let b = FutexTable::bucket_index(&key);
1665
1666 let node1 = WaiterNode::new_stack_rt_mutex(key, 1, 20, b as u16);
1667 let _eg1 = node1.event().begin_wait();
1668 let guard1 =
1669 StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b].lock(), &node1);
1670 assert_eq!(guard1.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1671
1672 let node2 = WaiterNode::new_stack_rt_mutex(key, 1, 20, b as u16);
1673 let _eg2 = node2.event().begin_wait();
1674 let guard2 =
1675 StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b].lock(), &node2);
1676
1677 {
1679 let mut bucket = table.buckets[b].lock();
1680 let popped = table.pop_rt_mutex_waiter(b, &mut bucket, &key).unwrap();
1681 unsafe {
1683 bucket.wake_and_mark_woken(popped);
1684 }
1685 }
1686
1687 assert_eq!(guard2.resolve(error!(ETIMEDOUT)), Ok(()));
1688 }
1689
1690 #[fuchsia::test]
1691 fn test_requeue_fairness() {
1692 let table = FutexTable::default();
1693 let key1 = test_key(0x1000);
1694 let key2 = test_key(0x2000);
1695 let b1 = FutexTable::bucket_index(&key1);
1696
1697 let n1 = WaiterNode::new_stack_event(key1, 1, 10, 0, b1 as u16);
1698 let n2 = WaiterNode::new_stack_event(key1, 2, 10, 0, b1 as u16);
1699 let n3 = WaiterNode::new_stack_event(key1, 3, 10, 0, b1 as u16);
1700 let _e1 = n1.event().begin_wait();
1701 let _e2 = n2.event().begin_wait();
1702 let _e3 = n3.event().begin_wait();
1703
1704 let (g1, g2, g3) = {
1705 let mut bucket = table.buckets[b1].lock();
1706 (
1707 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n1),
1708 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n2),
1709 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n3),
1710 )
1711 };
1712
1713 let requeued = table.requeue_by_key(key1, key2, 0, 2, || Ok(())).unwrap();
1714 assert_eq!(requeued, 2);
1715
1716 assert_eq!(wake_key(&table, &key2, 1, 1), 1);
1718 assert_eq!(g1.resolve(Ok(())), Ok(()));
1719 assert_eq!(wake_key(&table, &key2, 1, 2), 1);
1720 assert_eq!(g2.resolve(Ok(())), Ok(()));
1721 assert_eq!(wake_key(&table, &key1, 1, 3), 1);
1722 assert_eq!(g3.resolve(Ok(())), Ok(()));
1723 }
1724
1725 #[fuchsia::test]
1726 fn test_cross_bucket_requeue_with_wake_and_cancel() {
1727 let table = FutexTable::default();
1728 let key1 = test_key(0x1000);
1729 let b1 = FutexTable::bucket_index(&key1);
1730 let key2 = (2..4096)
1731 .map(|i| test_key(i * 0x1000))
1732 .find(|k| FutexTable::bucket_index(k) != b1)
1733 .unwrap();
1734 let b2 = FutexTable::bucket_index(&key2);
1735
1736 let n1 = WaiterNode::new_stack_event(key1, u32::MAX, 10, 0, b1 as u16);
1737 let n2 = WaiterNode::new_stack_event(key1, u32::MAX, 10, 0, b1 as u16);
1738 let n3 = WaiterNode::new_stack_event(key1, u32::MAX, 10, 0, b1 as u16);
1739 let _e1 = n1.event().begin_wait();
1740 let _e2 = n2.event().begin_wait();
1741 let _e3 = n3.event().begin_wait();
1742
1743 let (g1, g2, g3) = {
1744 let mut bucket = table.buckets[b1].lock();
1745 (
1746 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n1),
1747 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n2),
1748 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n3),
1749 )
1750 };
1751
1752 let requeued = table.requeue_by_key(key1, key2, 1, 2, || Ok(())).unwrap();
1754 assert_eq!(requeued, 3);
1755 assert_eq!(g1.resolve(Ok(())), Ok(()));
1756 assert!(table.buckets[b1].lock().waiters.is_empty());
1757 assert_eq!(table.buckets[b2].lock().waiters.len(), 2);
1758
1759 assert_eq!(g2.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1761 assert_eq!(table.buckets[b2].lock().waiters.len(), 1);
1762 assert_eq!(wake_key(&table, &key2, 1, FUTEX_BITSET_MATCH_ANY), 1);
1763 assert_eq!(g3.resolve(Ok(())), Ok(()));
1764 assert!(table.buckets[b2].lock().waiters.is_empty());
1765 }
1766
1767 #[fuchsia::test]
1768 fn test_stale_external_waiter_cleanup() {
1769 let table = FutexTable::default();
1770 let vmo = MemoryObject::from(zx::Vmo::create(4096).unwrap());
1771 let key = FutexKeyValue::shared(&vmo, 0);
1772 let key2 = FutexKeyValue::shared(&vmo, 4);
1773 let b = FutexTable::bucket_index(&key);
1774
1775 {
1776 let token = Arc::new(());
1777 let (sender, _receiver) = oneshot::channel::<()>();
1778 let waiter = UnattachedWaiter::new_external(
1779 key,
1780 u32::MAX,
1781 b as u16,
1782 Arc::downgrade(&token),
1783 sender,
1784 );
1785 table.buckets[b].lock().waiters.insert_by_prio(waiter);
1786 } assert_eq!(table.buckets[b].lock().waiters.len(), 1);
1789 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 0);
1790 assert!(
1791 table.buckets[b].lock().waiters.is_empty(),
1792 "Stale external waiter should be removed by wake"
1793 );
1794
1795 {
1796 let token = Arc::new(());
1797 let (sender, _receiver) = oneshot::channel::<()>();
1798 let waiter = UnattachedWaiter::new_external(
1799 key,
1800 u32::MAX,
1801 b as u16,
1802 Arc::downgrade(&token),
1803 sender,
1804 );
1805 table.buckets[b].lock().waiters.insert_by_prio(waiter);
1806 } assert_eq!(table.buckets[b].lock().waiters.len(), 1);
1809 assert_eq!(table.requeue_by_key(key, key2, 0, 1, || Ok(())).unwrap(), 0);
1810 assert!(
1811 table.buckets[b].lock().waiters.is_empty(),
1812 "Stale external waiter should be removed by requeue"
1813 );
1814
1815 {
1816 let token = Arc::new(());
1817 let (sender, _receiver) = oneshot::channel::<()>();
1818 let waiter = UnattachedWaiter::new_external(
1819 key,
1820 u32::MAX,
1821 b as u16,
1822 Arc::downgrade(&token),
1823 sender,
1824 );
1825 table.buckets[b].lock().waiters.insert_by_prio(waiter);
1826 } assert_eq!(table.buckets[b].lock().waiters.len(), 1);
1829 let (_token2, _receiver2) = table.external_wait(vmo, 0, 0, u32::MAX).unwrap();
1830 assert_eq!(
1831 table.buckets[b].lock().waiters.len(),
1832 1,
1833 "Stale external waiter should be removed by external_wait"
1834 );
1835 }
1836
1837 #[fuchsia::test]
1838 fn test_drop_futex_table_with_pending_external_waiters() {
1839 let vmo1 = MemoryObject::from(zx::Vmo::create(4096).unwrap());
1840 let vmo2 = MemoryObject::from(zx::Vmo::create(4096).unwrap());
1841 let (_token1, mut receiver1);
1842 let mut receiver2;
1843 {
1844 let table = FutexTable::default();
1845 (_token1, receiver1) = table.external_wait(vmo1, 0, 0, u32::MAX).unwrap();
1846 let (token2, r2) = table.external_wait(vmo2, 4, 0, u32::MAX).unwrap();
1847 drop(token2);
1848 receiver2 = r2;
1849 }
1850 assert_eq!(receiver1.try_recv(), Err(oneshot::Canceled));
1852 assert_eq!(receiver2.try_recv(), Err(oneshot::Canceled));
1853 }
1854
1855 #[fuchsia::test]
1857 fn test_same_bucket_different_keys_do_not_interfere() {
1858 let table = FutexTable::default();
1859 let key1 = test_key(0x1000);
1860 let b = FutexTable::bucket_index(&key1);
1861 let key2 = (2..4096)
1862 .map(|i| test_key(i * 0x1000))
1863 .find(|k| FutexTable::bucket_index(k) == b)
1864 .unwrap();
1865
1866 let n1 = WaiterNode::new_stack_event(key1, u32::MAX, 10, 0, b as u16);
1867 let n2 = WaiterNode::new_stack_event(key2, u32::MAX, 30, 0, b as u16);
1868 let _e1 = n1.event().begin_wait();
1869 let _e2 = n2.event().begin_wait();
1870
1871 let (g1, g2) = {
1872 let mut bucket = table.buckets[b].lock();
1873 (
1874 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n1),
1875 StackWaiterGuard::insert_waiter(&table, &mut bucket, &n2),
1876 )
1877 };
1878
1879 assert_eq!(wake_key(&table, &key1, 1, FUTEX_BITSET_MATCH_ANY), 1);
1881 assert_eq!(g1.resolve(Ok(())), Ok(()));
1882 assert_eq!(table.buckets[b].lock().waiters.len(), 1);
1883
1884 assert_eq!(wake_key(&table, &key2, 1, FUTEX_BITSET_MATCH_ANY), 1);
1885 assert_eq!(g2.resolve(Ok(())), Ok(()));
1886 assert!(table.buckets[b].lock().waiters.is_empty());
1887 }
1888
1889 #[fuchsia::test]
1890 fn test_rt_mutex_priority_boost_and_handoff() {
1891 let table = FutexTable::default();
1892 let key = test_key(0x1000);
1893 let b = FutexTable::bucket_index(&key);
1894
1895 let n1 = WaiterNode::new_stack_rt_mutex(key, 20, 25, b as u16);
1896 n1.owner_tid.set(10);
1897 let _e1 = n1.event().begin_wait();
1898
1899 let n2 = WaiterNode::new_stack_rt_mutex(key, 30, 20, b as u16);
1900 n2.owner_tid.set(10);
1901 let _e2 = n2.event().begin_wait();
1902
1903 let n3 = WaiterNode::new_stack_rt_mutex(key, 40, -2, b as u16);
1904 n3.owner_tid.set(10);
1905 let _e3 = n3.event().begin_wait();
1906
1907 let (g1, g2) = {
1908 let mut bucket = table.buckets[b].lock();
1909 (
1910 StackWaiterGuard::insert_rt_mutex(&table, &mut bucket, &n1),
1911 StackWaiterGuard::insert_rt_mutex(&table, &mut bucket, &n2),
1912 )
1913 };
1914
1915 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
1918
1919 let g3 = StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b].lock(), &n3);
1922 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(-2));
1923
1924 {
1926 let mut bucket = table.buckets[b].lock();
1927 let popped_rt = table.pop_rt_mutex_waiter(b, &mut bucket, &key).unwrap();
1928 assert_eq!(popped_rt.tid(), 40 | FUTEX_WAITERS);
1929 unsafe {
1931 bucket.wake_and_mark_woken(popped_rt);
1932 }
1933 }
1934 assert_eq!(g3.resolve(Ok(())), Ok(()));
1935 assert_eq!(table.get_rt_mutex_priority_boost(10), None);
1936 assert_eq!(table.get_rt_mutex_priority_boost(40), Some(25));
1937
1938 {
1941 let mut bucket = table.buckets[b].lock();
1942 let popped1 = table.pop_rt_mutex_waiter(b, &mut bucket, &key).unwrap();
1943 assert_eq!(popped1.tid(), 20 | FUTEX_WAITERS);
1944 unsafe {
1946 bucket.wake_and_mark_woken(popped1);
1947 }
1948 }
1949 assert_eq!(g1.resolve(Ok(())), Ok(()));
1950 assert_eq!(table.get_rt_mutex_priority_boost(40), None);
1951 assert_eq!(table.get_rt_mutex_priority_boost(20), Some(20));
1952
1953 {
1956 let mut bucket = table.buckets[b].lock();
1957 let popped2 = table.pop_rt_mutex_waiter(b, &mut bucket, &key).unwrap();
1958 assert_eq!(popped2.tid(), 30);
1959 unsafe {
1961 bucket.wake_and_mark_woken(popped2);
1962 }
1963 }
1964 assert_eq!(table.active_rt_mutex_waiters.load(Ordering::Relaxed), 0);
1965 assert_eq!(table.get_rt_mutex_priority_boost(20), None);
1966 assert_eq!(g2.resolve(Ok(())), Ok(()));
1967 assert_eq!(table.active_rt_mutex_waiters.load(Ordering::Relaxed), 0);
1968 }
1969
1970 #[fuchsia::test]
1971 fn test_rt_mutex_transitive_priority_boost_expires_when_upstream_waiter_leaves() {
1972 let table = FutexTable::default();
1973 let key1 = test_key(0x1000);
1974 let key2 = test_key(0x2000);
1975 let b1 = FutexTable::bucket_index(&key1);
1976 let b2 = FutexTable::bucket_index(&key2);
1977
1978 let mid_node = WaiterNode::new_stack_rt_mutex(key1, 20, 25, b1 as u16);
1980 mid_node.owner_tid.set(10);
1981 let _mid_eg = mid_node.event().begin_wait();
1982 let mid_guard =
1983 StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b1].lock(), &mid_node);
1984 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
1985
1986 let up_node = WaiterNode::new_stack_rt_mutex(key2, 30, 20, b2 as u16);
1989 up_node.owner_tid.set(20);
1990 let _up_eg = up_node.event().begin_wait();
1991 let up_guard =
1992 StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b2].lock(), &up_node);
1993 assert_eq!(table.get_rt_mutex_priority_boost(20), Some(20));
1994 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(20));
1995
1996 assert_eq!(up_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
1999 assert_eq!(table.get_rt_mutex_priority_boost(20), None);
2000 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
2001
2002 let second_waiter_node = WaiterNode::new_stack_rt_mutex(key1, 21, 25, b1 as u16);
2005 second_waiter_node.owner_tid.set(10);
2006 let _second_eg = second_waiter_node.event().begin_wait();
2007 let second_guard = StackWaiterGuard::insert_rt_mutex(
2008 &table,
2009 &mut table.buckets[b1].lock(),
2010 &second_waiter_node,
2011 );
2012 let rt_up_node = WaiterNode::new_stack_rt_mutex(key2, 31, -5, b2 as u16);
2013 rt_up_node.owner_tid.set(21);
2014 let _rt_up_eg = rt_up_node.event().begin_wait();
2015 let rt_up_guard =
2016 StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b2].lock(), &rt_up_node);
2017 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(-5));
2018 assert_eq!(rt_up_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2019 assert_eq!(second_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2020 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
2021
2022 let same_bucket_key = (3..4096)
2026 .map(|i| test_key(i * 0x1000))
2027 .find(|k| *k != key1 && FutexTable::bucket_index(k) == b1)
2028 .unwrap();
2029 let same_bucket_node = WaiterNode::new_stack_rt_mutex(same_bucket_key, 22, 20, b1 as u16);
2030 same_bucket_node.owner_tid.set(10);
2031 let _same_bucket_eg = same_bucket_node.event().begin_wait();
2032 let same_bucket_guard = StackWaiterGuard::insert_rt_mutex(
2033 &table,
2034 &mut table.buckets[b1].lock(),
2035 &same_bucket_node,
2036 );
2037 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(20));
2038 assert_eq!(same_bucket_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2039 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
2040
2041 let higher_bucket_key = (3..4096)
2042 .map(|i| test_key(i * 0x1000))
2043 .find(|k| FutexTable::bucket_index(k) > b1)
2044 .unwrap();
2045 let b_high = FutexTable::bucket_index(&higher_bucket_key);
2046 let higher_bucket_node =
2047 WaiterNode::new_stack_rt_mutex(higher_bucket_key, 23, 20, b_high as u16);
2048 higher_bucket_node.owner_tid.set(10);
2049 let _higher_bucket_eg = higher_bucket_node.event().begin_wait();
2050 let higher_bucket_guard = StackWaiterGuard::insert_rt_mutex(
2051 &table,
2052 &mut table.buckets[b_high].lock(),
2053 &higher_bucket_node,
2054 );
2055 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(20));
2056 assert_eq!(higher_bucket_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2057 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
2058
2059 let cycle_node = WaiterNode::new_stack_rt_mutex(key2, 10, 30, b2 as u16);
2063 cycle_node.owner_tid.set(20);
2064 let _cycle_eg = cycle_node.event().begin_wait();
2065 let cycle_guard =
2066 StackWaiterGuard::insert_rt_mutex(&table, &mut table.buckets[b2].lock(), &cycle_node);
2067 assert_eq!(table.get_rt_mutex_priority_boost(20), Some(30));
2068 assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
2069 assert_eq!(cycle_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2070
2071 assert_eq!(mid_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2072 assert_eq!(table.get_rt_mutex_priority_boost(10), None);
2073 }
2074
2075 #[::fuchsia::test]
2078 async fn test_private_futex_same_address_different_processes_do_not_interfere() {
2079 use crate::testing::spawn_kernel_and_run;
2080
2081 spawn_kernel_and_run(async move |current_task| {
2082 let child_task = current_task.clone_task_for_test(0, None);
2083 let addr =
2084 FutexAddress::try_from(UserAddress::from((RESTRICTED_ASPACE_BASE + 0x1000) as u64))
2085 .unwrap();
2086
2087 let mm1 = current_task.mm().unwrap();
2088 let mm2 = child_task.mm().unwrap();
2089 let key1 = PrivateFutexKey::get(&mm1, addr).unwrap();
2090 let key2 = PrivateFutexKey::get(&mm2, addr).unwrap();
2091 assert_ne!(key1, key2);
2092
2093 let table = FutexTable::default();
2094 let b1 = FutexTable::bucket_index(&key1);
2095 let b2 = FutexTable::bucket_index(&key2);
2096
2097 let n1 = WaiterNode::new_stack_event(key1, u32::MAX, 10, 0, b1 as u16);
2098 let n2 = WaiterNode::new_stack_event(key2, u32::MAX, 10, 0, b2 as u16);
2099 let _e1 = n1.event().begin_wait();
2100 let _e2 = n2.event().begin_wait();
2101
2102 let g1 = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b1].lock(), &n1);
2103 let g2 = StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b2].lock(), &n2);
2104
2105 assert_eq!(wake_key(&table, &key1, 1, FUTEX_BITSET_MATCH_ANY), 1);
2106 assert_eq!(g1.resolve(Ok(())), Ok(()));
2107 assert_eq!(g2.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2108 })
2109 .await;
2110 }
2111
2112 #[fuchsia::test]
2113 fn test_futex_waiters_priority_ordering() {
2114 let table = FutexTable::default();
2115 let key = test_key(0x1000);
2116 let b = FutexTable::bucket_index(&key);
2117
2118 let low_node = WaiterNode::new_stack_event(key, 1, 10, 0, b as u16);
2119 let low_eg = low_node.event().begin_wait();
2120 let high_node = WaiterNode::new_stack_event(key, 2, 30, 0, b as u16);
2121 let high_eg = high_node.event().begin_wait();
2122 let mid1_node = WaiterNode::new_stack_event(key, 4, 20, 0, b as u16);
2123 let mid1_eg = mid1_node.event().begin_wait();
2124 let mid2_node = WaiterNode::new_stack_event(key, 8, 20, 0, b as u16);
2125 let mid2_eg = mid2_node.event().begin_wait();
2126
2127 let (low_guard, high_guard, mid1_guard, mid2_guard) = {
2128 let mut bucket = table.buckets[b].lock();
2129 (
2130 StackWaiterGuard::insert_waiter(&table, &mut bucket, &low_node),
2131 StackWaiterGuard::insert_waiter(&table, &mut bucket, &high_node),
2132 StackWaiterGuard::insert_waiter(&table, &mut bucket, &mid1_node),
2135 StackWaiterGuard::insert_waiter(&table, &mut bucket, &mid2_node),
2136 )
2137 };
2138
2139 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
2140 assert_eq!(high_eg.block_until(None, zx::MonotonicInstant::ZERO), Ok(()));
2141 assert_eq!(high_guard.resolve(Ok(())), Ok(()));
2142
2143 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
2144 assert_eq!(mid1_eg.block_until(None, zx::MonotonicInstant::ZERO), Ok(()));
2145 assert_eq!(mid1_guard.resolve(Ok(())), Ok(()));
2146
2147 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
2148 assert_eq!(mid2_eg.block_until(None, zx::MonotonicInstant::ZERO), Ok(()));
2149 assert_eq!(mid2_guard.resolve(Ok(())), Ok(()));
2150
2151 assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
2152 assert_eq!(low_eg.block_until(None, zx::MonotonicInstant::ZERO), Ok(()));
2153 assert_eq!(low_guard.resolve(Ok(())), Ok(()));
2154
2155 let dst_key = test_key(0x2000);
2158 let dst_b = FutexTable::bucket_index(&dst_key);
2159 let dst_low_node = WaiterNode::new_stack_event(dst_key, 1, 10, 0, dst_b as u16);
2160 let dst_low_eg = dst_low_node.event().begin_wait();
2161 let src_high_node = WaiterNode::new_stack_event(key, 2, 30, 0, b as u16);
2162 let src_high_eg = src_high_node.event().begin_wait();
2163
2164 let dst_low_guard = StackWaiterGuard::insert_waiter(
2165 &table,
2166 &mut table.buckets[dst_b].lock(),
2167 &dst_low_node,
2168 );
2169 let src_high_guard =
2170 StackWaiterGuard::insert_waiter(&table, &mut table.buckets[b].lock(), &src_high_node);
2171
2172 assert_eq!(table.requeue_by_key(key, dst_key, 0, 1, || Ok(())).unwrap(), 1);
2173
2174 assert_eq!(wake_key(&table, &dst_key, 1, FUTEX_BITSET_MATCH_ANY), 1);
2175 assert_eq!(src_high_eg.block_until(None, zx::MonotonicInstant::ZERO), Ok(()));
2176 assert_eq!(src_high_guard.resolve(Ok(())), Ok(()));
2177
2178 assert_eq!(wake_key(&table, &dst_key, 1, FUTEX_BITSET_MATCH_ANY), 1);
2179 assert_eq!(dst_low_eg.block_until(None, zx::MonotonicInstant::ZERO), Ok(()));
2180 assert_eq!(dst_low_guard.resolve(Ok(())), Ok(()));
2181 }
2182
2183 #[::fuchsia::test]
2187 async fn test_unlock_pi_hands_over_to_next_waiter_after_cancelled_one() {
2188 use crate::mm::memory::MemoryObject;
2189 use crate::mm::{DesiredAddress, MappingName, MappingOptions, PAGE_SIZE, ProtectionFlags};
2190 use crate::testing::spawn_kernel_and_run;
2191
2192 spawn_kernel_and_run(async move |current_task| {
2193 let mm = current_task.mm().unwrap();
2194 let addr = mm
2195 .map_memory(
2196 DesiredAddress::Any,
2197 Arc::new(MemoryObject::from(zx::Vmo::create(*PAGE_SIZE).unwrap())),
2198 0,
2199 *PAGE_SIZE as usize,
2200 ProtectionFlags::READ | ProtectionFlags::WRITE,
2201 MappingOptions::empty(),
2202 MappingName::None,
2203 )
2204 .expect("map failed");
2205 let futex_addr = FutexAddress::try_from(addr).unwrap();
2206
2207 let owner_tid = current_task.get_tid() as u32;
2209 assert!(matches!(
2210 mm.atomic_compare_exchange_u32_acq_rel(futex_addr, 0, owner_tid | FUTEX_WAITERS),
2211 CompareExchangeResult::Success
2212 ));
2213
2214 const CANCELLED_TID: u32 = 0x111;
2215 const NEXT_OWNER_TID: u32 = 0x222;
2216 const THIRD_WAITER_TID: u32 = 0x333;
2217 const OTHER_RT_WAITER_TID: u32 = 0x444;
2218 let futex_table = FutexTable::default();
2219 let key = PrivateFutexKey::get(&mm, futex_addr).unwrap();
2220 let b = FutexTable::bucket_index(&key);
2221 let other_key = (1..4096)
2222 .map(|i| test_key(i * 0x1000))
2223 .find(|k| *k != key && FutexTable::bucket_index(k) == b)
2224 .unwrap();
2225
2226 let next_node = WaiterNode::new_stack_rt_mutex(key, NEXT_OWNER_TID, 20, b as u16);
2227 next_node.owner_tid.set(owner_tid);
2228 let _next_eg = next_node.event().begin_wait();
2229 let third_node = WaiterNode::new_stack_rt_mutex(key, THIRD_WAITER_TID, 25, b as u16);
2230 third_node.owner_tid.set(owner_tid);
2231 let _third_eg = third_node.event().begin_wait();
2232 let other_rt_node =
2233 WaiterNode::new_stack_rt_mutex(other_key, OTHER_RT_WAITER_TID, -2, b as u16);
2234 other_rt_node.owner_tid.set(owner_tid);
2235 let _other_rt_eg = other_rt_node.event().begin_wait();
2236
2237 {
2238 let cancelled_node =
2239 WaiterNode::new_stack_rt_mutex(key, CANCELLED_TID, 20, b as u16);
2240 cancelled_node.owner_tid.set(owner_tid);
2241 let _cancelled_guard = StackWaiterGuard::insert_rt_mutex(
2242 &futex_table,
2243 &mut futex_table.buckets[b].lock(),
2244 &cancelled_node,
2245 );
2246 }
2248
2249 let next_guard = StackWaiterGuard::insert_rt_mutex(
2250 &futex_table,
2251 &mut futex_table.buckets[b].lock(),
2252 &next_node,
2253 );
2254 let third_guard = StackWaiterGuard::insert_rt_mutex(
2255 &futex_table,
2256 &mut futex_table.buckets[b].lock(),
2257 &third_node,
2258 );
2259 let other_rt_guard = StackWaiterGuard::insert_rt_mutex(
2260 &futex_table,
2261 &mut futex_table.buckets[b].lock(),
2262 &other_rt_node,
2263 );
2264
2265 mm.protect(current_task, addr, *PAGE_SIZE as usize, ProtectionFlags::READ)
2269 .expect("protect READ failed");
2270 assert_eq!(
2271 futex_table.unlock_pi::<PrivateFutexKey>(current_task, addr),
2272 error!(EACCES)
2273 );
2274 assert_eq!(futex_table.buckets[b].lock().rt_mutex_waiters.len(), 3);
2275 assert_eq!(futex_table.get_rt_mutex_priority_boost(owner_tid), Some(-2));
2276
2277 mm.protect(
2278 current_task,
2279 addr,
2280 *PAGE_SIZE as usize,
2281 ProtectionFlags::READ | ProtectionFlags::WRITE,
2282 )
2283 .expect("protect READ|WRITE failed");
2284 futex_table.unlock_pi::<PrivateFutexKey>(current_task, addr).expect("unlock_pi failed");
2285
2286 assert_eq!(
2287 mm.atomic_load_u32_relaxed(futex_addr).unwrap(),
2288 NEXT_OWNER_TID | FUTEX_WAITERS,
2289 "the mutex should be handed to NEXT_OWNER_TID with FUTEX_WAITERS still set"
2290 );
2291 assert_eq!(futex_table.get_rt_mutex_priority_boost(NEXT_OWNER_TID), Some(25));
2292 assert_eq!(next_guard.resolve(error!(ETIMEDOUT)), Ok(()));
2293 assert_eq!(third_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2294 assert_eq!(other_rt_guard.resolve(error!(ETIMEDOUT)), error!(ETIMEDOUT));
2295 assert!(futex_table.buckets[b].lock().rt_mutex_waiters.is_empty());
2296 })
2297 .await;
2298 }
2299
2300 #[::fuchsia::test]
2301 async fn test_futex_deadlock_with_pager() {
2302 use crate::mm::memory::MemoryObject;
2303 use crate::mm::{DesiredAddress, MappingName, MappingOptions, PAGE_SIZE, ProtectionFlags};
2304 use crate::testing::spawn_kernel_and_run;
2305 use std::sync::atomic::{AtomicBool, Ordering};
2306 use zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ;
2307
2308 spawn_kernel_and_run(async move |current_task| {
2309 let mm = current_task.mm().unwrap();
2310
2311 let port = Arc::new(zx::Port::create());
2312 let port_clone = port.clone();
2313 let pager =
2314 Arc::new(zx::Pager::create(zx::PagerOptions::empty()).expect("create failed"));
2315 let pager_clone = pager.clone();
2316
2317 let vmo = Arc::new(
2318 pager
2319 .create_vmo(zx::VmoOptions::RESIZABLE, &port, 1, *PAGE_SIZE)
2320 .expect("create_vmo failed"),
2321 );
2322 let vmo_clone = vmo.clone();
2323
2324 let mapped_addr = mm
2325 .map_memory(
2326 DesiredAddress::Any,
2327 Arc::new(MemoryObject::from(
2328 (*vmo).duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2329 )),
2330 0,
2331 *PAGE_SIZE as usize,
2332 ProtectionFlags::READ | ProtectionFlags::WRITE,
2333 MappingOptions::empty(),
2334 MappingName::None,
2335 )
2336 .expect("map failed");
2337
2338 let futex_table = Arc::new(FutexTable::default());
2339 let futex_table_clone = futex_table.clone();
2340 let task_clone = current_task.task.clone();
2341
2342 let page_requested = Arc::new(AtomicBool::new(false));
2343 let wake_completed = Arc::new(AtomicBool::new(false));
2344
2345 let page_req_clone = page_requested.clone();
2346 let wake_completed_clone = wake_completed.clone();
2347
2348 let pager_thread = std::thread::spawn(move || {
2349 let packet = port_clone.wait(zx::MonotonicInstant::INFINITE).expect("wait failed");
2350 if let zx::PacketContents::Pager(contents) = packet.contents() {
2351 if contents.command() == ZX_PAGER_VMO_READ {
2352 let range = contents.range();
2353 page_req_clone.store(true, Ordering::SeqCst);
2354
2355 let waker = std::thread::spawn(move || {
2358 let _ = futex_table_clone.wake::<PrivateFutexKey>(
2359 &task_clone,
2360 mapped_addr,
2361 1,
2362 u32::MAX,
2363 );
2364 wake_completed_clone.store(true, Ordering::SeqCst);
2365 });
2366
2367 waker.join().unwrap();
2370 assert!(
2371 wake_completed.load(Ordering::SeqCst),
2372 "Waker thread must NOT be blocked while page fault is in progress!"
2373 );
2374
2375 let source_vmo =
2377 zx::Vmo::create(range.end - range.start).expect("create failed");
2378 pager_clone
2379 .supply_pages(&vmo_clone, range, &source_vmo, 0)
2380 .expect("supply_pages failed");
2381 }
2382 }
2383 });
2384
2385 let _ = futex_table.wait::<PrivateFutexKey>(
2387 current_task,
2388 mapped_addr,
2389 0,
2390 u32::MAX,
2391 zx::MonotonicInstant::from_nanos(1),
2392 );
2393
2394 pager_thread.join().unwrap();
2395 })
2396 .await;
2397 }
2398
2399 #[::fuchsia::test]
2400 async fn test_wait_expired_deadline_returns_etimedout_without_queuing() {
2401 use crate::mm::memory::MemoryObject;
2402 use crate::mm::{DesiredAddress, MappingName, MappingOptions, PAGE_SIZE, ProtectionFlags};
2403 use crate::testing::spawn_kernel_and_run;
2404
2405 spawn_kernel_and_run(async move |current_task| {
2406 let mm = current_task.mm().unwrap();
2407 let addr = mm
2408 .map_memory(
2409 DesiredAddress::Any,
2410 Arc::new(MemoryObject::from(zx::Vmo::create(*PAGE_SIZE).unwrap())),
2411 0,
2412 *PAGE_SIZE as usize,
2413 ProtectionFlags::READ | ProtectionFlags::WRITE,
2414 MappingOptions::empty(),
2415 MappingName::None,
2416 )
2417 .expect("map failed");
2418
2419 let futex_table = FutexTable::default();
2420 let key = PrivateFutexKey::get(&mm, FutexAddress::try_from(addr).unwrap()).unwrap();
2421 let b = FutexTable::bucket_index(&key);
2422
2423 assert_eq!(
2424 futex_table.wait::<PrivateFutexKey>(
2425 current_task,
2426 addr,
2427 0,
2428 FUTEX_BITSET_MATCH_ANY,
2429 zx::MonotonicInstant::ZERO,
2430 ),
2431 error!(ETIMEDOUT)
2432 );
2433 assert_eq!(
2434 futex_table.wait::<PrivateFutexKey>(
2435 current_task,
2436 addr,
2437 0,
2438 FUTEX_BITSET_MATCH_ANY,
2439 zx::MonotonicInstant::get(),
2440 ),
2441 error!(ETIMEDOUT)
2442 );
2443 assert_eq!(
2444 futex_table.wait_boot::<PrivateFutexKey>(
2445 current_task,
2446 addr,
2447 0,
2448 FUTEX_BITSET_MATCH_ANY,
2449 zx::BootInstant::ZERO,
2450 zx::BootDuration::ZERO,
2451 ),
2452 error!(ETIMEDOUT)
2453 );
2454 assert_eq!(
2455 futex_table.wait_boot::<PrivateFutexKey>(
2456 current_task,
2457 addr,
2458 0,
2459 FUTEX_BITSET_MATCH_ANY,
2460 zx::BootInstant::get(),
2461 zx::BootDuration::ZERO,
2462 ),
2463 error!(ETIMEDOUT)
2464 );
2465 assert!(futex_table.buckets[b].lock().waiters.is_empty());
2466
2467 assert_eq!(
2469 futex_table.wait::<PrivateFutexKey>(
2470 current_task,
2471 addr,
2472 1,
2473 FUTEX_BITSET_MATCH_ANY,
2474 zx::MonotonicInstant::ZERO,
2475 ),
2476 error!(EAGAIN)
2477 );
2478 assert_eq!(
2479 futex_table.wait_boot::<PrivateFutexKey>(
2480 current_task,
2481 addr,
2482 1,
2483 FUTEX_BITSET_MATCH_ANY,
2484 zx::BootInstant::ZERO,
2485 zx::BootDuration::ZERO,
2486 ),
2487 error!(EAGAIN)
2488 );
2489 assert!(futex_table.buckets[b].lock().waiters.is_empty());
2490 })
2491 .await;
2492 }
2493}