1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
//! Provides a wrapper around native shared memory.
//!
//! This crate is ideal if you need to share large amounts of data with another process purely through memory.
//!
//! ## Examples
//! Creator based on examples/create.rs
//! ```
//! //Create a SharedMem at `pwd`\shared_mem.link that links to a shared memory mapping of size 4096 and managed by a mutex.
//! let mut my_shmem: SharedMem = match SharedMem::create(PathBuf::from("shared_mem.link") LockType::Mutex, 4096).unwrap();
//! //Set explicit scope for the lock (no need to call drop(shared_data))
//! {
//!     //Acquire write lock
//!     let mut shared_data = match my_shmem.wlock_as_slice::<u8>().unwrap();
//!     let src = b"Some string you want to share\x00";
//!     //Write to the shared memory
//!     shared_data[0..src.len()].copy_from_slice(src);
//! }
//! ```
//!
//! Slave based on examples/open.rs
//! ```
// Open an existing SharedMem from `pwd`\shared_mem.link
//! let mut my_shmem: SharedMem = match SharedMem::open(PathBuf::from("shared_mem.link")).unwrap();
//! //Set explicit scope for the lock (no need to call drop(shared_data))
//! {
//!     //Acquire read lock
//!     let mut shared_data = match my_shmem.rlock_as_slice::<u8>().unwrap();
//!     //Print the content of the shared memory as chars
//!     for byte in &shared_data[0..256] {
//!         if *byte == 0 { break; }
//!         print!("{}", *byte as char);
//!     }
//! }
//! ```

#[macro_use]
extern crate cfg_if;

//Load up the proper implementations
cfg_if! {
    if #[cfg(target_os="windows")] {
        mod win;
        use win as os_impl;
    } else if #[cfg(target_os="linux")] {
        mod linux;
        use linux as os_impl;
    } else if #[cfg(target_os="macos")] {
        mod macos;
        use macos as os_impl;
    } else {
        compile_error!("This library isnt implemented for this platform...");
    }
}

//Include definitions from locking.rs
mod locking;
pub use locking::*;

use std::path::PathBuf;
use std::fs::{File};
use std::io::{Write, Read};
use std::fs::remove_file;
use std::slice;

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

///Struct used to manipulate the shared memory
pub struct SharedMem<'a> {
    ///Meta data to help manage this SharedMem
    meta: Option<os_impl::MemMetadata<'a>>,
    ///Did we create this SharedMem
    owner: bool,
    ///Path to the SharedMem link on disk
    link_path: Option<PathBuf>,
    ///Path to the OS's identifier for the shared memory object
    real_path: Option<String>,
    ///Size of the mapping
    size: usize,
}

impl<'a> SharedMem<'a> {
    /// Creates a new SharedMem
    ///
    /// This involves creating a "link" on disk specified by the first parameter.
    /// This link contains the OS specific identifier to the shared memory. The usage of such link files
    /// on disk help manage identifier colisions. (ie: a binary using the same argument to this function
    /// can be ran from different directories without worrying about collisions)
    ///
    /// # Examples
    /// ```
    /// # use shared_memory::*;
    /// # use std::path::PathBuf;
    /// # let mut my_shmem: SharedMem = match SharedMem::open(PathBuf::from("shared_mem.link")) {Ok(v) => v, Err(_) => return,};
    /// //Creates a new shared SharedMem named shared_mem.link of size 4096
    /// let mut my_shmem: SharedMem = match SharedMem::create(PathBuf::from("shared_mem.link"), LockType::Mutex, 4096) {
    ///     Ok(v) => v,
    ///     Err(e) => {
    ///         println!("Error : {}", e);
    ///         println!("Failed to create SharedMem...");
    ///         return;
    ///     }
    /// };
    /// ```
    pub fn create(new_link_path: PathBuf, lock_type: LockType, size: usize) -> Result<SharedMem<'a>> {

        let mut cur_link;
        if new_link_path.is_file() {
            return Err(From::from("Cannot create SharedMem because file already exists"));
        } else {
            cur_link = File::create(&new_link_path)?;
        }

        let my_shmem: SharedMem = SharedMem {
            meta: None,
            owner: true,
            link_path: Some(new_link_path),
            real_path: None,
            size: size,
        };

        let created_file = os_impl::create(my_shmem, lock_type)?;

        //Write OS specific identifier in link file
        if let Some(ref real_path) = created_file.real_path {
            match cur_link.write(real_path.as_bytes()) {
                Ok(write_sz) => if write_sz != real_path.as_bytes().len() {
                    return Err(From::from("Failed to write full contents info on disk"));
                },
                Err(_) => return Err(From::from("Failed to write info on disk")),
            };
        } else {
            panic!("os_impl::create() returned succesfully but didnt update SharedMem::real_path() !");
        }

