shadow_rs/host/
futex_table.rs

1use std::collections::HashMap;
2use std::collections::hash_map::Entry;
3
4use shadow_shim_helper_rs::syscall_types::ManagedPhysicalMemoryAddr;
5use shadow_shim_helper_rs::util::SyncSendPointer;
6
7use crate::cshadow as c;
8use crate::utility::ObjectCounter;
9
10/// A map of [`ManagedPhysicalMemoryAddr`] to [`Futex`](c::Futex).
11pub struct FutexTable {
12    /// All futexes that we are tracking. Each futex has a unique physical address associated with
13    /// it when it is stored in our table, which we refer to as a table index or table indices.
14    futexes: HashMap<ManagedPhysicalMemoryAddr, FutexRef>,
15    _counter: ObjectCounter,
16}
17
18impl FutexTable {
19    #[allow(clippy::new_without_default)]
20    pub fn new() -> Self {
21        Self {
22            futexes: HashMap::new(),
23            _counter: ObjectCounter::new("FutexTable"),
24        }
25    }
26
27    /// Add the futex to the table. If the futex already exists in the table, `futex` will be
28    /// returned in the `Err` value.
29    pub fn add(&mut self, futex: FutexRef) -> Result<(), FutexRef> {
30        let addr = futex.physical_addr();
31
32        match self.futexes.entry(addr) {
33            Entry::Occupied(_) => Err(futex),
34            Entry::Vacant(x) => {
35                x.insert(futex);
36                Ok(())
37            }
38        }
39    }
40
41    pub fn remove(&mut self, addr: ManagedPhysicalMemoryAddr) -> Option<FutexRef> {
42        self.futexes.remove(&addr)
43    }
44
45    pub fn get(&self, addr: ManagedPhysicalMemoryAddr) -> Option<&FutexRef> {
46        self.futexes.get(&addr)
47    }
48}
49
50/// An owned reference to a [`Futex`][c::Futex].
51pub struct FutexRef(SyncSendPointer<c::Futex>);
52
53impl FutexRef {
54    /// Takes ownership of the reference.
55    ///
56    /// # Safety
57    ///
58    /// The pointer must be a valid [`Futex`][c::Futex].
59    pub unsafe fn new(ptr: *mut c::Futex) -> Self {
60        debug_assert!(!ptr.is_null());
61        Self(unsafe { SyncSendPointer::new(ptr) })
62    }
63
64    pub fn ptr(&self) -> *mut c::Futex {
65        self.0.ptr()
66    }
67
68    pub fn physical_addr(&self) -> ManagedPhysicalMemoryAddr {
69        unsafe { c::futex_getAddress(self.ptr()) }
70    }
71
72    pub fn wake(&self, num_wakeups: libc::c_uint) -> libc::c_uint {
73        unsafe { c::futex_wake(self.ptr(), num_wakeups) }
74    }
75}
76
77impl std::ops::Drop for FutexRef {
78    fn drop(&mut self) {
79        unsafe { c::futex_unref(self.0.ptr()) };
80    }
81}
82
83mod export {
84    use super::*;
85
86    /// This does not consume the `futex` reference.
87    #[unsafe(no_mangle)]
88    pub unsafe extern "C-unwind" fn futextable_add(
89        table: *mut FutexTable,
90        futex: *mut c::Futex,
91    ) -> bool {
92        let table = unsafe { table.as_mut() }.unwrap();
93
94        assert!(!futex.is_null());
95        unsafe { c::futex_ref(futex) };
96        let futex = unsafe { FutexRef::new(futex) };
97
98        table.add(futex).is_ok()
99    }
100
101    #[unsafe(no_mangle)]
102    pub unsafe extern "C-unwind" fn futextable_remove(
103        table: *mut FutexTable,
104        addr: ManagedPhysicalMemoryAddr,
105    ) -> bool {
106        let table = unsafe { table.as_mut() }.unwrap();
107        table.remove(addr).is_some()
108    }
109
110    /// This returns a borrowed reference. If you don't increment the refcount of the returned
111    /// futex, then the returned pointer will be invalidated if the futex table is mutated.
112    #[unsafe(no_mangle)]
113    pub unsafe extern "C-unwind" fn futextable_get(
114        table: *mut FutexTable,
115        addr: ManagedPhysicalMemoryAddr,
116    ) -> *mut c::Futex {
117        let table = unsafe { table.as_mut() }.unwrap();
118        table
119            .get(addr)
120            .map(|x| x.ptr())
121            .unwrap_or(std::ptr::null_mut())
122    }
123}