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
use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr;
use crate::INVALID_FD;
/// POSIX shared memory wrapper for Rust
pub struct POSIXShm<T>
where
T: Clone,
{
file_path: String,
fd_shm: i32,
mem_size: usize,
ptr_data: *mut libc::c_void,
phantom: PhantomData<T>,
}
impl<T> POSIXShm<T>
where
T: Clone,
{
/// Create a new shared memory object
/// # Arguments
/// * `path` - Path to shared memory
/// * `size` - Size of shared memory
/// # Example
/// ```
/// use rushm::posixaccessor::POSIXShm;
/// use std::mem;
/// let shm_name = "test_shared_memory_file";
/// let mut posix_shm = POSIXShm::<u64>::new(shm_name.to_string(), std::mem::size_of::<u64>());
/// ```
/// # Safety
/// This function is unsafe because it uses raw pointers
/// and can cause undefined behavior if misused
/// # Returns
/// A new shared memory object
/// # Panics
/// This function panics if the path is not a valid C string
/// or if the size is not a valid size
pub fn new(path: String, size: usize) -> Self {
POSIXShm {
file_path: path,
fd_shm: INVALID_FD,
mem_size: size,
ptr_data: std::ptr::null_mut(),
phantom: PhantomData,
}
}
/// Open shared memory
/// # Safety
/// This function is unsafe because it uses raw pointers
/// and can cause undefined behavior if misused
/// # Returns
/// An empty result if successful, otherwise an error message
/// # Panics
/// This function panics if the pointer to shared memory is null
/// or if the shared memory cannot be truncated
/// or if the shared memory cannot be opened
/// or if the shared memory cannot be mapped
pub unsafe fn open(&mut self) -> Result<(), String> {
let c_shm_name = CString::new(self.file_path.to_string()).unwrap();
self.fd_shm = libc::shm_open(
c_shm_name.as_ptr(),
libc::O_CREAT | libc::O_RDWR,
libc::S_IRUSR | libc::S_IWUSR,
);
if self.fd_shm > 0 {
let ret = libc::ftruncate(self.fd_shm, self.mem_size.try_into().unwrap());
if ret < 0 {
return Err(String::from("Cannot truncate"));
}
} else {
let e = libc::__errno_location();
return Err(String::from(format!(
"Error opening shared memory {} errno {}",
self.fd_shm, *e
)));
}
self.ptr_data = libc::mmap(
ptr::null_mut(),
self.mem_size,
libc::PROT_WRITE,
libc::MAP_SHARED,
self.fd_shm,
0,
);
if self.ptr_data.is_null() {
return Err(String::from("Error, mmap failed"));
}
Ok(())
}
/// Read shared memory
/// # Safety
/// This function is unsafe because it uses raw pointers
/// and can cause undefined behavior if misused
/// # Returns
/// A reference to the shared memory
/// # Panics
/// This function panics if the pointer to shared memory is null
/// or if the shared memory destination is null
pub unsafe fn read(&mut self) -> Result<&T, String> {
if self.ptr_data.is_null() {
return Err(String::from("Error, pointer to shared memory is NULL"));
}
let p = self.ptr_data as *const T;
Ok(&*p)
}
/// Write to shared memory
/// # Arguments
/// * `value` - Value to write to shared memory
/// # Safety
/// This function is unsafe because it uses raw pointers
/// and can cause undefined behavior if misused
/// # Returns
/// An empty result if successful, otherwise an error message
/// # Panics
/// This function panics if the pointer to shared memory is null
/// or if the shared memory destination is null
pub unsafe fn write(&mut self, value: T) -> Result<(), String> {
if self.ptr_data.is_null() {
return Err(String::from("Error, pointer to shared memory is NULL"));
}
let w: *mut T = self.ptr_data as *mut T;
*w = value;
Ok(())
}
/// Get a mutable pointer to shared memory
/// # Returns
/// A mutable pointer to shared memory
pub fn get_cptr_mut(&self) -> *mut libc::c_void {
self.ptr_data
}
/// Get a mutable pointer to shared memory
/// # Returns
/// A mutable pointer to shared memory
pub fn get_as_mut(&self) -> *mut T {
self.ptr_data as *mut T
}
/// Set shared memory to a value
/// # Arguments
/// * `value` - Value to set shared memory to
/// * `n` - Number of bytes to set
/// # Safety
/// This function is unsafe because it uses raw pointers
/// and can cause undefined behavior if misused
/// # Returns
/// An empty result if successful, otherwise an error message
/// # Panics
/// This function panics if the pointer to shared memory is null
/// or if the shared memory destination is null
/// or if the shared memory cannot be set
pub unsafe fn memset(&mut self, value: i32, n: usize) -> Result<(), String> {
if self.ptr_data.is_null() {
return Err(String::from("Pointer to shared memory is NULL"));
}
libc::memset(self.ptr_data, value, n);
Ok(())
}
/// Close shared memory
/// # Arguments
/// * `unlink` - Unlink shared memory
/// # Returns
/// An empty result if successful, otherwise an error message
/// # Panics
/// This function panics if the shared memory cannot be unmapped
/// or if the shared memory cannot be closed
pub unsafe fn close(&mut self, unlink: bool) -> Result<(), String> {
let ret = libc::munmap(self.ptr_data, self.mem_size);
if ret < 0 {
return Err(String::from("Error unmapping shared memory"));
}
let ret = libc::close(self.fd_shm);
if ret < 0 {
return Err(String::from("Error closing shared memory"));
}
if unlink {
let c_shm_name = CString::new(self.file_path.to_string()).unwrap();
let ret = libc::shm_unlink(c_shm_name.as_ptr());
if ret < 0 {
return Err(String::from("Error unlinking shared memory"));
}
}
Ok(())
}
}
#[cfg(test)]
use std::mem;
#[test]
fn test_shared_memory_write_read() {
unsafe {
let shm_name = "test_shared_memory_file";
let mut posix_shm = POSIXShm::<u64>::new(shm_name.to_string(), mem::size_of::<u64>());
let ret = posix_shm.open();
assert!(ret.is_ok());
let mut val: u64 = 0xAA;
let ret = posix_shm.write(val);
assert!(ret.is_ok());
let ret: u64 = *posix_shm.read().unwrap();
assert_eq!(val, ret);
let ret = posix_shm.memset(0, mem::size_of::<u64>());
assert!(ret.is_ok());
let ret: u64 = *posix_shm.read().unwrap();
assert_eq!(ret, 0);
let shm_ptr = posix_shm.get_cptr_mut();
val = 0xFF;
let mut cptr_mut: *mut u64 = shm_ptr as *mut u64;
*cptr_mut = val;
let ret: u64 = *posix_shm.read().unwrap();
assert_eq!(ret, val);
cptr_mut = posix_shm.get_as_mut();
val = 42;
*cptr_mut = val;
let ret: u64 = *posix_shm.read().unwrap();
assert_eq!(ret, val);
let ret = posix_shm.close(true);
assert!(ret.is_ok());
}
}