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: MAP_ANONYMOUS does not require a file descriptor.
21    // MAP_SHARED ensures visibility across fork().
22    // The returned memory is zeroed by the kernel.
23    let ptr = unsafe {
24        libc::mmap(
25            std::ptr::null_mut(),
26            size,
27            libc::PROT_READ | libc::PROT_WRITE,
28            libc::MAP_SHARED | libc::MAP_ANONYMOUS,
29            -1,
30            0,
31        )
32    };
33    if ptr == libc::MAP_FAILED {
34        return Err(io::Error::last_os_error());
35    }
36    Ok(ptr as *mut u8)
37}
38
39/// Free shared memory allocated by [`alloc_shared`].
40///
41/// # Safety
42///
43/// `ptr` must have been returned by [`alloc_shared`] with the same `size`.
44/// The pointer must not be used after this call.
45#[cfg(unix)]
46pub unsafe fn free_shared(ptr: *mut u8, size: usize) {
47    unsafe {
48        libc::munmap(ptr as *mut libc::c_void, size);
49    }
50}
51
52/// No-op on non-unix platforms.
53#[cfg(not(unix))]
54pub fn alloc_shared(size: usize) -> Result<*mut u8, io::Error> {
55    Err(io::Error::new(
56        io::ErrorKind::Unsupported,
57        "shared memory requires unix",
58    ))
59}
60
61/// No-op on non-unix platforms.
62#[cfg(not(unix))]
63pub unsafe fn free_shared(_ptr: *mut u8, _size: usize) {}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_alloc_write_read_free() {
71        let size = 4096;
72        let ptr = alloc_shared(size).expect("alloc_shared failed");
73        assert!(!ptr.is_null());
74
75        // Write and read back
76        unsafe {
77            *ptr = 42;
78            *ptr.add(size - 1) = 99;
79            assert_eq!(*ptr, 42);
80            assert_eq!(*ptr.add(size - 1), 99);
81            free_shared(ptr, size);
82        }
83    }
84
85    #[test]
86    fn test_zeroed_on_alloc() {
87        let size = 1024;
88        let ptr = alloc_shared(size).expect("alloc_shared failed");
89
90        // Kernel guarantees zeroed memory from mmap
91        unsafe {
92            for i in 0..size {
93                assert_eq!(*ptr.add(i), 0);
94            }
95            free_shared(ptr, size);
96        }
97    }
98}