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
use libc::{c_void, mmap, munmap, MAP_ANONYMOUS, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
use std::{mem::size_of, ptr::null_mut};
/// An object that can be shared between processes.
///
/// After spawning child process (using `fork()`, `clone()`, etc.) updates to this object will be seen by both processes.
/// This is achieved by allocating memory using mmap with `MAP_SHARED` flag.
///
/// For more details see [man page](https://man7.org/linux/man-pages/man2/mmap.2.html).
///
/// # Example
/// ```rust
/// # use std::error::Error;
/// # use std::thread::sleep;
/// # use std::time::Duration;
/// #
/// # use libc::fork;
/// #
/// # use process_sync::private::check_libc_err;
/// # use process_sync::SharedMemoryObject;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// let mut shared = SharedMemoryObject::new(123)?;
///
/// let pid = unsafe { fork() };
/// assert!(pid >= 0);
///
/// if pid == 0 {
/// assert_eq!(*shared.get(), 123);
/// *shared.get_mut() = 456;
/// sleep(Duration::from_millis(40));
/// assert_eq!(*shared.get(), 789);
/// } else {
/// sleep(Duration::from_millis(20));
/// assert_eq!(*shared.get(), 456);
/// *shared.get_mut() = 789;
/// }
/// #
/// # Ok(())
/// # }
/// ```
pub struct SharedMemoryObject<T> {
ptr: *mut T,
}
impl<T: Sync + Send> SharedMemoryObject<T> {
/// Allocates shared memory and moves `obj` there.
///
/// # Errors
/// If allocation fails returns error from [`last_os_error`].
///
/// [`last_os_error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html#method.last_os_error
pub fn new(obj: T) -> std::io::Result<Self> {
let addr = allocate_shared_memory(size_of::<T>())?;
let addr = addr as *mut T;
unsafe { *addr = obj };
Ok(Self { ptr: addr })
}
/// Returns reference to underlying object.
///
/// # Safety
/// See [`get_mut`](#method.get_mut).
pub fn get(&self) -> &T {
unsafe { &*self.ptr }
}
/// Returns mutable reference to underlying object.
///
/// # Safety
/// This function (and [`get`](#method.get)) is always safe to call, but access to data under returned reference
/// must be somehow synchronized with another processes to avoid data race.
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<T> Drop for SharedMemoryObject<T> {
fn drop(&mut self) {
// every process owning shared memory object must free it individually
free_shared_memory(self.ptr as *mut c_void, size_of::<T>())
.expect("cannot munmap() shared memory");
}
}
fn allocate_shared_memory(len: usize) -> std::io::Result<*mut c_void> {
let addr = unsafe {
mmap(
null_mut(),
len,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1,
0,
)
};
if addr == MAP_FAILED {
return Err(std::io::Error::last_os_error());
}
Ok(addr)
}
fn free_shared_memory(addr: *mut c_void, len: usize) -> std::io::Result<()> {
let ret = unsafe { munmap(addr, len) };
if ret != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}