Skip to main content

maolan_plugin_protocol/
shm.rs

1use std::ffi::CString;
2
3/// Owned mapping of a POSIX shared-memory segment.
4pub struct ShmMapping {
5    ptr: *mut u8,
6    size: usize,
7    #[allow(dead_code)]
8    name: String,
9}
10
11// Safety: ShmMapping is Send+Sync because the mapped memory is process-shared.
12unsafe impl Send for ShmMapping {}
13unsafe impl Sync for ShmMapping {}
14
15impl ShmMapping {
16    /// Create a new shared-memory segment, truncate to `size`, and map it.
17    #[cfg(unix)]
18    pub fn create(name: &str, size: usize) -> Result<Self, String> {
19        let c_name = CString::new(name).map_err(|e| e.to_string())?;
20        let fd = unsafe { libc::shm_open(c_name.as_ptr(), libc::O_CREAT | libc::O_RDWR, 0o644) };
21        if fd < 0 {
22            return Err(format!(
23                "shm_open({}, O_CREAT|O_RDWR) failed: {:?}",
24                name,
25                std::io::Error::last_os_error()
26            ));
27        }
28        if unsafe { libc::ftruncate(fd, size as libc::off_t) } < 0 {
29            unsafe { libc::close(fd) };
30            return Err(format!(
31                "ftruncate failed: {:?}",
32                std::io::Error::last_os_error()
33            ));
34        }
35        let ptr = unsafe {
36            libc::mmap(
37                std::ptr::null_mut(),
38                size,
39                libc::PROT_READ | libc::PROT_WRITE,
40                libc::MAP_SHARED,
41                fd,
42                0,
43            )
44        };
45        unsafe { libc::close(fd) };
46        if ptr == libc::MAP_FAILED {
47            unsafe { libc::shm_unlink(c_name.as_ptr()) };
48            return Err(format!(
49                "mmap failed: {:?}",
50                std::io::Error::last_os_error()
51            ));
52        }
53        Ok(Self {
54            ptr: ptr as *mut u8,
55            size,
56            name: name.to_string(),
57        })
58    }
59
60    /// Open an existing shared-memory segment and map it.
61    #[cfg(unix)]
62    pub fn open_existing(name: &str, size: usize) -> Result<Self, String> {
63        let c_name = CString::new(name).map_err(|e| e.to_string())?;
64        let fd = unsafe { libc::shm_open(c_name.as_ptr(), libc::O_RDWR, 0) };
65        if fd < 0 {
66            return Err(format!(
67                "shm_open({}, O_RDWR) failed: {:?}",
68                name,
69                std::io::Error::last_os_error()
70            ));
71        }
72        let ptr = unsafe {
73            libc::mmap(
74                std::ptr::null_mut(),
75                size,
76                libc::PROT_READ | libc::PROT_WRITE,
77                libc::MAP_SHARED,
78                fd,
79                0,
80            )
81        };
82        unsafe { libc::close(fd) };
83        if ptr == libc::MAP_FAILED {
84            return Err(format!(
85                "mmap failed: {:?}",
86                std::io::Error::last_os_error()
87            ));
88        }
89        Ok(Self {
90            ptr: ptr as *mut u8,
91            size,
92            name: name.to_string(),
93        })
94    }
95
96    /// Windows stubs — not yet implemented.
97    #[cfg(windows)]
98    pub fn create(name: &str, size: usize) -> Result<Self, String> {
99        let _ = (name, size);
100        Err("Shared memory creation not yet implemented on Windows".to_string())
101    }
102
103    #[cfg(windows)]
104    pub fn open_existing(name: &str, size: usize) -> Result<Self, String> {
105        let _ = (name, size);
106        Err("Shared memory open not yet implemented on Windows".to_string())
107    }
108
109    /// Raw pointer to the start of the mapping.
110    pub fn as_ptr(&self) -> *mut u8 {
111        self.ptr
112    }
113
114    /// Size of the mapping in bytes.
115    pub fn size(&self) -> usize {
116        self.size
117    }
118
119    /// Name used to create/open the segment.
120    pub fn name(&self) -> &str {
121        &self.name
122    }
123
124    /// Unlink the underlying POSIX shared-memory object.
125    #[cfg(unix)]
126    pub fn unlink(name: &str) -> Result<(), String> {
127        let c_name = CString::new(name).map_err(|e| e.to_string())?;
128        let res = unsafe { libc::shm_unlink(c_name.as_ptr()) };
129        if res < 0 {
130            Err(format!(
131                "shm_unlink failed: {:?}",
132                std::io::Error::last_os_error()
133            ))
134        } else {
135            Ok(())
136        }
137    }
138
139    #[cfg(windows)]
140    pub fn unlink(_name: &str) -> Result<(), String> {
141        Ok(())
142    }
143}
144
145#[cfg(unix)]
146impl Drop for ShmMapping {
147    fn drop(&mut self) {
148        if !self.ptr.is_null() && self.ptr != libc::MAP_FAILED as *mut u8 {
149            unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.size) };
150        }
151    }
152}