process_sync/shared_memory.rs
1use libc::{c_void, mmap, munmap, MAP_ANONYMOUS, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
2use std::{mem::size_of, ptr::null_mut};
3
4/// An object that can be shared between processes.
5///
6/// After spawning child process (using `fork()`, `clone()`, etc.) updates to this object will be seen by both processes.
7/// This is achieved by allocating memory using mmap with `MAP_SHARED` flag.
8///
9/// For more details see [man page](https://man7.org/linux/man-pages/man2/mmap.2.html).
10///
11/// # Example
12/// ```rust
13/// # use std::error::Error;
14/// # use std::thread::sleep;
15/// # use std::time::Duration;
16/// #
17/// # use libc::fork;
18/// #
19/// # use process_sync::private::check_libc_err;
20/// # use process_sync::SharedMemoryObject;
21/// #
22/// # fn main() -> Result<(), Box<dyn Error>> {
23/// #
24/// let mut shared = SharedMemoryObject::new(123)?;
25///
26/// let pid = unsafe { fork() };
27/// assert!(pid >= 0);
28///
29/// if pid == 0 {
30/// assert_eq!(*shared.get(), 123);
31/// *shared.get_mut() = 456;
32/// sleep(Duration::from_millis(40));
33/// assert_eq!(*shared.get(), 789);
34/// } else {
35/// sleep(Duration::from_millis(20));
36/// assert_eq!(*shared.get(), 456);
37/// *shared.get_mut() = 789;
38/// }
39/// #
40/// # Ok(())
41/// # }
42/// ```
43pub struct SharedMemoryObject<T> {
44 ptr: *mut T,
45}
46
47impl<T: Sync + Send> SharedMemoryObject<T> {
48 /// Allocates shared memory and moves `obj` there.
49 ///
50 /// # Errors
51 /// If allocation fails returns error from [`last_os_error`].
52 ///
53 /// [`last_os_error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html#method.last_os_error
54 pub fn new(obj: T) -> std::io::Result<Self> {
55 let addr = allocate_shared_memory(size_of::<T>())?;
56
57 let addr = addr as *mut T;
58 unsafe { *addr = obj };
59
60 Ok(Self { ptr: addr })
61 }
62
63 /// Returns reference to underlying object.
64 ///
65 /// # Safety
66 /// See [`get_mut`](#method.get_mut).
67 pub fn get(&self) -> &T {
68 unsafe { &*self.ptr }
69 }
70
71 /// Returns mutable reference to underlying object.
72 ///
73 /// # Safety
74 /// This function (and [`get`](#method.get)) is always safe to call, but access to data under returned reference
75 /// must be somehow synchronized with another processes to avoid data race.
76 pub fn get_mut(&mut self) -> &mut T {
77 unsafe { &mut *self.ptr }
78 }
79}
80
81impl<T> Drop for SharedMemoryObject<T> {
82 fn drop(&mut self) {
83 // every process owning shared memory object must free it individually
84 free_shared_memory(self.ptr as *mut c_void, size_of::<T>())
85 .expect("cannot munmap() shared memory");
86 }
87}
88
89fn allocate_shared_memory(len: usize) -> std::io::Result<*mut c_void> {
90 let addr = unsafe {
91 mmap(
92 null_mut(),
93 len,
94 PROT_READ | PROT_WRITE,
95 MAP_SHARED | MAP_ANONYMOUS,
96 -1,
97 0,
98 )
99 };
100 if addr == MAP_FAILED {
101 return Err(std::io::Error::last_os_error());
102 }
103 Ok(addr)
104}
105
106fn free_shared_memory(addr: *mut c_void, len: usize) -> std::io::Result<()> {
107 let ret = unsafe { munmap(addr, len) };
108 if ret != 0 {
109 return Err(std::io::Error::last_os_error());
110 }
111 Ok(())
112}