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].
51#[derive(Debug)]
52pub struct FutexRef(SyncSendPointer<c::Futex>);
53
54impl FutexRef {
55    /// Takes ownership of the reference.
56    ///
57    /// # Safety
58    ///
59    /// The pointer must be a valid [`Futex`][c::Futex].
60    pub unsafe fn new(ptr: *mut c::Futex) -> Self {
61        debug_assert!(!ptr.is_null());
62        Self(unsafe { SyncSendPointer::new(ptr) })
63    }
64
65    pub fn ptr(&self) -> *mut c::Futex {
66        self.0.ptr()
67    }
68
69    pub fn physical_addr(&self) -> ManagedPhysicalMemoryAddr {
70        unsafe { c::futex_getAddress(self.ptr()) }
71    }
72
73    pub fn wake(&self, num_wakeups: libc::c_uint) -> libc::c_uint {
74        unsafe { c::futex_wake(self.ptr(), num_wakeups) }
75    }
76
77    pub fn listener_count(&self) -> libc::c_uint {
78        unsafe { c::futex_getListenerCount(self.ptr()) }
79    }
80
81    /// Ownership of the reference is transferred to the returned pointer.
82    ///
83    /// In otherwords, `self` is dropped without changing the futex's refcount, and the returned
84    /// pointer can be safely used. The refcount should be decremented when the returned pointer is
85    /// no longer used.
86    pub fn into_c_ptr(self) -> *mut c::Futex {
87        let ptr = self.ptr();
88        unsafe { c::futex_ref(self.0.ptr()) };
89        ptr
90    }
91}
92
93impl Clone for FutexRef {
94    fn clone(&self) -> Self {
95        unsafe { c::futex_ref(self.0.ptr()) };
96        Self(self.0)
97    }
98}
99
100impl std::ops::Drop for FutexRef {
101    fn drop(&mut self) {
102        unsafe { c::futex_unref(self.0.ptr()) };
103    }
104}
105
106mod export {
107    use super::*;
108
109    /// This does not consume the `futex` reference.
110    #[unsafe(no_mangle)]
111    pub unsafe extern "C-unwind" fn futextable_add(
112        table: *mut FutexTable,
113        futex: *mut c::Futex,
114    ) -> bool {
115        let table = unsafe { table.as_mut() }.unwrap();
116
117        assert!(!futex.is_null());
118        unsafe { c::futex_ref(futex) };
119        let futex = unsafe { FutexRef::new(futex) };
120
121        table.add(futex).is_ok()
122    }
123
124    #[unsafe(no_mangle)]
125    pub unsafe extern "C-unwind" fn futextable_remove(
126        table: *mut FutexTable,
127        addr: ManagedPhysicalMemoryAddr,
128    ) -> bool {
129        let table = unsafe { table.as_mut() }.unwrap();
130        table.remove(addr).is_some()
131    }
132
133    /// This returns a borrowed reference. If you don't increment the refcount of the returned
134    /// futex, then the returned pointer will be invalidated if the futex table is mutated.
135    #[unsafe(no_mangle)]
136    pub unsafe extern "C-unwind" fn futextable_get(
137        table: *mut FutexTable,
138        addr: ManagedPhysicalMemoryAddr,
139    ) -> *mut c::Futex {
140        let table = unsafe { table.as_mut() }.unwrap();
141        table
142            .get(addr)
143            .map(|x| x.ptr())
144            .unwrap_or(std::ptr::null_mut())
145    }
146}