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}