//! Helper module to open and close shared memory handles use libc::{ c_int, close, ftruncate, mmap, mode_t, munmap, shm_open, shm_unlink, MAP_SHARED, PROT_READ, PROT_WRITE, }; use std::ffi::CString; use std::os::fd::RawFd; use std::os::raw::c_void; use std::ptr; use std::result::Result; pub struct ShmHandle { shm_fd: RawFd, size: usize, mapped_memory: *mut c_void, shm_name: CString, } impl ShmHandle { pub fn new( shm_name: &str, size: usize, flag: c_int, mode: mode_t, ) -> Result> { let shm_name_c = CString::new(shm_name)?; let is_writer = flag & libc::O_CREAT != 0; // TODO Until the Drop impl works, we need to unlink the old shared mem in order to // recreate it successfully if is_writer { unsafe { shm_unlink(shm_name_c.as_ptr()) }; } // Get fd via shm_open // mode_t defined differently on macOS and Linux -> u32 works for both in shm_open let shm_fd = unsafe { shm_open(shm_name_c.as_ptr(), flag, u32::from(mode)) }; if shm_fd < 0 { eprintln!("shm_open failed"); return Err(Box::new(std::io::Error::last_os_error())); } // On macOS, ftruncate only works after the initial creation of the shared memory. // Therefore, we only ftruncate if we are the writer which is indicated here by the O_CREAT // flag. // https://stackoverflow.com/questions/25502229/ftruncate-not-working-on-posix-shared-memory-in-mac-os-x if is_writer { // Set the size of the shm fd if unsafe { ftruncate(shm_fd, size as i64) } == -1 { eprintln!("ftruncate failed"); unsafe { close(shm_fd) }; return Err(Box::new(std::io::Error::last_os_error())); } } // Map fd to shared mem let mapped_memory = unsafe { mmap( ptr::null_mut(), size, PROT_WRITE | PROT_READ, MAP_SHARED, shm_fd, 0, ) }; if mapped_memory == libc::MAP_FAILED { eprintln!("mmap failed"); unsafe { close(shm_fd) }; return Err(Box::new(std::io::Error::last_os_error())); } Ok(Self { shm_fd, size, mapped_memory, shm_name: shm_name_c, }) } pub fn as_mut_ptr_u8(&mut self) -> *mut u8 { self.mapped_memory as *mut u8 } } impl Drop for ShmHandle { fn drop(&mut self) { eprintln!("dropping shm handle"); unsafe { if munmap(self.mapped_memory, self.size) == -1 { eprintln!("munmap failed"); } if close(self.shm_fd) == -1 { eprintln!("close failed"); } if shm_unlink(self.shm_name.as_ptr()) == -1 { eprintln!("shm_unlink failed"); } } } }