use super::{mem, AllocError};
use libc::{
c_int, c_void, mmap, munmap, shmat, shmctl, shmdt, shmget, shmid_ds,
sysconf, IPC_CREAT, IPC_PRIVATE, IPC_RMID, MAP_FAILED, MAP_PRIVATE,
PROT_NONE, _SC_PAGESIZE,
};
#[cfg(not(target_os = "macos"))]
use libc::MAP_ANONYMOUS;
#[cfg(target_os = "macos")]
use libc::MAP_ANON as MAP_ANONYMOUS;
pub fn allocation_granularity() -> usize {
unsafe { sysconf(_SC_PAGESIZE) as usize }
}
struct SharedMemory {
id: c_int,
}
struct MemoryMap(*mut c_void);
impl SharedMemory {
pub fn allocate(size: usize) -> Result<SharedMemory, AllocError> {
assert!(size != 0);
assert!(size % allocation_granularity() == 0);
unsafe {
let id = shmget(IPC_PRIVATE, size, IPC_CREAT | 448);
if id == -1 {
print_error("shmget");
return Err(AllocError::Oom);
}
Ok(SharedMemory { id })
}
}
pub fn attach(&self, ptr: *mut c_void) -> Result<MemoryMap, AllocError> {
unsafe {
assert!(!ptr.is_null());
let r = shmat(self.id, ptr, 0);
if r as isize == -1 {
print_error("shmat");
return Err(AllocError::Other);
}
let map = MemoryMap(ptr);
if r != ptr {
print_error("shmat2");
return Err(AllocError::Other);
}
Ok(map)
}
}
}
impl Drop for SharedMemory {
fn drop(&mut self) {
unsafe {
let r = shmctl(self.id, IPC_RMID, 0 as *mut shmid_ds);
if r == -1 {
print_error("shmctl");
panic!("freeing system V shared-memory failed");
}
}
}
}
impl MemoryMap {
unsafe fn from_raw(ptr: *mut c_void) -> MemoryMap {
assert!(!ptr.is_null());
MemoryMap(ptr)
}
}
impl Drop for MemoryMap {
fn drop(&mut self) {
unsafe {
let r = shmdt(self.0);
if r == -1 {
print_error("shmdt");
panic!("freeing system V memory map failed");
}
}
}
}
pub fn allocate_mirrored(size: usize) -> Result<*mut u8, AllocError> {
unsafe {
assert!(size != 0);
let half_size = size / 2;
assert!(half_size % allocation_granularity() == 0);
let shm = SharedMemory::allocate(half_size)?;
const MAX_NO_ITERS: i32 = 10;
let mut counter = 0;
let ptr = loop {
counter += 1;
if counter > MAX_NO_ITERS {
return Err(AllocError::Other);
}
let ptr = mmap(
0 as *mut c_void,
size,
PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1,
0,
);
if ptr == MAP_FAILED {
print_error("mmap initial");
return Err(AllocError::Oom);
}
let ptr2 =
(ptr as *mut u8).offset(half_size as isize) as *mut c_void;
unmap(ptr, size).expect("unmap initial failed");
let map0 = shm.attach(ptr);
if map0.is_err() {
print_error("attach_shm first failed");
continue;
}
let map1 = shm.attach(ptr2);
if map1.is_err() {
print_error("attach_shm second failed");
continue;
}
mem::forget(map0);
mem::forget(map1);
break ptr;
};
Ok(ptr as *mut u8)
}
}
pub unsafe fn deallocate_mirrored(ptr: *mut u8, size: usize) {
let ptr2 = ptr.offset(size as isize / 2);
MemoryMap::from_raw(ptr as *mut c_void);
MemoryMap::from_raw(ptr2 as *mut c_void);
}
unsafe fn unmap(ptr: *mut c_void, size: usize) -> Result<(), ()> {
let r = munmap(ptr, size);
if r == -1 {
print_error("unmap");
return Err(());
}
Ok(())
}
#[cfg(not(all(debug_assertions, feature = "use_std")))]
fn print_error(_location: &str) {}
#[cfg(all(debug_assertions, feature = "use_std"))]
fn print_error(location: &str) {
eprintln!(
"Error at {}: {}",
location,
::std::io::Error::last_os_error()
);
}