shared-mem-queue 0.4.0

Single-writer single-reader queues which can be used for inter-processor-communication in a shared memory region
Documentation
//! 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<Self, Box<dyn std::error::Error>> {
        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");
            }
        }
    }
}