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, ProtectionFlags};
7use crate::task::{CurrentTask, EventHandler, SignalHandler, SignalHandlerInner, Task, Waiter};
8use futures::channel::oneshot;
9use starnix_sync::{FutexTableStateLock, InterruptibleEvent, LockDepMutex};
10use starnix_types::futex_address::FutexAddress;
11use starnix_uapi::errors::Errno;
12use starnix_uapi::user_address::UserAddress;
13use starnix_uapi::{FUTEX_BITSET_MATCH_ANY, FUTEX_TID_MASK, FUTEX_WAITERS, errno, error};
14use std::collections::hash_map::Entry;
15use std::collections::{HashMap, VecDeque};
16use std::hash::Hash;
17use std::sync::{Arc, Weak};
18
19/// A table of futexes.
20///
21/// Each 32-bit aligned address in an address space can potentially have an associated futex that
22/// userspace can wait upon. This table is a sparse representation that has an actual WaitQueue
23/// only for those addresses that have ever actually had a futex operation performed on them.
24pub struct FutexTable<Key: FutexKey> {
25    /// The futexes associated with each address in each VMO.
26    ///
27    /// This HashMap is populated on-demand when futexes are used.
28    state: LockDepMutex<FutexTableState<Key>, FutexTableStateLock>,
29}
30
31impl<Key: FutexKey> Default for FutexTable<Key> {
32    fn default() -> Self {
33        Self { state: LockDepMutex::new(FutexTableState::default()) }
34    }
35}
36
37impl<Key: FutexKey> FutexTable<Key> {
38    /// Resolves the outcome of a blocking wait on this table.
39    ///
40    /// On success, the waker has already removed the waiter from the queue and there is nothing
41    /// left to do. On error (e.g. ETIMEDOUT, EINTR), `remove` takes the waiter out of the queue
42    /// to prevent a memory leak.
43    ///
44    /// Finding nothing to remove means a concurrent wake dequeued the waiter while it was waking
45    /// up for another reason. That wake counted this waiter as woken, so report success rather
46    /// than dropping the wake on the floor.
47    fn resolve_wait(
48        &self,
49        result: Result<(), Errno>,
50        remove: impl FnOnce(&mut FutexTableState<Key>) -> bool,
51    ) -> Result<(), Errno> {
52        result.or_else(|e| if remove(&mut self.state.lock()) { Err(e) } else { Ok(()) })
53    }
54
55    /// Wait on the futex at the given address given a boot deadline.
56    ///
57    /// See FUTEX_WAIT when passed a deadline in CLOCK_REALTIME.
58    pub fn wait_boot(
59        &self,
60        current_task: &CurrentTask,
61        addr: UserAddress,
62        value: u32,
63        mask: u32,
64        deadline: zx::BootInstant,
65        timer_slack: zx::BootDuration,
66    ) -> Result<(), Errno> {
67        let addr = FutexAddress::try_from(addr)?;
68        // Pre-fault the address before acquiring the FutexTable lock to make page faults or
69        // memory reclamation under the lock highly unlikely.
70        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
71        // supported by Zircon.
72        let _ = current_task.mm()?.atomic_load_u32_acquire(addr)?;
73        let mut state = self.state.lock();
74        // As the state is locked, no wake can happen before the waiter is registered.
75        // If the addr is remapped, we will read stale data, but we will not miss a futex wake.
76        // Acquire ordering to synchronize with userspace modifications to the value on other
77        // threads.
78        let loaded_value = current_task.mm()?.atomic_load_u32_acquire(addr)?;
79        if value != loaded_value {
80            return error!(EAGAIN);
81        }
82
83        let key = Key::get(current_task, addr)?;
84        let waiter = Arc::new(Waiter::new());
85        let timer = zx::BootTimer::create();
86        let signal_handler = SignalHandler {
87            inner: SignalHandlerInner::None,
88            event_handler: EventHandler::None,
89            err_code: Some(errno!(ETIMEDOUT)),
90        };
91        waiter
92            .wake_on_zircon_signals(&timer, zx::Signals::TIMER_SIGNALED, signal_handler)
93            .expect("wait can only fail in OOM conditions");
94        timer
95            .set(deadline, timer_slack)
96            .expect("timer set cannot fail with valid handles and slack");
97        state.get_waiters_or_default(key.clone()).add(FutexWaiter {
98            mask,
99            notifiable: FutexNotifiable::new_internal_boot(Arc::downgrade(&waiter)),
100        });
101        std::mem::drop(state);
102
103        let result = waiter.wait(current_task);
104        self.resolve_wait(result, |state| {
105            state.remove_waiter_from_queue(key, &WaiterMatcher::BootWaiter(&waiter))
106        })
107    }
108
109    /// Wait on the futex at the given address.
110    ///
111    /// See FUTEX_WAIT.
112    pub fn wait(
113        &self,
114        current_task: &CurrentTask,
115        addr: UserAddress,
116        value: u32,
117        mask: u32,
118        deadline: zx::MonotonicInstant,
119    ) -> Result<(), Errno> {
120        let addr = FutexAddress::try_from(addr)?;
121        // Pre-fault the address before acquiring the FutexTable lock to make page faults or
122        // memory reclamation under the lock highly unlikely.
123        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
124        // supported by Zircon.
125        let _ = current_task.mm()?.atomic_load_u32_acquire(addr)?;
126        let mut state = self.state.lock();
127        // As the state is locked, no wake can happen before the waiter is registered.
128        // If the addr is remapped, we will read stale data, but we will not miss a futex wake.
129        // Acquire ordering to synchronize with userspace modifications to the value on other
130        // threads.
131        let loaded_value = current_task.mm()?.atomic_load_u32_acquire(addr)?;
132        if value != loaded_value {
133            return error!(EAGAIN);
134        }
135
136        let key = Key::get(current_task, addr)?;
137        let event = InterruptibleEvent::new();
138        let guard = event.begin_wait();
139        state.get_waiters_or_default(key.clone()).add(FutexWaiter {
140            mask,
141            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&event)),
142        });
143        std::mem::drop(state);
144
145        let result = current_task.block_until(guard, deadline);
146        self.resolve_wait(result, |state| {
147            state.remove_waiter_from_queue(key, &WaiterMatcher::Event(&event))
148        })
149    }
150
151    /// Wake the given number of waiters on futex at the given address. Returns the number of
152    /// waiters actually woken.
153    ///
154    /// See FUTEX_WAKE.
155    pub fn wake(
156        &self,
157        task: &Task,
158        addr: UserAddress,
159        count: usize,
160        mask: u32,
161    ) -> Result<usize, Errno> {
162        let addr = FutexAddress::try_from(addr)?;
163        let key = Key::get(task, addr)?;
164        Ok(self.state.lock().wake(key, count, mask))
165    }
166
167    /// Requeue the waiters to another address.
168    ///
169    /// See FUTEX_CMP_REQUEUE
170    pub fn requeue(
171        &self,
172        current_task: &CurrentTask,
173        addr: UserAddress,
174        wake_count: usize,
175        requeue_count: usize,
176        new_addr: UserAddress,
177        expected_value: Option<u32>,
178    ) -> Result<usize, Errno> {
179        let addr = FutexAddress::try_from(addr)?;
180        let new_addr = FutexAddress::try_from(new_addr)?;
181        if expected_value.is_some() {
182            // Pre-fault the address before acquiring the FutexTable lock to make page faults or
183            // memory reclamation under the lock highly unlikely.
184            // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
185            // supported by Zircon.
186            let _ = current_task.mm()?.atomic_load_u32_acquire(addr)?;
187        }
188        let key = Key::get(current_task, addr)?;
189        let new_key = Key::get(current_task, new_addr)?;
190        let mut state = self.state.lock();
191        if let Some(expected) = expected_value {
192            // Use acquire ordering here to synchronize with mutex impls that store w/ release
193            // ordering.
194            let value = current_task.mm()?.atomic_load_u32_acquire(addr)?;
195            if value != expected {
196                return error!(EAGAIN);
197            }
198        }
199
200        Ok(state.requeue(key, new_key, wake_count, requeue_count))
201    }
202
203    /// Lock the futex at the given address.
204    ///
205    /// See FUTEX_LOCK_PI.
206    pub fn lock_pi(
207        &self,
208        current_task: &CurrentTask,
209        addr: UserAddress,
210        deadline: zx::MonotonicInstant,
211    ) -> Result<(), Errno> {
212        let addr = FutexAddress::try_from(addr)?;
213        let mm = current_task.mm()?;
214        // Perform a dummy CAS to pre-fault the page with write permissions (e.g. for COW pages)
215        // before acquiring the FutexTable lock to make page faults or memory reclamation under
216        // the lock highly unlikely.
217        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
218        // supported by Zircon.
219        if let Ok(current) = mm.atomic_load_u32_relaxed(addr) {
220            let _ = mm.atomic_compare_exchange_u32_acq_rel(addr, current, current);
221        }
222        let mut state = self.state.lock();
223        // As the state is locked, no unlock 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 unlock.
225        let key = Key::get(current_task, addr)?;
226
227        let tid = current_task.get_tid() as u32;
228
229        // Use a relaxed ordering because the compare/exchange below creates a synchronization
230        // point with userspace threads in the success case. No synchronization is required in
231        // failure cases.
232        let mut current_value = mm.atomic_load_u32_relaxed(addr)?;
233        let new_owner_tid = loop {
234            let new_owner_tid = current_value & FUTEX_TID_MASK;
235            if new_owner_tid == tid {
236                // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
237                //
238                //   EDEADLK
239                //          (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,
240                //          FUTEX_CMP_REQUEUE_PI) The futex word at uaddr is
241                //          already locked by the caller.
242                return error!(EDEADLOCK);
243            }
244
245            if current_value == 0 {
246                // Use acq/rel ordering to synchronize with acquire ordering on userspace lock ops
247                // and with the release ordering on userspace unlock ops.
248                match mm.atomic_compare_exchange_weak_u32_acq_rel(addr, current_value, tid) {
249                    CompareExchangeResult::Success => return Ok(()),
250                    CompareExchangeResult::Stale { observed } => {
251                        current_value = observed;
252                        continue;
253                    }
254                    CompareExchangeResult::Error(e) => return Err(e),
255                }
256            }
257
258            // Use acq/rel ordering to synchronize with acquire ordering on userspace lock ops and
259            // with the release ordering on userspace unlock ops.
260            let target_value = current_value | FUTEX_WAITERS;
261            match mm.atomic_compare_exchange_u32_acq_rel(addr, current_value, target_value) {
262                CompareExchangeResult::Success => (),
263                CompareExchangeResult::Stale { observed } => {
264                    current_value = observed;
265                    continue;
266                }
267                CompareExchangeResult::Error(e) => return Err(e),
268            }
269            break new_owner_tid;
270        };
271
272        let event = InterruptibleEvent::new();
273        let guard = event.begin_wait();
274        let notifiable = FutexNotifiable::new_internal(Arc::downgrade(&event));
275        state
276            .get_rt_mutex_waiters_or_default(key.clone())
277            .push_back(RtMutexWaiter { tid, notifiable });
278        std::mem::drop(state);
279
280        // ESRCH  (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,
281        //        FUTEX_CMP_REQUEUE_PI) The thread ID in the futex word at
282        //        uaddr does not exist.
283        let result = current_task
284            .get_task(new_owner_tid as i32)
285            .ok()
286            .and_then(|o| o.running_state().unwrap().thread.get().map(|t| Arc::clone(&t.thread)))
287            .map_or_else(
288                || error!(ESRCH),
289                |owner| current_task.block_with_owner_until(guard, &owner, deadline),
290            );
291
292        // Being gone from the queue means `unlock_pi` picked this waiter as the new owner and
293        // already published its tid in the futex word, so the mutex is held even though the wait
294        // itself failed. Reporting the error would leave the mutex locked by a thread that
295        // believes it does not own it.
296        self.resolve_wait(result, |state| {
297            state.remove_rt_mutex_waiter_from_queue(key, &WaiterMatcher::Event(&event))
298        })
299    }
300
301    /// Unlock the futex at the given address.
302    ///
303    /// See FUTEX_UNLOCK_PI.
304    pub fn unlock_pi(&self, current_task: &CurrentTask, addr: UserAddress) -> Result<(), Errno> {
305        let addr = FutexAddress::try_from(addr)?;
306        let mm = current_task.mm()?;
307        // Perform a placeholder CAS to pre-fault the page with write permissions (e.g. for COW pages)
308        // before acquiring the FutexTable lock to make page faults or memory reclamation under
309        // the lock highly unlikely.
310        // TODO(https://fxbug.dev/548025031): Implement a non-blocking try_load and retry loop once
311        // supported by Zircon.
312        if let Ok(current) = mm.atomic_load_u32_relaxed(addr) {
313            let _ = mm.atomic_compare_exchange_u32_acq_rel(addr, current, current);
314        }
315        let mut state = self.state.lock();
316        let tid = current_task.get_tid() as u32;
317
318        let key = Key::get(current_task, addr)?;
319
320        // Use a relaxed ordering because the compare/exchange below creates a synchronization
321        // point with userspace threads in the success case. No synchronization is required in
322        // failure cases.
323        let mut expected_value = mm.atomic_load_u32_relaxed(addr)?;
324        if expected_value & FUTEX_TID_MASK != tid {
325            // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
326            //
327            //   EPERM  (FUTEX_UNLOCK_PI) The caller does not own the lock
328            //          represented by the futex word.
329            return error!(EPERM);
330        }
331
332        loop {
333            let maybe_waiter = state.pop_rt_mutex_waiter(key.clone());
334            let target_value = maybe_waiter.as_ref().map_or(0, |waiter| waiter.tid);
335
336            // Use acq/rel ordering to synchronize with acquire ordering on userspace lock ops and
337            // with the release ordering on userspace unlock ops.
338            let handoff =
339                mm.atomic_compare_exchange_u32_acq_rel(addr, expected_value, target_value);
340            if let Err(e) = handoff_result(handoff) {
341                if let Some(waiter) = maybe_waiter {
342                    state.unpop_rt_mutex_waiter(key, waiter);
343                }
344                return Err(e);
345            }
346            // The futex word now holds the value just written, which is what a further handoff
347            // has to compare against.
348            expected_value = target_value;
349
350            let Some(mut waiter) = maybe_waiter else {
351                // We can stop trying to notify a thread if there are no more waiters.
352                break;
353            };
354
355            if waiter.notifiable.notify() {
356                break;
357            }
358
359            // If we couldn't notify the waiter, then we need to pull the next thread off the
360            // waiter list.
361        }
362
363        Ok(())
364    }
365}
366
367/// Maps the outcome of the compare-exchange that hands a PI mutex over to its next owner.
368fn handoff_result(result: CompareExchangeResult<u32>) -> Result<(), Errno> {
369    match result {
370        CompareExchangeResult::Success => Ok(()),
371        // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
372        //
373        //   EINVAL (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,
374        //       FUTEX_UNLOCK_PI) The kernel detected an inconsistency
375        //       between the user-space state at uaddr and the kernel
376        //       state.  This indicates either state corruption or that the
377        //       kernel found a waiter on uaddr which is waiting via
378        //       FUTEX_WAIT or FUTEX_WAIT_BITSET.
379        CompareExchangeResult::Stale { .. } => error!(EINVAL),
380        // From <https://man7.org/linux/man-pages/man2/futex.2.html>:
381        //
382        //   EACCES No read access to the memory of a futex word.
383        CompareExchangeResult::Error(_) => error!(EACCES),
384    }
385}
386
387impl FutexTable<SharedFutexKey> {
388    /// Wait on the futex at the given offset in the memory.
389    ///
390    /// Returns a receiver that will be signaled when the futex is woken, and an
391    /// `Arc<()>` token that must be kept alive by the caller for the duration of the
392    /// wait. If the caller drops the token (e.g., if the external client
393    /// disconnects), the waiter is marked as stale and will be garbage-collected by the
394    /// next futex operation on this table.
395    ///
396    /// See FUTEX_WAIT.
397    pub fn external_wait(
398        &self,
399        memory: MemoryObject,
400        offset: u64,
401        value: u32,
402        mask: u32,
403    ) -> Result<(Arc<()>, oneshot::Receiver<()>), Errno> {
404        let key = SharedFutexKey::new(&memory, offset);
405        let mut state = self.state.lock();
406        // As the state is locked, no wake can happen before the waiter is registered.
407        Self::external_check_futex_value(&memory, offset, value)?;
408
409        let token = Arc::new(());
410        let (sender, receiver) = oneshot::channel::<()>();
411        state.get_waiters_or_default(key).add(FutexWaiter {
412            mask,
413            notifiable: FutexNotifiable::new_external(Arc::downgrade(&token), sender),
414        });
415        Ok((token, receiver))
416    }
417
418    /// Wake the given number of waiters on futex at the given offset in the memory. Returns the
419    /// number of waiters actually woken.
420    ///
421    /// See FUTEX_WAKE.
422    pub fn external_wake(
423        &self,
424        memory: MemoryObject,
425        offset: u64,
426        count: usize,
427        mask: u32,
428    ) -> Result<usize, Errno> {
429        Ok(self.state.lock().wake(SharedFutexKey::new(&memory, offset), count, mask))
430    }
431
432    pub fn external_requeue(
433        &self,
434        first_memory: MemoryObject,
435        first_offset: u64,
436        second_memory: Option<MemoryObject>,
437        second_offset: u64,
438        wake_count: usize,
439        requeue_count: usize,
440        expected_value: Option<u32>,
441    ) -> Result<usize, Errno> {
442        let first_key = SharedFutexKey::new(&first_memory, first_offset);
443        let second_key = match second_memory.as_ref() {
444            Some(second_memory) => SharedFutexKey::new(second_memory, second_offset),
445            None => SharedFutexKey::new(&first_memory, second_offset),
446        };
447        // If/when we move from a single table mutex to a mutex per futex, we'll likely want to
448        // define a consistent SharedFutexKey sort order independent of which is "first" and which
449        // is "second" in this call. Then we can acquire each of the two mutexes corresponding to
450        // each of the two futexes per that sort order. This way, we can be holding both mutexes to
451        // make the requeue atomic despite each futex having its own mutex, while avoiding
452        // deadlocks. But for now we lock the whole FutexTable.
453        let mut state = self.state.lock();
454        if let Some(expected) = expected_value {
455            // The state being locked is how this is included in the set of atomic changes.
456            Self::external_check_futex_value(&first_memory, first_offset, expected)?;
457        }
458        Ok(state.requeue(first_key, second_key, wake_count, requeue_count))
459    }
460
461    fn external_check_futex_value(
462        memory: &MemoryObject,
463        offset: u64,
464        value: u32,
465    ) -> Result<(), Errno> {
466        let loaded_value = {
467            // TODO: This read should be atomic.
468            let mut buf = [0u8; 4];
469            memory.read(&mut buf, offset).map_err(|_| errno!(EINVAL))?;
470            u32::from_ne_bytes(buf)
471        };
472        if loaded_value != value {
473            return error!(EAGAIN);
474        }
475        Ok(())
476    }
477}
478
479pub trait FutexKey: Sized + Ord + Hash + Clone {
480    fn get(task: &Task, addr: FutexAddress) -> Result<Self, Errno>;
481    fn get_table_from_task(task: &Task) -> Result<Arc<FutexTable<Self>>, Errno>;
482}
483
484#[derive(Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
485pub struct PrivateFutexKey {
486    addr: FutexAddress,
487}
488
489impl FutexKey for PrivateFutexKey {
490    fn get(_task: &Task, addr: FutexAddress) -> Result<Self, Errno> {
491        Ok(PrivateFutexKey { addr })
492    }
493
494    fn get_table_from_task(task: &Task) -> Result<Arc<FutexTable<Self>>, Errno> {
495        Ok(task.mm()?.futex.clone())
496    }
497}
498
499#[derive(Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
500pub struct SharedFutexKey {
501    // No chance of collisions since koids are never reused:
502    // https://fuchsia.dev/fuchsia-src/concepts/kernel/concepts#kernel_object_ids
503    koid: zx::Koid,
504    offset: u64,
505}
506
507impl FutexKey for SharedFutexKey {
508    fn get(task: &Task, addr: FutexAddress) -> Result<Self, Errno> {
509        let (memory, offset) = task.mm()?.get_mapping_memory(addr.into(), ProtectionFlags::READ)?;
510        Ok(SharedFutexKey::new(&memory, offset))
511    }
512
513    fn get_table_from_task(task: &Task) -> Result<Arc<FutexTable<Self>>, Errno> {
514        Ok(task.kernel().shared_futexes.clone())
515    }
516}
517
518impl SharedFutexKey {
519    fn new(memory: &MemoryObject, offset: u64) -> Self {
520        Self { koid: memory.get_koid(), offset }
521    }
522}
523
524struct FutexTableState<Key: FutexKey> {
525    waiters: HashMap<Key, FutexWaiters>,
526    rt_mutex_waiters: HashMap<Key, VecDeque<RtMutexWaiter>>,
527}
528
529impl<Key: FutexKey> Default for FutexTableState<Key> {
530    fn default() -> Self {
531        Self { waiters: Default::default(), rt_mutex_waiters: Default::default() }
532    }
533}
534
535impl<Key: FutexKey> FutexTableState<Key> {
536    /// Returns the FutexWaiters for a given address, creating an empty one if none is registered.
537    fn get_waiters_or_default(&mut self, key: Key) -> &mut FutexWaiters {
538        self.waiters.entry(key).or_default()
539    }
540
541    fn wake(&mut self, key: Key, count: usize, mask: u32) -> usize {
542        let entry = self.waiters.entry(key);
543        match entry {
544            Entry::Vacant(_) => 0,
545            Entry::Occupied(mut entry) => {
546                let count = entry.get_mut().notify(mask, count);
547                if entry.get().is_empty() {
548                    entry.remove();
549                }
550                count
551            }
552        }
553    }
554
555    fn requeue(
556        &mut self,
557        key: Key,
558        new_key: Key,
559        wake_count: usize,
560        requeue_count: usize,
561    ) -> usize {
562        let woken;
563        let to_requeue;
564        match self.waiters.entry(key) {
565            Entry::Vacant(_) => return 0,
566            Entry::Occupied(mut entry) => {
567                // Wake up at most `wake_count` waiters.
568                woken = entry.get_mut().notify(FUTEX_BITSET_MATCH_ANY, wake_count);
569
570                // Dequeue up to `requeue_count` waiters to requeue below.
571                to_requeue = entry.get_mut().split_for_requeue(requeue_count);
572
573                if entry.get().is_empty() {
574                    entry.remove();
575                }
576            }
577        }
578
579        let requeued = to_requeue.0.len();
580        if !to_requeue.is_empty() {
581            self.get_waiters_or_default(new_key).transfer(to_requeue);
582        }
583
584        woken + requeued
585    }
586
587    /// Returns the RT-Mutex waiters queue for a given address, creating an empty queue if none is
588    /// registered.
589    fn get_rt_mutex_waiters_or_default(&mut self, key: Key) -> &mut VecDeque<RtMutexWaiter> {
590        self.rt_mutex_waiters.entry(key).or_default()
591    }
592
593    /// Pop the next RT-Mutex for the given address.
594    fn pop_rt_mutex_waiter(&mut self, key: Key) -> Option<RtMutexWaiter> {
595        let entry = self.rt_mutex_waiters.entry(key);
596        match entry {
597            Entry::Vacant(_) => None,
598            Entry::Occupied(mut entry) => {
599                let mut waiter = entry.get_mut().pop_front();
600                // Clean up the hash map entry if the queue is empty. We do this
601                // regardless of whether `pop_front` returned a waiter or `None`,
602                // effectively garbage collecting erroneously empty map entries.
603                if entry.get().is_empty() {
604                    entry.remove();
605                } else if let Some(waiter) = &mut waiter {
606                    waiter.tid |= FUTEX_WAITERS;
607                }
608                waiter
609            }
610        }
611    }
612
613    /// Puts a waiter popped for a handoff that did not happen back at the head of its queue.
614    ///
615    /// Clearing `FUTEX_WAITERS` undoes what `pop_rt_mutex_waiter` set, restoring the tid the
616    /// waiter was queued with. The waiter must go back, otherwise it would conclude from its
617    /// absence that it was made the new owner of a mutex it does not hold.
618    fn unpop_rt_mutex_waiter(&mut self, key: Key, mut waiter: RtMutexWaiter) {
619        waiter.tid &= FUTEX_TID_MASK;
620        self.get_rt_mutex_waiters_or_default(key).push_front(waiter);
621    }
622
623    /// Removes the waiter designated by `matcher` from the `FUTEX_WAIT` queue it is waiting on.
624    ///
625    /// Returns whether the waiter was still queued. A `false` return means the waiter was
626    /// already dequeued by a concurrent wake.
627    fn remove_waiter_from_queue(&mut self, key: Key, matcher: &WaiterMatcher<'_>) -> bool {
628        search_and_remove_waiter(&mut self.waiters, key, matcher)
629    }
630
631    /// Removes a PI-mutex (`FUTEX_LOCK_PI`) waiter.
632    ///
633    /// Returns whether the waiter was still queued. A `false` return means `unlock_pi` already
634    /// dequeued it to make it the new owner of the mutex.
635    fn remove_rt_mutex_waiter_from_queue(&mut self, key: Key, matcher: &WaiterMatcher<'_>) -> bool {
636        search_and_remove_waiter(&mut self.rt_mutex_waiters, key, matcher)
637    }
638}
639
640/// Searches `queues` for the waiter designated by `matcher` and removes it from the queue it is
641/// waiting on.
642///
643/// This uses a two-step approach:
644/// 1. O(1) Fast path: Check the `key` where the waiter originally went to sleep.
645/// 2. O(N) Fallback: If not found (e.g. moved via `FUTEX_REQUEUE`), scan all futexes.
646///
647/// Queues left empty along the way are dropped from `queues`, whether or not they held the
648/// waiter.
649///
650/// Returns whether the waiter was still queued. A `false` return means the waiter was already
651/// dequeued by a concurrent wake.
652fn search_and_remove_waiter<Key: FutexKey, Queue: WaiterQueue>(
653    queues: &mut HashMap<Key, Queue>,
654    key: Key,
655    matcher: &WaiterMatcher<'_>,
656) -> bool {
657    if let Entry::Occupied(mut entry) = queues.entry(key) {
658        let found = entry.get_mut().remove_waiter(matcher);
659        if entry.get().is_empty() {
660            entry.remove();
661        }
662        if found {
663            return true;
664        }
665    }
666
667    // The waiter sits in at most one queue, so stop looking once it turns up. The scan still
668    // visits the remaining queues to drop the ones left empty.
669    let mut found = false;
670    queues.retain(|_, waiters| {
671        if !found {
672            found = waiters.remove_waiter(matcher);
673        }
674        !waiters.is_empty()
675    });
676    found
677}
678
679/// A queue of waiters parked on a single futex key.
680trait WaiterQueue {
681    /// Removes the waiter designated by `matcher` from the queue, also garbage collecting stale
682    /// waiters.
683    ///
684    /// Returns whether that waiter was in the queue. Removing stale waiters alone does not count
685    /// as a match: a caller that concludes it was dequeued by a wake would otherwise report a
686    /// wake that never happened.
687    fn remove_waiter(&mut self, matcher: &WaiterMatcher<'_>) -> bool;
688
689    /// Returns whether the queue holds no waiter.
690    fn is_empty(&self) -> bool;
691}
692
693/// Designates the waiter a removal operation is looking for.
694///
695/// The queues mix the waiters of every flavor of `FUTEX_WAIT`, so a removal needs to describe
696/// which one of them belongs to the caller.
697enum WaiterMatcher<'a> {
698    /// The waiter blocked on the given `InterruptibleEvent`. See `FutexNotifiable::Internal`.
699    Event(&'a Arc<InterruptibleEvent>),
700
701    /// The waiter blocked on the given `Waiter`. See `FutexNotifiable::InternalBoot`.
702    BootWaiter(&'a Arc<Waiter>),
703}
704
705impl WaiterMatcher<'_> {
706    /// Returns whether `notifiable` designates the waiter this matcher is looking for.
707    fn matches(&self, notifiable: &FutexNotifiable) -> bool {
708        match (self, notifiable) {
709            (Self::Event(event), FutexNotifiable::Internal(weak)) => {
710                weak.upgrade().is_some_and(|strong| Arc::ptr_eq(&strong, event))
711            }
712            (Self::BootWaiter(waiter), FutexNotifiable::InternalBoot(weak)) => {
713                weak.upgrade().is_some_and(|strong| Arc::ptr_eq(&strong, waiter))
714            }
715            _ => false,
716        }
717    }
718}
719
720/// Abstraction over a process waiting on a Futex that can be notified.
721enum FutexNotifiable {
722    /// An internal process waiting on a Futex.
723    Internal(Weak<InterruptibleEvent>),
724    // An internal process waiting on a Futex with a boot deadline.
725    InternalBoot(Weak<Waiter>),
726    /// An external process waiting on a Futex.
727    // The sender needs to be an option so that one can send the notification while only holding a
728    // mut reference on the ExternalWaiter.
729    External(Weak<()>, Option<oneshot::Sender<()>>),
730}
731
732impl FutexNotifiable {
733    fn new_internal(event: Weak<InterruptibleEvent>) -> Self {
734        Self::Internal(event)
735    }
736
737    fn new_internal_boot(waiter: Weak<Waiter>) -> Self {
738        Self::InternalBoot(waiter)
739    }
740
741    fn new_external(token: Weak<()>, sender: oneshot::Sender<()>) -> Self {
742        Self::External(token, Some(sender))
743    }
744
745    /// Tries to notify the process. Returns `true` is the process have been notified. Returns
746    /// `false` otherwise. This means the process is stale and will never be available again.
747    fn notify(&mut self) -> bool {
748        match self {
749            Self::Internal(event) => {
750                if let Some(event) = event.upgrade() {
751                    event.notify();
752                    true
753                } else {
754                    false
755                }
756            }
757            Self::InternalBoot(waiter) => {
758                if let Some(waiter) = waiter.upgrade() {
759                    waiter.notify();
760                    true
761                } else {
762                    false
763                }
764            }
765            Self::External(_, sender) => {
766                if let Some(sender) = sender.take() {
767                    sender.send(()).is_ok()
768                } else {
769                    false
770                }
771            }
772        }
773    }
774
775    fn is_stale(&self) -> bool {
776        match self {
777            Self::Internal(weak) => weak.strong_count() == 0,
778            Self::External(weak, _) => weak.strong_count() == 0,
779            Self::InternalBoot(weak) => weak.strong_count() == 0,
780        }
781    }
782}
783
784struct FutexWaiter {
785    mask: u32,
786    notifiable: FutexNotifiable,
787}
788
789#[derive(Default)]
790struct FutexWaiters(VecDeque<FutexWaiter>);
791
792impl FutexWaiters {
793    fn add(&mut self, waiter: FutexWaiter) {
794        self.0.push_back(waiter);
795    }
796
797    fn notify(&mut self, mask: u32, count: usize) -> usize {
798        let mut woken = 0;
799        self.0.retain_mut(|waiter| {
800            if woken == count || waiter.mask & mask == 0 {
801                return true;
802            }
803            // The send will fail if the receiver is gone, which means nothing was actualling
804            // waiting on the futex.
805            if waiter.notifiable.notify() {
806                woken += 1;
807            }
808            false
809        });
810        woken
811    }
812
813    fn transfer(&mut self, mut other: Self) {
814        self.0.append(&mut other.0);
815    }
816
817    fn split_for_requeue(&mut self, count: usize) -> Self {
818        let count = std::cmp::min(count, self.0.len());
819        let tail = self.0.split_off(count);
820        let head = std::mem::replace(&mut self.0, tail);
821        FutexWaiters(head)
822    }
823}
824
825/// An entry of a waiter queue, which a `WaiterMatcher` can be tested against.
826trait QueuedWaiter {
827    fn notifiable(&self) -> &FutexNotifiable;
828}
829
830impl QueuedWaiter for FutexWaiter {
831    fn notifiable(&self) -> &FutexNotifiable {
832        &self.notifiable
833    }
834}
835
836impl QueuedWaiter for RtMutexWaiter {
837    fn notifiable(&self) -> &FutexNotifiable {
838        &self.notifiable
839    }
840}
841
842impl<W: QueuedWaiter> WaiterQueue for VecDeque<W> {
843    fn remove_waiter(&mut self, matcher: &WaiterMatcher<'_>) -> bool {
844        let mut found = false;
845        self.retain(|w| {
846            if matcher.matches(w.notifiable()) {
847                found = true;
848                return false;
849            }
850            !w.notifiable().is_stale()
851        });
852        found
853    }
854
855    fn is_empty(&self) -> bool {
856        VecDeque::is_empty(self)
857    }
858}
859
860impl WaiterQueue for FutexWaiters {
861    fn remove_waiter(&mut self, matcher: &WaiterMatcher<'_>) -> bool {
862        self.0.remove_waiter(matcher)
863    }
864
865    fn is_empty(&self) -> bool {
866        self.0.is_empty()
867    }
868}
869
870struct RtMutexWaiter {
871    /// The tid, possibly with the FUTEX_WAITERS bit set.
872    tid: u32,
873
874    notifiable: FutexNotifiable,
875}
876
877#[cfg(test)]
878mod tests {
879    use super::*;
880    use starnix_sync::InterruptibleEvent;
881    use starnix_uapi::restricted_aspace::RESTRICTED_ASPACE_BASE;
882    use starnix_uapi::user_address::UserAddress;
883
884    #[fuchsia::test]
885    fn test_remove_waiter_simple() {
886        let mut state = FutexTableState::<PrivateFutexKey>::default();
887        let key = PrivateFutexKey {
888            addr: FutexAddress::try_from(UserAddress::from(
889                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
890            ))
891            .unwrap(),
892        };
893        let event = Arc::new(InterruptibleEvent::new());
894
895        state.get_waiters_or_default(key.clone()).add(FutexWaiter {
896            mask: u32::MAX,
897            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&event)),
898        });
899
900        assert_eq!(state.waiters.len(), 1);
901        assert!(state.remove_waiter_from_queue(key, &WaiterMatcher::Event(&event)));
902        assert_eq!(state.waiters.len(), 0);
903    }
904
905    #[fuchsia::test]
906    fn test_remove_waiter_requeued() {
907        let mut state = FutexTableState::<PrivateFutexKey>::default();
908        let key1 = PrivateFutexKey {
909            addr: FutexAddress::try_from(UserAddress::from(
910                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
911            ))
912            .unwrap(),
913        };
914        let key2 = PrivateFutexKey {
915            addr: FutexAddress::try_from(UserAddress::from(
916                (RESTRICTED_ASPACE_BASE + 0x2000) as u64,
917            ))
918            .unwrap(),
919        };
920        let event = Arc::new(InterruptibleEvent::new());
921
922        state.get_waiters_or_default(key2.clone()).add(FutexWaiter {
923            mask: u32::MAX,
924            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&event)),
925        });
926
927        assert_eq!(state.waiters.len(), 1);
928        assert!(state.remove_waiter_from_queue(key1, &WaiterMatcher::Event(&event)));
929        assert_eq!(state.waiters.len(), 0);
930    }
931
932    /// A wake that races with an interruption must not be lost.
933    ///
934    /// When a signal interrupts a waiter, the waiter can still be sitting in the queue when
935    /// another thread issues a wake. That wake counts the waiter as woken, so the waiter must
936    /// notice that it has been dequeued and report the wake instead of the interruption.
937    #[fuchsia::test]
938    fn test_wake_racing_with_interruption_is_not_lost() {
939        let mut state = FutexTableState::<PrivateFutexKey>::default();
940        let key = PrivateFutexKey {
941            addr: FutexAddress::try_from(UserAddress::from(
942                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
943            ))
944            .unwrap(),
945        };
946        let event = InterruptibleEvent::new();
947        let _guard = event.begin_wait();
948
949        state.get_waiters_or_default(key.clone()).add(FutexWaiter {
950            mask: u32::MAX,
951            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&event)),
952        });
953
954        // A signal interrupts the waiter before it is dequeued.
955        event.interrupt();
956
957        // The waker still sees the waiter in the queue and counts it as woken.
958        assert_eq!(state.wake(key.clone(), 1, FUTEX_BITSET_MATCH_ANY), 1);
959
960        // The interrupted waiter must therefore observe that it is no longer queued, which tells
961        // it to report the wake rather than the interruption.
962        assert!(!state.remove_waiter_from_queue(key, &WaiterMatcher::Event(&event)));
963    }
964
965    /// The boot-deadline flavor of `FUTEX_WAIT` must resolve the same race the same way.
966    #[fuchsia::test]
967    fn test_wake_racing_with_boot_waiter_removal_is_not_lost() {
968        let mut state = FutexTableState::<PrivateFutexKey>::default();
969        let key = PrivateFutexKey {
970            addr: FutexAddress::try_from(UserAddress::from(
971                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
972            ))
973            .unwrap(),
974        };
975        let waiter = Arc::new(Waiter::new());
976
977        state.get_waiters_or_default(key.clone()).add(FutexWaiter {
978            mask: u32::MAX,
979            notifiable: FutexNotifiable::new_internal_boot(Arc::downgrade(&waiter)),
980        });
981
982        // The waker dequeues the waiter and counts it as woken, whether or not the waiter was
983        // about to give up on its deadline.
984        assert_eq!(state.wake(key.clone(), 1, FUTEX_BITSET_MATCH_ANY), 1);
985
986        assert!(!state.remove_waiter_from_queue(key, &WaiterMatcher::BootWaiter(&waiter)));
987    }
988
989    /// A waiter must only be removed by a matcher of its own kind.
990    ///
991    /// The two kinds share the same queues, so a mismatched removal must neither report a match
992    /// nor dequeue the waiter, which would turn the next wake into a lost one.
993    #[fuchsia::test]
994    fn test_remove_waiter_ignores_other_waiter_kinds() {
995        let mut state = FutexTableState::<PrivateFutexKey>::default();
996        let key = PrivateFutexKey {
997            addr: FutexAddress::try_from(UserAddress::from(
998                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
999            ))
1000            .unwrap(),
1001        };
1002        let event = InterruptibleEvent::new();
1003        let waiter = Arc::new(Waiter::new());
1004
1005        state.get_waiters_or_default(key.clone()).add(FutexWaiter {
1006            mask: u32::MAX,
1007            notifiable: FutexNotifiable::new_internal_boot(Arc::downgrade(&waiter)),
1008        });
1009
1010        assert!(!state.remove_waiter_from_queue(key.clone(), &WaiterMatcher::Event(&event)));
1011        assert_eq!(state.waiters.len(), 1);
1012
1013        assert!(state.remove_waiter_from_queue(key, &WaiterMatcher::BootWaiter(&waiter)));
1014        assert_eq!(state.waiters.len(), 0);
1015    }
1016
1017    #[fuchsia::test]
1018    fn test_remove_rt_mutex_waiter() {
1019        let mut state = FutexTableState::<PrivateFutexKey>::default();
1020        let key = PrivateFutexKey {
1021            addr: FutexAddress::try_from(UserAddress::from(
1022                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
1023            ))
1024            .unwrap(),
1025        };
1026        let event = Arc::new(InterruptibleEvent::new());
1027
1028        state.get_rt_mutex_waiters_or_default(key.clone()).push_back(RtMutexWaiter {
1029            tid: 1,
1030            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&event)),
1031        });
1032
1033        assert_eq!(state.rt_mutex_waiters.len(), 1);
1034        state.remove_rt_mutex_waiter_from_queue(key, &WaiterMatcher::Event(&event));
1035        assert_eq!(state.rt_mutex_waiters.len(), 0);
1036    }
1037
1038    /// A PI-mutex waiter must learn whether it was still queued.
1039    ///
1040    /// `lock_pi` relies on that answer: a waiter that `unlock_pi` dequeued has been made the
1041    /// owner of the mutex, and must report success even if its wait failed.
1042    #[fuchsia::test]
1043    fn test_remove_rt_mutex_waiter_reports_whether_it_was_queued() {
1044        let mut state = FutexTableState::<PrivateFutexKey>::default();
1045        let key = PrivateFutexKey {
1046            addr: FutexAddress::try_from(UserAddress::from(
1047                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
1048            ))
1049            .unwrap(),
1050        };
1051        let event = InterruptibleEvent::new();
1052        let queue_waiter = |state: &mut FutexTableState<PrivateFutexKey>| {
1053            state.get_rt_mutex_waiters_or_default(key.clone()).push_back(RtMutexWaiter {
1054                tid: 1,
1055                notifiable: FutexNotifiable::new_internal(Arc::downgrade(&event)),
1056            });
1057        };
1058
1059        queue_waiter(&mut state);
1060        assert!(
1061            state.remove_rt_mutex_waiter_from_queue(key.clone(), &WaiterMatcher::Event(&event))
1062        );
1063
1064        // The handoff performed by `unlock_pi` takes the waiter out of the queue.
1065        queue_waiter(&mut state);
1066        assert!(state.pop_rt_mutex_waiter(key.clone()).is_some());
1067
1068        assert!(!state.remove_rt_mutex_waiter_from_queue(key, &WaiterMatcher::Event(&event)));
1069    }
1070
1071    /// Collecting a dead PI-mutex waiter is not a match.
1072    ///
1073    /// Reporting a match would tell the caller that its own waiter was still queued, while
1074    /// leaving the queue in place would leak the dead entry.
1075    #[fuchsia::test]
1076    fn test_remove_rt_mutex_waiter_stale_cleanup_is_not_a_match() {
1077        let mut state = FutexTableState::<PrivateFutexKey>::default();
1078        let key = PrivateFutexKey {
1079            addr: FutexAddress::try_from(UserAddress::from(
1080                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
1081            ))
1082            .unwrap(),
1083        };
1084
1085        {
1086            let dead_event = InterruptibleEvent::new();
1087            state.get_rt_mutex_waiters_or_default(key.clone()).push_back(RtMutexWaiter {
1088                tid: 1,
1089                notifiable: FutexNotifiable::new_internal(Arc::downgrade(&dead_event)),
1090            });
1091        } // dead_event is dropped here, so its waiter becomes stale.
1092
1093        let event = InterruptibleEvent::new();
1094        assert!(!state.remove_rt_mutex_waiter_from_queue(key, &WaiterMatcher::Event(&event)));
1095        assert_eq!(state.rt_mutex_waiters.len(), 0, "the stale waiter should be collected");
1096    }
1097
1098    /// Queues emptied by the fallback scan are collected too, not just the one holding the
1099    /// waiter.
1100    #[fuchsia::test]
1101    fn test_remove_waiter_collects_queues_emptied_by_the_scan() {
1102        let mut state = FutexTableState::<PrivateFutexKey>::default();
1103        let stale_key = PrivateFutexKey {
1104            addr: FutexAddress::try_from(UserAddress::from(
1105                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
1106            ))
1107            .unwrap(),
1108        };
1109        let key = PrivateFutexKey {
1110            addr: FutexAddress::try_from(UserAddress::from(
1111                (RESTRICTED_ASPACE_BASE + 0x2000) as u64,
1112            ))
1113            .unwrap(),
1114        };
1115
1116        {
1117            let dead_event = InterruptibleEvent::new();
1118            state.get_waiters_or_default(stale_key).add(FutexWaiter {
1119                mask: u32::MAX,
1120                notifiable: FutexNotifiable::new_internal(Arc::downgrade(&dead_event)),
1121            });
1122        } // dead_event is dropped here, so its waiter becomes stale.
1123
1124        // Looking for a waiter that is not there scans every queue, emptying the stale one.
1125        let event = InterruptibleEvent::new();
1126        assert!(!state.remove_waiter_from_queue(key, &WaiterMatcher::Event(&event)));
1127        assert_eq!(state.waiters.len(), 0, "the emptied queue should be collected");
1128    }
1129
1130    #[fuchsia::test]
1131    fn test_split_for_requeue_fairness() {
1132        let mut waiters = FutexWaiters::default();
1133        let e1 = Arc::new(InterruptibleEvent::new());
1134        let e2 = Arc::new(InterruptibleEvent::new());
1135        let e3 = Arc::new(InterruptibleEvent::new());
1136
1137        waiters.add(FutexWaiter {
1138            mask: 1,
1139            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&e1)),
1140        });
1141        waiters.add(FutexWaiter {
1142            mask: 2,
1143            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&e2)),
1144        });
1145        waiters.add(FutexWaiter {
1146            mask: 3,
1147            notifiable: FutexNotifiable::new_internal(Arc::downgrade(&e3)),
1148        });
1149
1150        let split = waiters.split_for_requeue(2);
1151
1152        assert_eq!(split.0.len(), 2);
1153        assert_eq!(split.0[0].mask, 1);
1154        assert_eq!(split.0[1].mask, 2);
1155
1156        assert_eq!(waiters.0.len(), 1);
1157        assert_eq!(waiters.0[0].mask, 3);
1158    }
1159
1160    #[fuchsia::test]
1161    fn test_stale_external_waiter_cleanup() {
1162        let mut state = FutexTableState::<PrivateFutexKey>::default();
1163        let key = PrivateFutexKey {
1164            addr: FutexAddress::try_from(UserAddress::from(
1165                (RESTRICTED_ASPACE_BASE + 0x1000) as u64,
1166            ))
1167            .unwrap(),
1168        };
1169
1170        {
1171            let token = Arc::new(());
1172            let (sender, _receiver) = oneshot::channel::<()>();
1173            state.get_waiters_or_default(key.clone()).add(FutexWaiter {
1174                mask: u32::MAX,
1175                notifiable: FutexNotifiable::new_external(Arc::downgrade(&token), sender),
1176            });
1177        } // token is dropped here, so it becomes stale
1178
1179        assert_eq!(state.waiters.len(), 1);
1180
1181        // Trigger a cleanup with a placeholder event
1182        let dummy_event = InterruptibleEvent::new();
1183        state.remove_waiter_from_queue(key, &WaiterMatcher::Event(&dummy_event));
1184
1185        assert_eq!(state.waiters.len(), 0, "Stale external waiter should be removed");
1186    }
1187
1188    /// `unlock_pi` must keep handing the mutex over after skipping a dead waiter.
1189    ///
1190    /// Each handoff rewrites the futex word, so a second attempt has to compare against what the
1191    /// first one wrote. Comparing against the value read on entry made the retry fail with
1192    /// EINVAL, leaving the mutex owned by a thread that no longer exists and dropping the waiter
1193    /// that should have got it.
1194    #[::fuchsia::test]
1195    async fn test_unlock_pi_hands_over_to_next_waiter_after_a_stale_one() {
1196        use crate::mm::memory::MemoryObject;
1197        use crate::mm::{DesiredAddress, MappingName, MappingOptions, PAGE_SIZE, ProtectionFlags};
1198        use crate::testing::spawn_kernel_and_run;
1199
1200        spawn_kernel_and_run(async move |current_task| {
1201            let mm = current_task.mm().unwrap();
1202            let addr = mm
1203                .map_memory(
1204                    DesiredAddress::Any,
1205                    Arc::new(MemoryObject::from(zx::Vmo::create(*PAGE_SIZE).unwrap())),
1206                    0,
1207                    *PAGE_SIZE as usize,
1208                    ProtectionFlags::READ | ProtectionFlags::WRITE,
1209                    MappingOptions::empty(),
1210                    MappingName::None,
1211                )
1212                .expect("map failed");
1213            let futex_addr = FutexAddress::try_from(addr).unwrap();
1214
1215            // The current task owns the mutex and has waiters queued behind it.
1216            let owner_tid = current_task.get_tid() as u32;
1217            assert!(matches!(
1218                mm.atomic_compare_exchange_u32_acq_rel(futex_addr, 0, owner_tid | FUTEX_WAITERS),
1219                CompareExchangeResult::Success
1220            ));
1221
1222            const STALE_TID: u32 = 0x111;
1223            const NEXT_OWNER_TID: u32 = 0x222;
1224            let futex_table = FutexTable::<PrivateFutexKey>::default();
1225            let key = PrivateFutexKey::get(current_task, futex_addr).unwrap();
1226            let next_owner_event = InterruptibleEvent::new();
1227            {
1228                let mut state = futex_table.state.lock();
1229                let queue = state.get_rt_mutex_waiters_or_default(key);
1230                {
1231                    let dead_event = InterruptibleEvent::new();
1232                    queue.push_back(RtMutexWaiter {
1233                        tid: STALE_TID,
1234                        notifiable: FutexNotifiable::new_internal(Arc::downgrade(&dead_event)),
1235                    });
1236                } // dead_event is dropped here, so its waiter can never be notified.
1237                queue.push_back(RtMutexWaiter {
1238                    tid: NEXT_OWNER_TID,
1239                    notifiable: FutexNotifiable::new_internal(Arc::downgrade(&next_owner_event)),
1240                });
1241            }
1242
1243            futex_table.unlock_pi(current_task, addr).expect("unlock_pi failed");
1244
1245            assert_eq!(
1246                mm.atomic_load_u32_relaxed(futex_addr).unwrap(),
1247                NEXT_OWNER_TID,
1248                "the mutex should have been handed to the waiter behind the stale one"
1249            );
1250            assert!(futex_table.state.lock().rt_mutex_waiters.is_empty());
1251        })
1252        .await;
1253    }
1254
1255    #[::fuchsia::test]
1256    async fn test_futex_deadlock_with_pager() {
1257        use crate::mm::memory::MemoryObject;
1258        use crate::mm::{DesiredAddress, MappingName, MappingOptions, PAGE_SIZE, ProtectionFlags};
1259        use crate::testing::spawn_kernel_and_run;
1260        use std::sync::atomic::{AtomicBool, Ordering};
1261        use zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ;
1262
1263        spawn_kernel_and_run(async move |current_task| {
1264            let mm = current_task.mm().unwrap();
1265
1266            let port = Arc::new(zx::Port::create());
1267            let port_clone = port.clone();
1268            let pager =
1269                Arc::new(zx::Pager::create(zx::PagerOptions::empty()).expect("create failed"));
1270            let pager_clone = pager.clone();
1271
1272            let vmo = Arc::new(
1273                pager
1274                    .create_vmo(zx::VmoOptions::RESIZABLE, &port, 1, *PAGE_SIZE)
1275                    .expect("create_vmo failed"),
1276            );
1277            let vmo_clone = vmo.clone();
1278
1279            let mapped_addr = mm
1280                .map_memory(
1281                    DesiredAddress::Any,
1282                    Arc::new(MemoryObject::from(
1283                        (*vmo).duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1284                    )),
1285                    0,
1286                    *PAGE_SIZE as usize,
1287                    ProtectionFlags::READ | ProtectionFlags::WRITE,
1288                    MappingOptions::empty(),
1289                    MappingName::None,
1290                )
1291                .expect("map failed");
1292
1293            let futex_table = Arc::new(FutexTable::<PrivateFutexKey>::default());
1294            let futex_table_clone = futex_table.clone();
1295            let task_clone = current_task.task.clone();
1296            let dummy_addr = UserAddress::from((RESTRICTED_ASPACE_BASE + 0x2000) as u64);
1297
1298            let page_requested = Arc::new(AtomicBool::new(false));
1299            let wake_completed = Arc::new(AtomicBool::new(false));
1300
1301            let page_req_clone = page_requested.clone();
1302            let wake_completed_clone = wake_completed.clone();
1303
1304            let pager_thread = std::thread::spawn(move || {
1305                let packet = port_clone.wait(zx::MonotonicInstant::INFINITE).expect("wait failed");
1306                if let zx::PacketContents::Pager(contents) = packet.contents() {
1307                    if contents.command() == ZX_PAGER_VMO_READ {
1308                        let range = contents.range();
1309                        page_req_clone.store(true, Ordering::SeqCst);
1310
1311                        // Spawn waker thread while main thread is page-faulting before acquiring the lock
1312                        let waker = std::thread::spawn(move || {
1313                            let _ = futex_table_clone.wake(&task_clone, dummy_addr, 1, u32::MAX);
1314                            wake_completed_clone.store(true, Ordering::SeqCst);
1315                        });
1316
1317                        // Verify waker completes immediately without being blocked by the page fault
1318                        waker.join().unwrap();
1319                        assert!(
1320                            wake_completed.load(Ordering::SeqCst),
1321                            "Waker thread must NOT be blocked while page fault is in progress!"
1322                        );
1323
1324                        // Supply pages to unblock the main thread's page fault
1325                        let source_vmo =
1326                            zx::Vmo::create(range.end - range.start).expect("create failed");
1327                        pager_clone
1328                            .supply_pages(&vmo_clone, range, &source_vmo, 0)
1329                            .expect("supply_pages failed");
1330                    }
1331                }
1332            });
1333
1334            // This will lock the FutexTable, then page fault on mapped_addr when reading value
1335            let _ = futex_table.wait(
1336                current_task,
1337                mapped_addr,
1338                0,
1339                u32::MAX,
1340                zx::MonotonicInstant::from_nanos(1),
1341            );
1342
1343            pager_thread.join().unwrap();
1344        })
1345        .await;
1346    }
1347}