extern crate libc;
extern crate nix;
use self::libc::{
pthread_mutex_t,
pthread_mutex_init,
pthread_mutex_lock,
pthread_mutex_unlock,
pthread_mutexattr_t,
pthread_mutexattr_init,
pthread_mutexattr_setpshared,
pthread_rwlock_t,
pthread_rwlock_init,
pthread_rwlock_unlock,
pthread_rwlock_rdlock,
pthread_rwlock_wrlock,
pthread_rwlockattr_t,
pthread_rwlockattr_init,
pthread_rwlockattr_setpshared,
PTHREAD_PROCESS_SHARED,
};
use self::nix::sys::mman::{mmap, munmap, shm_open, shm_unlink, ProtFlags, MapFlags};
use self::nix::sys::stat::{fstat, FileStat, Mode};
use self::nix::fcntl::OFlag;
use self::nix::unistd::{close, ftruncate};
use super::{std,
SharedMem,
LockType,
LockNone,
SharedMemLockImpl,
};
use std::path::PathBuf;
use std::os::raw::c_void;
use std::os::unix::io::RawFd;
use std::ptr::{null_mut};
use std::mem::size_of;
type Result<T> = std::result::Result<T, Box<std::error::Error>>;
struct SharedData {
lock_ind: u8,
}
pub struct MemMetadata<'a> {
owner: bool,
shared_data: *mut SharedData,
map_name: String,
map_fd: RawFd,
map_size: usize,
pub lock_data: *mut c_void,
pub data: *mut c_void,
pub lock_impl : &'a SharedMemLockImpl,
}
impl<'a> Drop for MemMetadata<'a> {
fn drop(&mut self) {
if !self.shared_data.is_null() {
match unsafe {munmap(self.shared_data as *mut _, self.map_size)} {
Ok(_) => {
},
Err(e) => {
println!("Failed to unmap memory while dropping SharedMem !");
println!("{}", e);
},
};
}
if self.map_fd != 0 {
if self.owner {
match shm_unlink(self.map_name.as_str()) {
Ok(_) => {
},
Err(e) => {
println!("Failed to shm_unlink while dropping SharedMem !");
println!("{}", e);
},
};
}
match close(self.map_fd) {
Ok(_) => {
},
Err(e) => {
println!("Failed to close shmem fd while dropping SharedMem !");
println!("{}", e);
},
};
}
}
}
pub fn open(mut new_file: SharedMem) -> Result<SharedMem> {
let is_raw: bool = !new_file.link_path.is_some();
let shmem_path = match new_file.real_path {
Some(ref path) => path.clone(),
None => {
panic!("Tried to open SharedMem with no real_path");
},
};
println!("Openning shared mem \"{}\"", shmem_path);
let map_fd = match shm_open(
shmem_path.as_str(),
OFlag::O_RDWR, Mode::S_IRUSR ) {
Ok(v) => v,
Err(e) => return Err(From::from(format!("shm_open() failed with :\n{}", e))),
};
new_file.real_path = Some(shmem_path.clone());
let file_stat: FileStat = match fstat(map_fd) {
Ok(v) => v,
Err(e) => {
return Err(From::from(e));
}
};
let map_addr: *mut c_void = match unsafe {
mmap(null_mut(), file_stat.st_size as usize, ProtFlags::PROT_READ|ProtFlags::PROT_WRITE, MapFlags::MAP_SHARED, map_fd, 0 )
} {
Ok(v) => v as *mut c_void,
Err(e) => {
match close(map_fd) {_=>{},};
return Err(From::from(format!("mmap() failed with :\n{}", e)))
},
};
if is_raw {
new_file.size = file_stat.st_size as usize;
new_file.meta = Some(
MemMetadata {
owner: false,
map_name: shmem_path,
map_fd: map_fd,
map_size: new_file.size,
shared_data: map_addr as *mut SharedData,
lock_data: null_mut(),
data: map_addr as *mut c_void,
lock_impl: &LockNone {},
}
);
return Ok(new_file);
}
let shared_data: &SharedData = unsafe {&(*(map_addr as *mut SharedData))};
let lock_info = supported_locktype_from_ind(shared_data.lock_ind as usize);
let lock_type: LockType = lock_info.0;
let shared_data_sz = (size_of::<SharedData>() + 3) & !(0x03 as usize);
let lock_data_sz = lock_info.1;
let meta: MemMetadata = MemMetadata {
owner: false,
map_name: shmem_path,
map_fd: map_fd,
map_size: file_stat.st_size as usize,
shared_data: map_addr as *mut SharedData,
lock_data: (map_addr as usize + shared_data_sz) as *mut _,
data: (map_addr as usize + shared_data_sz + lock_data_sz) as *mut c_void,
lock_impl: match lock_type {
LockType::None => &LockNone{},
LockType::Mutex => &Mutex{},
LockType::RwLock => &RwLock{},
},
};
new_file.size = meta.map_size - shared_data_sz - lock_data_sz;
new_file.meta = Some(meta);
Ok(new_file)
}
pub fn create(mut new_file: SharedMem, lock_type: LockType) -> Result<SharedMem> {
let is_raw = new_file.real_path.is_some();
let real_path: String;
if is_raw {
real_path = new_file.real_path.as_ref().unwrap().clone();
} else {
let link_path: &PathBuf = match new_file.link_path {
Some(ref path) => path,
None => panic!("Trying to create SharedMem without link_path set"),
};
let abs_disk_path: PathBuf = link_path.canonicalize()?;
let chars = abs_disk_path.to_string_lossy();
let mut unique_name: String = String::with_capacity(chars.len());
let mut chars = chars.chars();
chars.next();
unique_name.push('/');
for c in chars {
match c {
'/' | '.' => unique_name.push('_'),
v => unique_name.push(v),
};
}
real_path = unique_name;
}
let locktype_info = supported_locktype_info(&lock_type);
let mut shared_data_sz: usize = 0;
let mut lock_ind: u8 = 0;
let mut lock_data_sz: usize = 0;
if !is_raw {
shared_data_sz = (size_of::<SharedData>() + 3) & !(0x03 as usize);
lock_ind = locktype_info.0 as u8;
lock_data_sz = locktype_info.1;
}
let shmem_fd = match shm_open(
real_path.as_str(), OFlag::O_CREAT|OFlag::O_EXCL|OFlag::O_RDWR, Mode::S_IRUSR|Mode::S_IWUSR ) {
Ok(v) => v,
Err(e) => return Err(From::from(format!("shm_open() failed with :\n{}", e))),
};
new_file.real_path = Some(real_path.clone());
let actual_size: usize = new_file.size + lock_data_sz + shared_data_sz;
#[cfg(target_arch="x86")]
let size: i32 = actual_size as i32;
#[cfg(target_arch="x86_64")]
let size: i64 = actual_size as i64;
match ftruncate(shmem_fd, size) {
Ok(_) => {},
Err(e) => {
match shm_unlink(real_path.as_str()) {_=>{},};
match close(shmem_fd) {_=>{},};
return Err(From::from(format!("ftruncate() failed with :\n{}", e)))
},
};
let map_addr: *mut c_void = match unsafe {
mmap(null_mut(), actual_size, ProtFlags::PROT_READ|ProtFlags::PROT_WRITE, MapFlags::MAP_SHARED, shmem_fd, 0 )
} {
Ok(v) => v as *mut c_void,
Err(e) => {
match shm_unlink(real_path.as_str()) {_=>{},};
match close(shmem_fd) {_=>{},};
return Err(From::from(format!("mmap() failed with :\n{}", e)))
},
};
if is_raw {
new_file.meta = Some(MemMetadata {
owner: true,
map_name: real_path,
map_fd: shmem_fd,
map_size: actual_size,
shared_data: map_addr as *mut SharedData,
lock_data: null_mut(),
data: map_addr as *mut c_void,
lock_impl: &LockNone{},
});
return Ok(new_file);
}
let mut meta = MemMetadata {
owner: true,
map_name: real_path,
map_fd: shmem_fd,
map_size: actual_size,
shared_data: map_addr as *mut SharedData,
lock_data: (map_addr as usize + shared_data_sz) as *mut _,
data: (map_addr as usize + shared_data_sz + lock_data_sz) as *mut c_void,
lock_impl: &LockNone{},
};
let shared_data: &mut SharedData = unsafe {
&mut (*meta.shared_data)
};
shared_data.lock_ind = lock_ind;
match lock_type {
LockType::None => {},
LockType::Mutex => {
let mut lock_attr: [u8; size_of::<pthread_mutexattr_t>()] = [0; size_of::<pthread_mutexattr_t>()];
unsafe {
pthread_mutexattr_init(lock_attr.as_mut_ptr() as *mut pthread_mutexattr_t);
pthread_mutexattr_setpshared(lock_attr.as_mut_ptr() as *mut pthread_mutexattr_t, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(meta.lock_data as *mut pthread_mutex_t, lock_attr.as_mut_ptr() as *mut pthread_mutexattr_t);
}
meta.lock_impl = &Mutex{};
},
LockType::RwLock => {
let mut lock_attr: [u8; size_of::<pthread_rwlockattr_t>()] = [0; size_of::<pthread_rwlockattr_t>()];
unsafe {
pthread_rwlockattr_init(lock_attr.as_mut_ptr() as *mut pthread_rwlockattr_t);
pthread_rwlockattr_setpshared(lock_attr.as_mut_ptr() as *mut pthread_rwlockattr_t, PTHREAD_PROCESS_SHARED);
pthread_rwlock_init(meta.lock_data as *mut pthread_rwlock_t, lock_attr.as_mut_ptr() as *mut pthread_rwlockattr_t);
}
meta.lock_impl = &RwLock{};
},
};
new_file.meta = Some(meta);
Ok(new_file)
}
fn supported_locktype_info(lock_type: &LockType) -> (usize, usize) {
match lock_type {
&LockType::None => (0, LockNone::size_of()),
&LockType::Mutex => (1, Mutex::size_of()),
&LockType::RwLock => (2, RwLock::size_of()),
}
}
fn supported_locktype_from_ind(index: usize) -> (LockType, usize) {
match index {
0 => (LockType::None, LockNone::size_of()),
1 => (LockType::Mutex, Mutex::size_of()),
2 => (LockType::RwLock, RwLock::size_of()),
_ => unimplemented!("Linux does not support this locktype index..."),
}
}
pub struct Mutex {}
impl SharedMemLockImpl for Mutex {
fn size_of() -> usize {
size_of::<pthread_mutex_t>()
}
fn rlock(&self, lock_ptr: *mut c_void) -> Result<()> {
unsafe {
pthread_mutex_lock(lock_ptr as *mut pthread_mutex_t);
}
Ok(())
}
fn wlock(&self, lock_ptr: *mut c_void) -> Result<()> {
unsafe {
pthread_mutex_lock(lock_ptr as *mut pthread_mutex_t);
}
Ok(())
}
fn runlock(&self, lock_ptr: *mut c_void) -> () {
unsafe {
pthread_mutex_unlock(lock_ptr as *mut pthread_mutex_t);
}
}
fn wunlock(&self, lock_ptr: *mut c_void) -> () {
unsafe {
pthread_mutex_unlock(lock_ptr as *mut pthread_mutex_t);
}
}
}
pub struct RwLock {}
impl SharedMemLockImpl for RwLock {
fn size_of() -> usize {
size_of::<pthread_rwlock_t>()
}
fn rlock(&self, lock_ptr: *mut c_void) -> Result<()> {
unsafe {
pthread_rwlock_rdlock(lock_ptr as *mut pthread_rwlock_t);
}
Ok(())
}
fn wlock(&self, lock_ptr: *mut c_void) -> Result<()> {
unsafe {
pthread_rwlock_wrlock(lock_ptr as *mut pthread_rwlock_t);
}
Ok(())
}
fn runlock(&self, lock_ptr: *mut c_void) -> () {
unsafe {
pthread_rwlock_unlock(lock_ptr as *mut pthread_rwlock_t);
}
}
fn wunlock(&self, lock_ptr: *mut c_void) -> () {
unsafe {
pthread_rwlock_unlock(lock_ptr as *mut pthread_rwlock_t);
}
}
}