use std::ffi::CString;
pub struct ShmRegion {
name: String,
ptr: *mut u8,
size: usize,
fd: i32,
}
unsafe impl Send for ShmRegion {}
impl ShmRegion {
pub fn create(size: usize) -> Result<Self, String> {
static COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let id = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let name = format!("/cp2k_{}_{}", std::process::id(), id);
Self::create_named(&name, size)
}
fn create_named(name: &str, size: usize) -> Result<Self, String> {
let c_name = CString::new(name).map_err(|e| e.to_string())?;
unsafe {
let fd = libc::shm_open(
c_name.as_ptr(),
libc::O_CREAT | libc::O_RDWR | libc::O_EXCL,
0o600,
);
if fd < 0 {
return Err(format!(
"shm_open({name}) failed: {}",
std::io::Error::last_os_error()
));
}
if libc::ftruncate(fd, size as libc::off_t) != 0 {
let err = std::io::Error::last_os_error();
libc::close(fd);
libc::shm_unlink(c_name.as_ptr());
return Err(format!("ftruncate failed: {err}"));
}
let ptr = libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
);
if ptr == libc::MAP_FAILED {
let err = std::io::Error::last_os_error();
libc::close(fd);
libc::shm_unlink(c_name.as_ptr());
return Err(format!("mmap failed: {err}"));
}
Ok(ShmRegion {
name: name.to_string(),
ptr: ptr as *mut u8,
size,
fd,
})
}
}
pub fn open_readonly(name: &str, size: usize) -> Result<Self, String> {
let c_name = CString::new(name).map_err(|e| e.to_string())?;
unsafe {
let fd = libc::shm_open(c_name.as_ptr(), libc::O_RDONLY, 0);
if fd < 0 {
return Err(format!(
"shm_open({name}) failed: {}",
std::io::Error::last_os_error()
));
}
let ptr = libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ,
libc::MAP_SHARED,
fd,
0,
);
if ptr == libc::MAP_FAILED {
let err = std::io::Error::last_os_error();
libc::close(fd);
return Err(format!("mmap failed: {err}"));
}
Ok(ShmRegion {
name: name.to_string(),
ptr: ptr as *mut u8,
size,
fd,
})
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn size(&self) -> usize {
self.size
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
}
pub fn as_f64_slice(&self) -> &[f64] {
let n = self.size / std::mem::size_of::<f64>();
unsafe { std::slice::from_raw_parts(self.ptr as *const f64, n) }
}
pub fn unlink(&self) {
if let Ok(c_name) = CString::new(self.name.as_str()) {
unsafe {
libc::shm_unlink(c_name.as_ptr());
}
}
}
}
impl Drop for ShmRegion {
fn drop(&mut self) {
unsafe {
libc::munmap(self.ptr as *mut libc::c_void, self.size);
libc::close(self.fd);
}
}
}