Skip to main content

moonpool_explorer/
shared_mem.rs

1//! POSIX shared memory allocation for cross-process data sharing.
2//!
3//! Provides `mmap(MAP_SHARED | MAP_ANONYMOUS)` wrappers for allocating memory
4//! visible across `fork()` boundaries. This is the foundation for all
5//! cross-process state in the explorer.
6
7use std::io;
8
9/// Allocate shared memory visible across `fork()` boundaries.
10///
11/// Returns a pointer to `size` bytes of zeroed memory backed by
12/// `MAP_SHARED | MAP_ANONYMOUS`. The memory is readable and writable
13/// by both parent and child processes after `fork()`.
14///
15/// # Errors
16///
17/// Returns an error if `mmap` fails (e.g., insufficient memory).
18#[cfg(unix)]
19pub fn alloc_shared(size: usize) -> Result<*mut u8, io::Error> {
20    // Safety: FFI call to libc::mmap.
21    // - MAP_ANONYMOUS: no file descriptor required (fd = -1).
22    // - MAP_SHARED: memory visible across fork() boundaries.
23    // - Kernel guarantees: returned memory is zeroed and page-aligned.
24    // - We check for MAP_FAILED before returning the pointer.
25    // - The caller owns the returned pointer and must free it via free_shared().
26    let ptr = unsafe {
27        libc::mmap(
28            std::ptr::null_mut(),
29            size,
30            libc::PROT_READ | libc::PROT_WRITE,
31            libc::MAP_SHARED | libc::MAP_ANONYMOUS,
32            -1,
33            0,
34        )
35    };
36    if ptr == libc::MAP_FAILED {
37        return Err(io::Error::last_os_error());
38    }
39    Ok(ptr as *mut u8)
40}
41
42/// Free shared memory allocated by [`alloc_shared`].
43///
44/// # Safety
45///
46/// - `ptr` must have been returned by [`alloc_shared`] with the same `size`.
47/// - `ptr` must not have been previously freed (no double-free).
48/// - The pointer and any derived references become invalid after this call.
49/// - `size` must exactly match the value passed to `alloc_shared`.
50#[cfg(unix)]
51pub unsafe fn free_shared(ptr: *mut u8, size: usize) {
52    unsafe {
53        libc::munmap(ptr as *mut libc::c_void, size);
54    }
55}
56
57/// No-op on non-unix platforms.
58#[cfg(not(unix))]
59pub fn alloc_shared(size: usize) -> Result<*mut u8, io::Error> {
60    Err(io::Error::new(
61        io::ErrorKind::Unsupported,
62        "shared memory requires unix",
63    ))
64}
65
66/// No-op on non-unix platforms.
67#[cfg(not(unix))]
68pub unsafe fn free_shared(_ptr: *mut u8, _size: usize) {}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn test_alloc_write_read_free() {
76        let size = 4096;
77        let ptr = alloc_shared(size).expect("alloc_shared failed");
78        assert!(!ptr.is_null());
79
80        // Write and read back
81        unsafe {
82            *ptr = 42;
83            *ptr.add(size - 1) = 99;
84            assert_eq!(*ptr, 42);
85            assert_eq!(*ptr.add(size - 1), 99);
86            free_shared(ptr, size);
87        }
88    }
89
90    #[test]
91    fn test_zeroed_on_alloc() {
92        let size = 1024;
93        let ptr = alloc_shared(size).expect("alloc_shared failed");
94
95        // Kernel guarantees zeroed memory from mmap
96        unsafe {
97            for i in 0..size {
98                assert_eq!(*ptr.add(i), 0);
99            }
100            free_shared(ptr, size);
101        }
102    }
103}