        Ok(created_file)
    }
    ///Opens an existing SharedMem
    ///
    /// This function takes a path to a link file created by create().
    /// Open() will automatically detect the size and locking mechanisms.
    ///
    /// # Examples
    /// ```
    /// use shared_memory::*;
    /// //Opens an existing shared SharedMem named test.txt
    /// let mut my_shmem: SharedMem = match SharedMem::open(PathBuf::from("shared_mem.link")) {
    ///     Ok(v) => v,
    ///     Err(e) => {
    ///         println!("Error : {}", e);
    ///         println!("Failed to open SharedMem...");
    ///         return;
    ///     }
    /// };
    /// ```
    pub fn open(existing_link_path: PathBuf) -> Result<SharedMem<'a>> {

        // Make sure the link file exists
        if !existing_link_path.is_file() {
            return Err(From::from("Cannot open SharedMem because file doesnt exists"));
        }

        let mut my_shmem: SharedMem = SharedMem {
            meta: None,
            owner: false,
            link_path: Some(existing_link_path.clone()),
            real_path: None,
            size: 0, //os_open needs to fill this field up
        };

        //Get real_path from link file
        {
            let mut disk_file = File::open(&existing_link_path)?;
            let mut file_contents: Vec<u8> = Vec::with_capacity(existing_link_path.to_string_lossy().len() + 5);
            disk_file.read_to_end(&mut file_contents)?;
            my_shmem.real_path = Some(String::from_utf8(file_contents)?);
        }

        //Open the shared memory using the real_path
        os_impl::open(my_shmem)
    }
    ///Creates a raw shared memory object. Only use this method if you do not wish to have all the nice features of a regular SharedMem.
    ///
    ///This function is useful when creating mappings for libraries/applications that do not use SharedMem.
    ///By using this function, you explicitly mean : do not create anything else than a memory mapping.
    ///
    ///The first argument needs to be a valid identifier for the OS in use.
    ///colisions wont be avoided through link files and no meta data (locks) is added to the shared mapping.
    pub fn create_raw(shmem_path: String, size: usize) -> Result<SharedMem<'a>> {
        let my_shmem: SharedMem = SharedMem {
            meta: None,
            owner: true,
            link_path: None, //Leave this explicitly empty
            real_path: Some(shmem_path),
            size: size,
        };

        Ok(os_impl::create(my_shmem, LockType::None)?)
    }
    ///Opens an existing shared memory mappping in raw mode.
    ///This simply opens an existing mapping with no additionnal features (no locking, no metadata, etc...).
    ///
    ///This function is useful when using mappings not created by my_shmem.
    ///
    ///To use this function, you need to pass a valid OS shared memory identifier as an argument.
    pub fn open_raw(shmem_path: String) -> Result<SharedMem<'a>> {

        let my_shmem: SharedMem = SharedMem {
            meta: None,
            owner: false,
            link_path: None, //Leave this explicity to None to specify raw mode
            real_path: Some(shmem_path),
            size: 0, //os_open needs to fill this field up
        };

        //Open the shared memory using the real_path
        os_impl::open(my_shmem)
    }

    ///Returns the size of the SharedMem
    pub fn get_size(&self) -> &usize {
        &self.size
    }
    ///Returns the link_path of the SharedMem
    pub fn get_link_path(&self) -> Option<&PathBuf> {
        self.link_path.as_ref()
    }
    ///Returns the OS specific path of the shared memory object
    ///
    /// Usualy on Linux, this will point to a file under /dev/shm/
    ///
    /// On Windows, this returns a namespace
    pub fn get_real_path(&self) -> Option<&String> {
        self.real_path.as_ref()
    }
}

impl<'a> Drop for SharedMem<'a> {
    ///Deletes the SharedMem artifacts
    fn drop(&mut self) {
        //Delete file on disk if we created it
        if self.owner {
            if let Some(ref file_path) = self.link_path {
                if file_path.is_file() {
                    match remove_file(file_path) {_=>{},};
                }
            }
        }
        //Drop our internal view of the SharedMem
        if let Some(meta) = self.meta.take() {
            drop(meta);
        }
    }
}

/// Read [WARNING](trait.SharedMemCast.html#warning) before use
///
/// Trait used to indicate that a type can be cast over the shared memory.
///
/// For now, shared_memory implements the trait on almost all primitive types.
///
/// ### __<span style="color:red">WARNING</span>__
///
/// Only implement this trait if you understand the implications of mapping Rust types to shared memory.
/// When doing so, you should be mindful of :
/// * Does my type have any pointers in its internal representation ?
///    * This is important because pointers in your type need to also point to the shared memory for it to be usable by other processes
/// * Can my type resize its contents ?
///    * If so, the type probably cannot be safely used over shared memory because your type might call alloc/realloc/free on shared memory addresses
/// * Does my type allow for initialisation after instantiation ?
///    * A [R|W]lock to the shared memory returns a reference to your type. That means that any use of that reference assumes that the type was properly initialized.
///
/// An example of a type that __shouldnt__ be cast to the shared memory would be Vec.
/// Vec internaly contains a pointer to a slice containing its data and some other metadata.
/// This means that to cast a Vec to the shared memory, the memory has to already be initialized with valid pointers and metadata.
/// Granted we could initialize those fields manually, the use of the vector might then trigger a free/realloc on our shared memory.
///
/// # Examples
/// ```
/// struct SharedState {
///     num_listenners: u32,
///     message: [u8; 256],
/// }
/// //WARNING : Only do this if you know what you're doing.
/// unsafe impl SharedMemCast for SharedState {}
///
/// <...>
///
/// {
///     let mut shared_state: WriteLockGuard<SharedState> = match my_shmem.wlock().unwrap();
///     shared_state.num_listenners = 0;
///     let src = b"Welcome, we currently have 0 listenners !\x00";
///     shared_state.message[0..src.len()].copy_from_slice(src);
/// }
///```
pub unsafe trait SharedMemCast {}
unsafe impl SharedMemCast for bool {}
unsafe impl SharedMemCast for char {}
unsafe impl SharedMemCast for str {}
unsafe impl SharedMemCast for i8 {}
unsafe impl SharedMemCast for i16 {}
unsafe impl SharedMemCast for i32 {}
unsafe impl SharedMemCast for u8 {}
unsafe impl SharedMemCast for i64 {}
unsafe impl SharedMemCast for u16 {}
unsafe impl SharedMemCast for u64 {}
unsafe impl SharedMemCast for isize {}
unsafe impl SharedMemCast for u32 {}
unsafe impl SharedMemCast for usize {}
unsafe impl SharedMemCast for f32 {}
unsafe impl SharedMemCast for f64 {}