Skip to main content

starnix_core/mm/
futex_table.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::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
24/// Number of hash buckets in the kernel-wide [`FutexTable`]. Must be a power of two.
25const NUM_BUCKETS: usize = 1024;
26
27/// Number of 64-bit words in [`FutexTable::rt_mutex_bucket_mask`].
28const RT_MUTEX_MASK_WORDS: usize = NUM_BUCKETS / 64;
29
30/// Maximum depth when walking transitive RT-Mutex priority-inheritance chains.
31const MAX_RT_MUTEX_CHAIN_DEPTH: usize = 16;
32
33/// Compact 16-byte key identifying either a private (`FUTEX_PRIVATE_FLAG`) or shared futex.
34#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
35pub struct FutexKeyValue {
36    /// For private futexes: `(mm_ptr as u64) | 1` (low bit 1).
37    /// For shared futexes: `koid.raw_koid() << 1` (low bit 0).
38    id_and_tag: u64,
39    /// For private futexes: `addr.ptr() as u64`.
40    /// For shared futexes: `offset`.
41    offset: u64,
42}
43
44impl FutexKeyValue {
45    #[inline]
46    fn private(mm: &Arc<MemoryManager>, addr: FutexAddress) -> Self {
47        // `Arc::as_ptr(mm)` uniquely identifies the process address space (analogous to Linux's
48        // `struct mm_struct *mm` in `union futex_key::private`). Every waiting thread holds its
49        // `Arc<MemoryManager>` alive for the entire duration of the wait and unlinks its
50        // `WaiterNode` via `StackWaiterGuard` before returning, so `mm` cannot be freed or have
51        // its address reused while any waiter with this key is queued.
52        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        // No chance of collisions since koids are never reused:
60        // https://fuchsia.dev/fuchsia-src/concepts/kernel/concepts#kernel_object_ids
61        Self { id_and_tag: memory.get_koid().raw_koid() << 1, offset }
62    }
63}
64
65/// A kernel-wide table of futexes sharded across `NUM_BUCKETS` hash buckets.
66///
67/// Each 32-bit aligned address in an address space can potentially have an associated futex that
68/// userspace can wait upon. Both private (`FUTEX_PRIVATE_FLAG`) and shared futexes share this
69/// table via [`FutexKeyValue`]. Each bucket holds intrusive doubly-linked lists of
70/// stack-allocated waiter nodes for internal waiters (`wait`, `wait_boot`, and `lock_pi`),
71/// eliminating per-process table allocations and per-wait heap allocations.
72pub 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    /// Wait on the futex at the given address given a boot deadline.
132    ///
133    /// See FUTEX_WAIT when passed a deadline in CLOCK_REALTIME.
134    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        // Pre-fault the address and check if the value already changed before acquiring the
146        // bucket lock to make page faults or memory reclamation under the lock highly unlikely.
147        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
148        // supported by Zircon.
149        if mm.atomic_load_u32_acquire(addr)? != value {
150            return error!(EAGAIN);
151        }
152        let key = Key::get(&mm, addr)?;
153        // Linux futex_wait(): an already-expired timer returns ETIMEDOUT without sleeping.
154        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        // As the bucket is locked, no wake can happen before the waiter is registered.
176        // If the addr is remapped, we will read stale data, but we will not miss a futex wake.
177        // Acquire ordering to synchronize with userspace modifications to the value on other
178        // threads.
179        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    /// Wait on the futex at the given address.
192    ///
193    /// See FUTEX_WAIT.
194    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        // Pre-fault the address and check if the value already changed before acquiring the
205        // bucket lock to make page faults or memory reclamation under the lock highly unlikely.
206        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
207        // supported by Zircon.
208        if mm.atomic_load_u32_acquire(addr)? != value {
209            return error!(EAGAIN);
210        }
211
212        let key = Key::get(&mm, addr)?;
213        // Linux futex_wait(): an already-expired timer returns ETIMEDOUT without sleeping.
214        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        // As the bucket is locked, no wake can happen before the waiter is registered.
224        // If the addr is remapped, we will read stale data, but we will not miss a futex wake.
225        // Acquire ordering to synchronize with userspace modifications to the value on other
226        // threads.
227        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    /// Wake the given number of waiters on futex at the given address. Returns the number of
240    /// waiters actually woken.
241    ///
242    /// See FUTEX_WAKE.
243    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    /// Requeue the waiters to another address.
259    ///
260    /// See FUTEX_CMP_REQUEUE
261    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            // Pre-fault the address before acquiring the bucket lock to make page faults or
275            // memory reclamation under the lock highly unlikely.
276            // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop
277            // once supported by Zircon.
278            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                // Use acquire ordering here to synchronize with mutex impls that store w/
285                // release ordering.
286                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            // Wake up at most `wake_count` waiters.
309            let woken = bucket.wake(&key, wake_count, FUTEX_BITSET_MATCH_ANY);
310            // Dequeue up to `requeue_count` waiters and requeue onto `new_key`.
311            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            // Wake up at most `wake_count` waiters.
318            let woken = bucket1.wake(&key, wake_count, FUTEX_BITSET_MATCH_ANY);
319            // Dequeue up to `requeue_count` waiters and requeue onto `bucket2`.
320            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    /// Returns the highest priority (lowest numerical `task_prio` value) among waiters blocked on
327    /// any RT-Mutex currently owned by `owner_tid`, including transitive priority boosts.
328    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    /// Evaluates the top waiter for each RT-Mutex in bucket `b` owned by `owner_tid` (including
363    /// transitive priority boosts) and returns the highest priority (lowest numerical `task_prio`
364    /// value) across those mutexes.
365    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    /// Lock the futex at the given address.
399    ///
400    /// See FUTEX_LOCK_PI.
401    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        // Check for self-deadlock or uncontended acquisition before reading scheduler state or
411        // acquiring the bucket lock. For contended futexes, perform a placeholder CAS to pre-fault
412        // writable permissions (e.g. for COW pages) before locking the bucket.
413        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
414        // supported by Zircon.
415        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        // As the bucket is locked, no unlock can happen before the waiter is registered.
438        // If the addr is remapped, we will read stale data, but we will not miss a futex unlock.
439        // Use a relaxed ordering because the compare/exchange below creates a synchronization
440        // point with userspace threads in the success case. No synchronization is required in
441        // failure cases.
442        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                // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
447                //
448                //   EDEADLK
449                //          (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,
450                //          FUTEX_CMP_REQUEUE_PI) The futex word at uaddr is
451                //          already locked by the caller.
452                return error!(EDEADLOCK);
453            }
454
455            if current_value == 0 {
456                // Use acq/rel ordering to synchronize with acquire ordering on userspace lock ops
457                // and with the release ordering on userspace unlock ops.
458                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            // Use acq/rel ordering to synchronize with acquire ordering on userspace lock ops and
469            // with the release ordering on userspace unlock ops.
470            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        // ESRCH  (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,
487        //        FUTEX_CMP_REQUEUE_PI) The thread ID in the futex word at
488        //        uaddr does not exist.
489        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        // Being gone from the queue means `unlock_pi` picked this waiter as the new owner and
499        // already published its tid in the futex word, so the mutex is held even though the wait
500        // itself failed. Reporting the error would leave the mutex locked by a thread that
501        // believes it does not own it.
502        queued_guard.resolve(result)
503    }
504
505    /// Unlock the futex at the given address.
506    ///
507    /// See FUTEX_UNLOCK_PI.
508    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        // Perform a placeholder CAS to pre-fault the page with write permissions (e.g. for COW
516        // pages) before acquiring the bucket lock to make page faults or memory reclamation under
517        // the lock highly unlikely.
518        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
519        // supported by Zircon.
520        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        // Use a relaxed ordering because the compare/exchange below creates a synchronization
529        // point with userspace threads in the success case. No synchronization is required in
530        // failure cases.
531        let expected_value = mm.atomic_load_u32_relaxed(addr)?;
532        if expected_value & FUTEX_TID_MASK != tid {
533            // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
534            //
535            //   EPERM  (FUTEX_UNLOCK_PI) The caller does not own the lock
536            //          represented by the futex word.
537            return error!(EPERM);
538        }
539
540        let target_value = bucket.peek_rt_mutex_target_value(&key);
541
542        // Use acq/rel ordering to synchronize with acquire ordering on userspace lock ops and
543        // with the release ordering on userspace unlock ops.
544        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            // SAFETY: `node` was popped from `bucket` under the current lock acquisition.
549            unsafe {
550                bucket.wake_and_mark_woken(node);
551            }
552        }
553
554        Ok(())
555    }
556
557    /// Wait on the futex at the given offset in the memory.
558    ///
559    /// Returns a receiver that will be signaled when the futex is woken, and an
560    /// `Arc<()>` token that must be kept alive by the caller for the duration of the
561    /// wait. If the caller drops the token (e.g., if the external client
562    /// disconnects), the waiter is marked as stale and will be garbage-collected by the
563    /// next futex operation on this bucket.
564    ///
565    /// See FUTEX_WAIT.
566    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        // As the bucket is locked, no wake can happen before the waiter is registered.
578        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    /// Wake the given number of waiters on futex at the given offset in the memory. Returns the
589    /// number of waiters actually woken.
590    ///
591    /// See FUTEX_WAKE.
592    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                // The state being locked is how this is included in the set of atomic changes.
623                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            // TODO: This read should be atomic.
636            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
647/// Maps the outcome of the compare-exchange that hands a PI mutex over to its next owner.
648fn handoff_result(result: CompareExchangeResult<u32>) -> Result<(), Errno> {
649    match result {
650        CompareExchangeResult::Success => Ok(()),
651        // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
652        //
653        //   EINVAL (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,
654        //       FUTEX_UNLOCK_PI) The kernel detected an inconsistency
655        //       between the user-space state at uaddr and the kernel
656        //       state.  This indicates either state corruption or that the
657        //       kernel found a waiter on uaddr which is waiting via
658        //       FUTEX_WAIT or FUTEX_WAIT_BITSET.
659        CompareExchangeResult::Stale { .. } => error!(EINVAL),
660        // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
661        //
662        //   EACCES No read access to the memory of a futex word.
663        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
688/// Computes a priority key for ordering `FUTEX_WAIT` waiters (matching Linux's priority-ordered
689/// `plist` futex queues).
690fn task_futex_priority(task: &Task) -> u8 {
691    task.scheduler_state.lock().futex_priority()
692}
693
694/// Maps a Linux `task_prio` value to the priority key used to order RT-Mutex waiters
695/// (`__waiter_prio` in Linux `kernel/locking/rtmutex_common.h`).
696#[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    /// Stack-allocated `InterruptibleEvent` (used by `wait` and `lock_pi`).
703    StackEvent(InterruptibleEvent),
704    /// Stack-allocated `Waiter` (used by `wait_boot`).
705    StackBoot(Waiter),
706    /// Heap-allocated external waiter (`Box<WaiterNode>`), owned by the bucket list.
707    External { token: Weak<()>, sender: oneshot::Sender<()> },
708}
709
710/// An intrusive waiter node.
711///
712/// For `wait`, `wait_boot`, and `lock_pi`, this node lives directly on the blocked thread's
713/// kernel stack frame and is protected by [`StackWaiterGuard`]. For `external_wait`, it is
714/// heap-allocated via `Box` and owned by [`WaiterList`].
715struct WaiterNode {
716    prev: Cell<Option<NonNull<WaiterNode>>>,
717    next: Cell<Option<NonNull<WaiterNode>>>,
718    key: Cell<FutexKeyValue>,
719    mask: u32,
720    /// Scheduling priority used to order `waiters` (`FUTEX_WAIT`); unused (`0`) for
721    /// `rt_mutex_waiters` (`FUTEX_LOCK_PI`).
722    prio: u8,
723    /// The waiter's effective task priority (`task_prio` value; lower values mean higher priority)
724    /// for `rt_mutex_waiters` (`FUTEX_LOCK_PI`).
725    task_prio: i64,
726    /// The tid of the current owner of the RT-Mutex this waiter is blocked on.
727    owner_tid: Cell<u32>,
728    attached: Cell<bool>,
729    woken: AtomicBool,
730    bucket_idx: AtomicU16,
731    /// The tid, possibly with the FUTEX_WAITERS bit set.
732    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    /// Returns the priority used for RT-Mutex waiter queue ordering (`__waiter_prio` in Linux
817    /// `kernel/locking/rtmutex_common.h`). Real-time tasks (`task_prio < 0`) are ordered by their
818    /// real-time priority, while non-real-time tasks all share `DEFAULT_PRIO` (`20`, nice 0) and
819    /// are queued FIFO relative to one another.
820    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/// Linear (non-`Copy`, non-`Clone`) ownership token representing a valid [`WaiterNode`] that is
844/// currently unattached from any [`WaiterList`].
845///
846/// Consuming `UnattachedWaiter` by value in [`WaiterList::push_back`], [`WaiterList::push_front`],
847/// [`WaiterList::insert_by_prio`], [`FutexBucket::wake_and_mark_woken`], and
848/// [`FutexBucket::free_stale_external`] guarantees at compile time that only unattached nodes can
849/// be inserted, woken, or freed, and that no node can be inserted or woken twice.
850#[must_use = "an UnattachedWaiter must be inserted, woken, or freed"]
851struct UnattachedWaiter(NonNull<WaiterNode>);
852
853impl UnattachedWaiter {
854    /// Allocates a new heap-backed external [`WaiterNode`] in the unattached state.
855    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    /// Creates an [`UnattachedWaiter`] for a stack-allocated `node`.
868    ///
869    /// Panics if `node` is already attached.
870    ///
871    /// # Safety
872    /// The caller must ensure that `node` is guarded by a [`StackWaiterGuard`] that unlinks or
873    /// confirms wakeup of `node` before `node`'s stack frame exits.
874    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    /// Updates `key` and `bucket_idx` while holding the destination [`FutexBucket`] lock.
884    fn set_requeued(&mut self, _bucket: &mut FutexBucket, new_key: FutexKeyValue, new_bucket: u16) {
885        // SAFETY: `self` is unattached and the caller holds the bucket lock(s).
886        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        // SAFETY: `self` is an unattached waiter protected by the bucket lock.
895        unsafe { self.0.as_ref().tid.get() }
896    }
897
898    fn set_tid(&mut self, _bucket: &mut FutexBucket, tid: u32) {
899        // SAFETY: `self` is an unattached waiter protected by the bucket lock.
900        unsafe {
901            self.0.as_ref().tid.set(tid);
902        }
903    }
904
905    /// Consumes the token when a stack-allocated waiter has been unlinked by its own
906    /// [`StackWaiterGuard`].
907    fn into_removed_stack_node(self) {
908        // Nothing to free: the node lives on the waiting thread's stack.
909        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
919/// Intrusive doubly-linked list of [`WaiterNode`]s within a single [`FutexBucket`].
920struct WaiterList {
921    head: Option<NonNull<WaiterNode>>,
922    tail: Option<NonNull<WaiterNode>>,
923}
924
925// SAFETY: Access to `WaiterList` and all linked `WaiterNode`s is synchronized by the enclosing
926// `FutexBucket` mutex.
927unsafe 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        // Free any remaining heap-allocated `External` nodes when the table is dropped.
938        let mut cur = self.head;
939        while let Some(node_ptr) = cur {
940            // SAFETY: Nodes in `self` are valid while linked.
941            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            // SAFETY: Valid while linked under bucket lock.
972            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        // SAFETY: `waiter` is an `UnattachedWaiter` (`prev` and `next` are `None`) and `&mut self`
980        // proves the enclosing bucket lock is held.
981        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        // SAFETY: `waiter` is an `UnattachedWaiter` (`prev` and `next` are `None`) and `&mut self`
999        // proves the enclosing bucket lock is held.
1000        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        // SAFETY: `head` is currently linked in `self`.
1018        Some(unsafe { self.unlink(head) })
1019    }
1020
1021    /// Inserts `waiter` in descending priority order, preserving FIFO order among waiters
1022    /// of equal priority.
1023    fn insert_by_prio(&mut self, waiter: UnattachedWaiter) {
1024        let node_ptr = waiter.0;
1025        // SAFETY: `waiter` is an `UnattachedWaiter`, all nodes in `self` are valid, and `&mut self`
1026        // proves the enclosing bucket lock is held.
1027        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                    // Insert right after `c`.
1034                    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        // `prio` is strictly higher than all existing nodes (or the list is empty).
1052        self.push_front(waiter);
1053    }
1054
1055    /// Inserts an RT-Mutex `waiter` in ascending [`WaiterNode::rt_mutex_waiter_prio`] order
1056    /// (lower numerical values mean higher priority), preserving FIFO order among waiters of
1057    /// equal priority.
1058    fn insert_rt_mutex_by_prio(&mut self, waiter: UnattachedWaiter) {
1059        let node_ptr = waiter.0;
1060        // SAFETY: `waiter` is an `UnattachedWaiter`, all nodes in `self` are valid, and `&mut self`
1061        // proves the enclosing bucket lock is held.
1062        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                    // Insert right after `c`.
1069                    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        // `waiter_prio` is strictly higher priority (lower value) than all existing nodes (or the
1087        // list is empty).
1088        self.push_front(waiter);
1089    }
1090
1091    /// Unlinks `node_ptr` from `self` and returns it as an [`UnattachedWaiter`].
1092    ///
1093    /// # Safety
1094    /// `node_ptr` must be currently linked in `self`.
1095    unsafe fn unlink(&mut self, node_ptr: NonNull<WaiterNode>) -> UnattachedWaiter {
1096        // SAFETY: `node_ptr` is currently linked in `self` and protected by the bucket lock.
1097        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    /// Notifies an unattached waiter node while holding its enclosing [`FutexBucket`] lock
1132    /// (proven by `&mut self`).
1133    ///
1134    /// For stack-allocated nodes, stores `woken = true` (`Ordering::Release`) as the final
1135    /// access to the node so [`StackWaiterGuard`] can return on the fast path without locking
1136    /// the bucket once `woken` is observed.
1137    ///
1138    /// # Safety
1139    /// `waiter` must have been unlinked from `self` under the current lock acquisition (`&mut self`).
1140    unsafe fn wake_and_mark_woken(&mut self, waiter: UnattachedWaiter) -> bool {
1141        let node_ptr = waiter.into_raw();
1142        // SAFETY: `waiter` was unlinked from this bucket under `&mut self`, so we have exclusive
1143        // wake access to `node_ptr`. While `woken` is still `false`, the waiting thread cannot
1144        // exit `StackWaiterGuard::unlink_or_confirm_woken` without acquiring the bucket lock that
1145        // `&mut self` currently holds, so `node_ptr` remains valid throughout `notify()` and the
1146        // `woken` store.
1147        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                // As soon as `woken` is set to `true` above, the waiting thread can take the
1168                // lock-free fast path in `StackWaiterGuard::unlink_or_confirm_woken` and pop its
1169                // stack frame, freeing and invalidating `node_ptr`. Do not access `node_ptr` or
1170                // `woken_ptr` after this point.
1171                true
1172            }
1173        }
1174    }
1175
1176    /// Frees a stale heap-allocated `External` waiter node.
1177    ///
1178    /// # Safety
1179    /// `waiter` must be an `External` node unlinked from `self` under the current lock acquisition.
1180    unsafe fn free_stale_external(&mut self, waiter: UnattachedWaiter) {
1181        let node_ptr = waiter.into_raw();
1182        // SAFETY: `waiter` is an unlinked heap-allocated `External` node owned by this bucket.
1183        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            // SAFETY: `node_ptr` is linked in `self.waiters` under the bucket lock.
1193            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                // SAFETY: `node_ptr` is currently linked in `self.waiters`.
1200                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            // SAFETY: `node_ptr` is linked in `self.waiters` under the bucket lock. We end the
1216            // `&WaiterNode` borrow before unlinking, freeing, or marking `node_ptr` woken.
1217            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                // SAFETY: `node_ptr` is currently linked in `self.waiters`.
1225                unsafe {
1226                    let unattached = self.waiters.unlink(node_ptr);
1227                    self.free_stale_external(unattached);
1228                }
1229                continue;
1230            }
1231
1232            if matches {
1233                // SAFETY: `node_ptr` is currently linked in `self.waiters`.
1234                let did_wake = unsafe {
1235                    let unattached = self.waiters.unlink(node_ptr);
1236                    self.wake_and_mark_woken(unattached)
1237                };
1238                // The send will fail if the receiver is gone, which means nothing was actually
1239                // waiting on the futex.
1240                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            // SAFETY: `node_ptr` is linked in `self.waiters` under the bucket lock. We end the
1261            // `&WaiterNode` borrow before unlinking or freeing `node_ptr`.
1262            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                // SAFETY: `node_ptr` is currently linked in `self.waiters`.
1270                unsafe {
1271                    let unattached = self.waiters.unlink(node_ptr);
1272                    self.free_stale_external(unattached);
1273                }
1274                continue;
1275            }
1276
1277            if matches {
1278                // SAFETY: `node_ptr` is currently linked in `self.waiters`.
1279                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    /// Returns the futex word value to write when unlocking `key`: `0` if no waiters are queued
1295    /// for `key`, the next waiter's TID if exactly one waiter is queued for `key`, or the next
1296    /// waiter's TID with `FUTEX_WAITERS` set if more than one waiter is queued for `key`.
1297    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            // SAFETY: `node_ptr` is linked in `self.rt_mutex_waiters` under the bucket lock.
1302            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    /// Pop the next RT-Mutex waiter for `key` and update remaining waiters' `owner_tid`.
1318    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            // SAFETY: `node_ptr` is linked in `self.rt_mutex_waiters` under the bucket lock.
1324            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                    // SAFETY: `node_ptr` is currently linked in `self.rt_mutex_waiters`.
1332                    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                    // SAFETY: `node_ptr` is linked in `self.rt_mutex_waiters` under the bucket lock.
1346                    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    /// Returns `(key, task_prio, waiter_tid)` for all RT-Mutex waiters in this bucket currently
1360    /// blocked on `owner_tid`, in queue order.
1361    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            // SAFETY: `node_ptr` is linked in `self.rt_mutex_waiters` under the bucket lock.
1366            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
1378/// RAII guard ensuring that a stack-allocated [`WaiterNode`] is either unlinked from its
1379/// [`FutexBucket`] or has completed its wake notification under the bucket lock before the
1380/// stack frame exits.
1381struct 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        // SAFETY: The returned `StackWaiterGuard` ensures `node` is unlinked or woken before `'a`
1395        // ends.
1396        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        // SAFETY: The returned `StackWaiterGuard` ensures `node` is unlinked or woken before `'a`
1409        // ends.
1410        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    /// Ensures `self.node` is no longer linked in any bucket and no waker is accessing it.
1420    ///
1421    /// Returns `true` if `self.node` was still queued (and was removed by this call), or `false`
1422    /// if a waker already dequeued and notified `self.node`.
1423    fn unlink_or_confirm_woken(&self) -> bool {
1424        // Fast path: if the waker already finished `notify()` and stored `woken = true`
1425        // (`Ordering::Release`), that store was the waker's final access to `self.node`, so we
1426        // can return immediately without locking the bucket.
1427        if self.node.woken.load(Ordering::Acquire) {
1428            return false;
1429        }
1430
1431        // Slow path: either the wait timed out / was interrupted while still queued, or we woke
1432        // up before the waker finished storing `woken = true` under the bucket lock. Acquiring
1433        // the bucket mutex serializes with the waker (with Zircon Priority Inheritance if the
1434        // waker was preempted).
1435        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                // `requeue` moved `self.node` to another bucket before we locked `buckets[b]`.
1440                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            // SAFETY: `!woken` and `bucket_idx == b` under `buckets[b]` lock guarantee that
1450            // `self.node` is currently linked in `list`.
1451            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    /// Resolves the outcome of a blocking wait on this table.
1461    ///
1462    /// On success, the waker has already removed the waiter from the queue. On error (e.g.
1463    /// ETIMEDOUT, EINTR), this unlinks the waiter from the queue if it is still queued.
1464    ///
1465    /// Finding that the waiter was already dequeued and woken means a concurrent wake dequeued
1466    /// the waiter while it was waking up for another reason. That wake counted this waiter as
1467    /// woken, so report success rather than dropping the wake on the floor.
1468    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        // Find a key2 that hashes to the same bucket as key1.
1532        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        // Find a key2 that hashes to a different bucket than key1.
1553        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    /// A wake that races with an interruption must not be lost.
1573    ///
1574    /// When a signal interrupts a waiter, the waiter can still be sitting in the queue when
1575    /// another thread issues a wake. That wake counts the waiter as woken, so the waiter must
1576    /// notice that it has been dequeued and report the wake instead of the interruption.
1577    #[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        // A signal interrupts the waiter before it is dequeued.
1588        node.event().interrupt();
1589
1590        // The waker still sees the waiter in the queue and counts it as woken.
1591        assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
1592
1593        // The interrupted waiter must therefore observe that it is no longer queued, which tells
1594        // it to report the wake rather than the interruption.
1595        assert_eq!(guard.resolve(error!(EINTR)), Ok(()));
1596    }
1597
1598    /// The boot-deadline flavor of `FUTEX_WAIT` must resolve the same race the same way.
1599    #[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        // The waker dequeues the waiter and counts it as woken, whether or not the waiter was
1609        // about to give up on its deadline.
1610        assert_eq!(wake_key(&table, &key, 1, FUTEX_BITSET_MATCH_ANY), 1);
1611        assert_eq!(guard.resolve(error!(ETIMEDOUT)), Ok(()));
1612    }
1613
1614    /// Removing one waiter on a key must leave other waiters (including different waiter kinds)
1615    /// queued and wakeable.
1616    #[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    /// A PI-mutex waiter must learn whether it was still queued.
1657    ///
1658    /// `lock_pi` relies on that answer: a waiter that `unlock_pi` dequeued has been made the
1659    /// owner of the mutex, and must report success even if its wait failed.
1660    #[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        // The handoff performed by `unlock_pi` takes the waiter out of the queue.
1678        {
1679            let mut bucket = table.buckets[b].lock();
1680            let popped = table.pop_rt_mutex_waiter(b, &mut bucket, &key).unwrap();
1681            // SAFETY: `popped` was unlinked from `bucket` under the current lock acquisition.
1682            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        // First two waiters should now be on key2 in FIFO order; third remains on key1.
1717        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        // Wake 1 waiter on key1 (`n1`) and requeue the remaining 2 (`n2`, `n3`) to `key2` (`b2`).
1753        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        // Cancel `n2` after it moved to `b2`, then wake `n3` on `key2`.
1760        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        } // token is dropped here, so it becomes stale
1787
1788        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        } // token is dropped here, so it becomes stale
1807
1808        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        } // token is dropped here, so it becomes stale
1827
1828        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        // Dropping `table` must drop the boxed `External` waiter nodes and their `oneshot::Sender`s.
1851        assert_eq!(receiver1.try_recv(), Err(oneshot::Canceled));
1852        assert_eq!(receiver2.try_recv(), Err(oneshot::Canceled));
1853    }
1854
1855    /// Waiters with different keys that hash to the same bucket must not interfere.
1856    #[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        // Waking key1 must only wake n1 even though n2 has higher priority in the same bucket.
1880        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        // Non-RT waiters share DEFAULT_PRIO (20) for queue ordering and are queued FIFO, so tid 20
1916        // remains the top waiter and boosts owner 10 to priority 25.
1917        assert_eq!(table.get_rt_mutex_priority_boost(10), Some(25));
1918
1919        // A real-time waiter (priority -2) queues ahead of normal waiters and becomes the top
1920        // waiter.
1921        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        // Popping hands off to real-time waiter tid 40 first and updates owner_tid to 40.
1925        {
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            // SAFETY: `popped_rt` was unlinked from `bucket` under the current lock acquisition.
1930            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        // Next handoff goes to tid 20 (the first normal waiter in FIFO order), which now sees
1939        // tid 30 (priority 20) as its top waiter.
1940        {
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            // SAFETY: `popped1` was unlinked from `bucket` under the current lock acquisition.
1945            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        // Popping the last waiter clears the boost and active bucket mask immediately, even before
1954        // the woken waiter runs `resolve`.
1955        {
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            // SAFETY: `popped2` was unlinked from `bucket` under the current lock acquisition.
1960            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        // Middle waiter tid 20 (base prio 25) is blocked on owner tid 10.
1979        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        // Upstream waiter tid 30 (base prio 20) blocks on middle waiter tid 20, transitively
1987        // boosting owner tid 10 to priority 20.
1988        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        // When upstream waiter tid 30 times out while tid 20 is still blocked on tid 10, the
1997        // transitive boost on tid 10 must revert to tid 20's base priority (25).
1998        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        // A second waiter on `key1` (tid 21, base prio 25) that receives a transitive real-time
2003        // boost (-5) overtakes the earlier non-RT waiter on `key1`.
2004        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        // When `owner_tid` holds multiple PI mutexes (in the same bucket or in a higher-indexed
2023        // bucket), `get_rt_mutex_priority_boost` compares the top waiter's `task_prio` across all
2024        // held mutexes rather than stopping at the first non-RT waiter in bucket/queue order.
2025        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        // A multi-node lock cycle (`20 -> 10 -> 20`) must stop at the revisited `owner_tid`
2060        // rather than walking the cycle and feeding `20`'s own priority (25) back through `10`
2061        // (30) into `20`.
2062        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    /// Private futexes at the same virtual address in two different processes (`MemoryManager`s)
2076    /// must not collide.
2077    #[::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                // `mid1_node` and `mid2_node` are inserted in the middle (after `high_node` and
2133                // before `low_node`) and must preserve FIFO order between equal priorities.
2134                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        // Requeuing a higher-priority waiter onto a destination key that already has a
2156        // lower-priority waiter queued must insert it ahead of the lower-priority waiter.
2157        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    /// `unlock_pi` must hand the mutex to the next live waiter after an earlier waiter cancelled,
2184    /// preserve `FUTEX_WAITERS` while further waiters remain, and leave the waiter queue intact
2185    /// if the futex word CAS fails.
2186    #[::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            // The current task owns the mutex and has waiters queued behind it.
2208            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                // `_cancelled_guard` drops here, unlinking `cancelled_node` automatically.
2247            }
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            // Make the mapping read-only so `unlock_pi` can read `expected_value`, peek
2266            // `target_value`, and fail the handoff CAS with `EACCES` without unlinking `next_node`
2267            // or disturbing the real-time waiter at the head of `rt_mutex_waiters`.
2268            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                        // Spawn waker thread while main thread is page-faulting before acquiring
2356                        // the lock.
2357                        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                        // Verify waker completes immediately without being blocked by the page
2368                        // fault.
2369                        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                        // Supply pages to unblock the main thread's page fault
2376                        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            // This will pre-fault mapped_addr before locking the FutexTable
2386            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            // A mismatched value must still report EAGAIN ahead of an expired deadline.
2468            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}