diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 56facc2..bf233a8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -46,8 +46,11 @@ jobs: - name: Tests uses: actions-rs/cargo@v1.0.3 + env: + RUST_LOG: "trace" with: command: test + args: --all-features -- --nocapture - name: Examples uses: actions-rs/cargo@v1.0.3 diff --git a/examples/mutex.rs b/examples/mutex.rs index dbf60df..3ea2e5c 100644 --- a/examples/mutex.rs +++ b/examples/mutex.rs @@ -57,7 +57,7 @@ fn increment_value(shmem_flink: &str, thread_num: usize) { let is_init: &mut AtomicU8; unsafe { - is_init = &mut *(raw_ptr as *mut u8 as *mut AtomicU8); + is_init = &mut *(raw_ptr as *mut AtomicU8); raw_ptr = raw_ptr.add(8); }; diff --git a/src/lib.rs b/src/lib.rs index 3f05937..6661478 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ cfg_if! { compile_error!("shared_memory isnt implemented for this platform..."); } } +pub use os_impl::ShmemConfExt; #[derive(Clone, Default)] /// Struct used to configure different parameters before creating a shared memory mapping @@ -100,6 +101,12 @@ impl ShmemConf { self } + /// Sets the platform-specific parameters + pub fn ext(mut self, ext: os_impl::ShmemConfExt) -> Self { + self.ext = ext; + self + } + /// Create a new mapping using the current configuration pub fn create(mut self) -> Result { if self.size == 0 { @@ -118,7 +125,7 @@ impl ShmemConf { // Generate random ID until one works loop { let cur_id = format!("/shmem_{:X}", rand::random::()); - match os_impl::create_mapping(&cur_id, self.size) { + match os_impl::create_mapping(&cur_id, self.size, &self.ext) { Err(ShmemError::MappingIdExists) => continue, Ok(m) => break m, Err(e) => { @@ -127,7 +134,7 @@ impl ShmemConf { }; } } - Some(ref specific_id) => os_impl::create_mapping(specific_id, self.size)?, + Some(ref specific_id) => os_impl::create_mapping(specific_id, self.size, &self.ext)?, }; debug!("Created shared memory mapping '{}'", mapping.unique_id); diff --git a/src/unix.rs b/src/unix.rs index 86b40c0..45cee78 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -81,7 +81,11 @@ impl MapData { } /// Creates a mapping specified by the uid and size -pub fn create_mapping(unique_id: &str, map_size: usize) -> Result { +pub fn create_mapping( + unique_id: &str, + map_size: usize, + _: &ShmemConfExt, +) -> Result { //Create shared memory file descriptor debug!("Creating persistent mapping at {}", unique_id); diff --git a/src/windows.rs b/src/windows.rs index 96f4a59..19b85a1 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -10,7 +10,8 @@ use crate::ShmemError; #[derive(Clone, Default)] pub struct ShmemConfExt { - allow_raw: bool, + pub allow_raw: bool, + pub suppress_persistency: bool, } impl ShmemConf { @@ -19,6 +20,12 @@ impl ShmemConf { self.ext.allow_raw = allow; self } + + /// If set to true, the persistency of the object will be suppressed + pub fn suppress_persistency(mut self, suppress: bool) -> Self { + self.ext.suppress_persistency = suppress; + self + } } pub struct MapData { @@ -73,7 +80,7 @@ impl Drop for MapData { { Ok(_) => { // 2. Rename file to prevent further use - base_path.push(&format!( + base_path.push(format!( "{}_deleted", self.unique_id.trim_start_matches('/') )); @@ -131,45 +138,36 @@ fn get_tmp_dir() -> Result { } } -fn new_map( +fn map_error(error: u32) -> Result { + Err(if CREATE { + ShmemError::MapCreateFailed(error) + } else { + ShmemError::MapOpenFailed(error) + }) +} + +fn attach_file_mapping( unique_id: &str, - mut map_size: usize, - create: bool, - allow_raw: bool, + map_size: usize, + persistent_file: Option, ) -> Result { - // Create file to back the shared memory - let mut file_path = get_tmp_dir()?; - file_path.push(unique_id.trim_start_matches('/')); - debug!( - "{} persistent_file at {}", - if create { "Creating" } else { "Openning" }, - file_path.to_string_lossy() - ); - - let mut opt = OpenOptions::new(); - opt.read(true) - .write(true) - .share_mode((FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE).0) - .attributes((FILE_ATTRIBUTE_TEMPORARY).0); - if create { - opt.create_new(true); - } else { - opt.create(false); - }; + let map_h = { + debug!( + "{} memory mapping", + if CREATE { "Creating" } else { "Opening" }, + ); - let mut persistent_file = None; - let map_h = match opt.open(&file_path) { - Ok(f) => { - //Create/open Mapping using persistent file - debug!( - "{} memory mapping", - if create { "Creating" } else { "Openning" }, - ); + // if there is an existing persistency file OR we create a new mapping, then use CreateFileMapping + if persistent_file.is_some() || CREATE { + let h_handle = persistent_file + .as_ref() + .map(|f| HANDLE(f.as_raw_handle() as _)) + .unwrap_or(INVALID_HANDLE_VALUE); let high_size: u32 = ((map_size as u64 & 0xFFFF_FFFF_0000_0000_u64) >> 32) as u32; let low_size: u32 = (map_size as u64 & 0xFFFF_FFFF_u64) as u32; trace!( "CreateFileMapping({:?}, NULL, {:X}, {}, {}, '{}')", - HANDLE(f.as_raw_handle() as _), + h_handle, PAGE_READWRITE.0, high_size, low_size, @@ -177,41 +175,25 @@ fn new_map( ); match CreateFileMapping( - HANDLE(f.as_raw_handle() as _), + h_handle, None, PAGE_READWRITE, high_size, low_size, unique_id, ) { - Ok(v) => { - persistent_file = Some(f); - v - } + Ok(v) => v, Err(e) => { let err_code = e.win32_error().unwrap(); return if err_code == ERROR_ALREADY_EXISTS { Err(ShmemError::MappingIdExists) } else { - Err(if create { - ShmemError::MapCreateFailed(err_code.0) - } else { - ShmemError::MapOpenFailed(err_code.0) - }) + map_error::(err_code.0) }; } } - } - Err(e) if e.kind() == ErrorKind::AlreadyExists => return Err(ShmemError::MappingIdExists), - Err(e) => { - if create { - return Err(ShmemError::MapCreateFailed(e.raw_os_error().unwrap() as _)); - } else if !allow_raw { - return Err(ShmemError::MapOpenFailed(ERROR_FILE_NOT_FOUND.0)); - } - - // This may be a mapping that isnt managed by this crate - // Try to open the mapping without any backing file + } else { + // open existing mapping instead trace!( "OpenFileMappingW({:?}, {}, '{}')", FILE_MAP_ALL_ACCESS, @@ -237,38 +219,81 @@ fn new_map( ); let map_ptr = match MapViewOfFile(map_h.as_handle(), FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0) { Ok(v) => v, - Err(e) => { - return Err(if create { - ShmemError::MapCreateFailed(e.win32_error().unwrap().0) - } else { - ShmemError::MapOpenFailed(e.win32_error().unwrap().0) - }) - } + Err(e) => return Err(ShmemError::UnknownOsError(e.win32_error().unwrap().0)), }; trace!("\t{:p}", map_ptr); - if !create { - //Get the real size of the openned mapping - let mut info = MEMORY_BASIC_INFORMATION::default(); - if let Err(e) = VirtualQuery(map_ptr.as_mut_ptr(), &mut info) { - return Err(ShmemError::UnknownOsError(e.win32_error().unwrap().0)); - } - map_size = info.RegionSize; + //Get the real size of the opened mapping + let mut info = MEMORY_BASIC_INFORMATION::default(); + if let Err(e) = VirtualQuery(map_ptr.as_mut_ptr(), &mut info) { + return Err(ShmemError::UnknownOsError(e.win32_error().unwrap().0)); } Ok(MapData { - owner: create, + owner: CREATE, file_map: map_h, persistent_file, unique_id: unique_id.to_string(), - map_size, + map_size: info.RegionSize, view: map_ptr, }) } +fn attach_persistent_file_mapping( + unique_id: &str, + map_size: usize, + allow_raw: bool, +) -> Result { + // Create file to back the shared memory + let mut file_path = get_tmp_dir()?; + file_path.push(unique_id.trim_start_matches('/')); + debug!( + "{} persistent_file at {}", + if CREATE { "Creating" } else { "Opening" }, + file_path.to_string_lossy() + ); + + match OpenOptions::new() + .read(true) + .write(true) + .share_mode((FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE).0) + .attributes((FILE_ATTRIBUTE_TEMPORARY).0) + .create_new(CREATE) + .create(false) + .open(&file_path) + { + Ok(f) => attach_file_mapping::(unique_id, map_size, Some(f)), + Err(e) if e.kind() == ErrorKind::AlreadyExists => Err(ShmemError::MappingIdExists), + Err(e) => { + if !CREATE && allow_raw { + // This may be a mapping that isnt managed by this crate + // Try to open the mapping without any backing file + return attach_file_mapping::(unique_id, map_size, None); + } + map_error::(e.raw_os_error().unwrap() as _) + } + } +} + +//Creates a mapping specified by the uid and size +pub fn attach( + unique_id: &str, + map_size: usize, + ext: &ShmemConfExt, +) -> Result { + match ext.suppress_persistency { + true => attach_file_mapping::(unique_id, map_size, None), + false => attach_persistent_file_mapping::(unique_id, map_size, ext.allow_raw), + } +} + //Creates a mapping specified by the uid and size -pub fn create_mapping(unique_id: &str, map_size: usize) -> Result { - new_map(unique_id, map_size, true, false) +pub fn create_mapping( + unique_id: &str, + map_size: usize, + ext: &ShmemConfExt, +) -> Result { + attach::(unique_id, map_size, ext) } //Opens an existing mapping specified by its uid @@ -277,5 +302,5 @@ pub fn open_mapping( map_size: usize, ext: &ShmemConfExt, ) -> Result { - new_map(unique_id, map_size, false, ext.allow_raw) + attach::(unique_id, map_size, ext) } diff --git a/tests/general.rs b/tests/general.rs index f689f54..b247826 100644 --- a/tests/general.rs +++ b/tests/general.rs @@ -57,7 +57,7 @@ fn open_os_id() { // Drop the owner of the mapping drop(s1); - // Make sure it can be openned again + // Make sure it can't be openned again assert!(ShmemConf::new().os_id(&os_id).open().is_err()); drop(s2); diff --git a/tests/posix_semantics.rs b/tests/posix_semantics.rs index dc10536..b100233 100644 --- a/tests/posix_semantics.rs +++ b/tests/posix_semantics.rs @@ -9,6 +9,7 @@ fn persistence() { shmem.set_owner(false); String::from(shmem.get_os_id()) }; + let mut shmem = ShmemConf::new().os_id(os_id).open().unwrap(); shmem.set_owner(true); } diff --git a/tests/windows.rs b/tests/windows.rs new file mode 100644 index 0000000..ad36dec --- /dev/null +++ b/tests/windows.rs @@ -0,0 +1,21 @@ +#[cfg(target_os = "windows")] +mod windows_tests { + use shared_memory::{ShmemConf, ShmemConfExt}; + + #[test] + fn suppress_persistency() { + let os_id = { + let mut shmem = ShmemConf::new() + .size(4096) + .ext(ShmemConfExt { + allow_raw: false, + suppress_persistency: true, + }) + .create() + .unwrap(); + shmem.set_owner(false); + String::from(shmem.get_os_id()) + }; + assert!(ShmemConf::new().os_id(os_id).open().is_err()); + } +}