use std::ffi::CString;
use std::ptr::NonNull;
use crate::err::{Error, Result};
pub struct Region {
ptr: NonNull<u8>,
len: usize,
fd: i32,
name: CString,
owner: bool,
}
unsafe impl Send for Region {}
unsafe impl Sync for Region {}
impl Region {
pub fn create(name: &str, size: usize) -> Result<Self> {
Self::validate_params(name, size)?;
let cname = CString::new(name).map_err(|_| Error::ShmCreate)?;
unsafe {
libc::shm_unlink(cname.as_ptr());
let fd = libc::shm_open(
cname.as_ptr(),
libc::O_CREAT | libc::O_RDWR | libc::O_EXCL,
0o600,
);
if fd < 0 {
return Err(Error::ShmCreate);
}
Self::complete_init(fd, cname, size, true)
}
}
pub fn open(name: &str, size: usize) -> Result<Self> {
Self::validate_params(name, size)?;
let cname = CString::new(name).map_err(|_| Error::ShmOpen)?;
unsafe {
let fd = libc::shm_open(cname.as_ptr(), libc::O_RDWR, 0);
if fd < 0 {
return Err(Error::ShmOpen);
}
Self::complete_init(fd, cname, size, false)
}
}
fn validate_params(name: &str, size: usize) -> Result<()> {
if name.is_empty() || name.len() > 255 {
return Err(Error::ShmCreate);
}
if size < 4096 || !size.is_power_of_two() {
return Err(Error::InvalidSize);
}
Ok(())
}
unsafe fn complete_init(fd: i32, name: CString, size: usize, owner: bool) -> Result<Self> {
if owner && libc::ftruncate(fd, size.try_into().unwrap_or(i64::MAX)) < 0 {
libc::close(fd);
libc::shm_unlink(name.as_ptr());
return Err(Error::ShmResize);
}
let ptr = libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED | libc::MAP_POPULATE,
fd,
0,
);
if ptr == libc::MAP_FAILED {
libc::close(fd);
if owner {
libc::shm_unlink(name.as_ptr());
}
return Err(Error::Mmap);
}
libc::mlock(ptr, size);
Ok(Self {
ptr: NonNull::new_unchecked(ptr.cast()),
len: size,
fd,
name,
owner,
})
}
#[inline]
pub fn as_ptr(&self) -> *mut u8 {
self.ptr.as_ptr()
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn is_owner(&self) -> bool {
self.owner
}
#[inline]
pub unsafe fn cast<T>(&self) -> &T {
&*self.ptr.as_ptr().cast()
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn cast_mut<T>(&self) -> &mut T {
&mut *self.ptr.as_ptr().cast()
}
}
impl Drop for Region {
fn drop(&mut self) {
unsafe {
libc::munlock(self.ptr.as_ptr().cast(), self.len);
libc::munmap(self.ptr.as_ptr().cast(), self.len);
libc::close(self.fd);
if self.owner {
libc::shm_unlink(self.name.as_ptr());
}
}
}
}