Skip to content

Commit d00f8cf

Browse files
committed
feat: add tmpfs support as alternative to shm_open
1 parent 8eb7859 commit d00f8cf

5 files changed

Lines changed: 545 additions & 33 deletions

File tree

rust-toolchain.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[toolchain]
2+
channel = "1.88.0"
3+
components = ["rustfmt", "clippy", "rust-src", "rust-analyzer"]

src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub enum ShmemError {
1313
MapCreateFailed(u32),
1414
MapOpenFailed(u32),
1515
UnknownOsError(u32),
16+
NotInTmpfsMode,
17+
NoTmpfsBaseDir,
1618
}
1719

1820
impl std::fmt::Display for ShmemError {
@@ -31,6 +33,8 @@ impl std::fmt::Display for ShmemError {
3133
ShmemError::MapCreateFailed(err) => write!(f, "Creating the shared memory failed, os error {err}"),
3234
ShmemError::MapOpenFailed(err) => write!(f, "Opening the shared memory failed, os error {err}"),
3335
ShmemError::UnknownOsError(err) => write!(f, "An unexpected OS error occurred, os error {err}"),
36+
ShmemError::NotInTmpfsMode => f.write_str("Operation requires tmpfs mode to be enabled"),
37+
ShmemError::NoTmpfsBaseDir => f.write_str("No tmpfs base directory specified"),
3438
}
3539
}
3640
}

src/lib.rs

Lines changed: 104 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ pub struct ShmemConf {
5555
size: usize,
5656
ext: os_impl::ShmemConfExt,
5757
mode: Option<Mode>,
58+
use_tmpfs: bool,
59+
tmpfs_base_dir: Option<PathBuf>,
5860
}
5961
impl Drop for ShmemConf {
6062
fn drop(&mut self) {
@@ -108,6 +110,35 @@ impl ShmemConf {
108110
self
109111
}
110112

113+
/// Enable tmpfs mode with a specific base directory
114+
///
115+
/// This will use regular files in the specified directory instead of POSIX shared memory
116+
pub fn use_tmpfs_with_dir<P: AsRef<Path>>(mut self, base_dir: P) -> Self {
117+
self.use_tmpfs = true;
118+
self.tmpfs_base_dir = Some(PathBuf::from(base_dir.as_ref()));
119+
self
120+
}
121+
122+
/// Get the tmpfs file path for this configuration
123+
fn get_tmpfs_file_path(&self) -> Result<PathBuf, ShmemError> {
124+
if !self.use_tmpfs {
125+
return Err(ShmemError::NotInTmpfsMode);
126+
}
127+
128+
let base_dir = self
129+
.tmpfs_base_dir
130+
.as_ref()
131+
.ok_or(ShmemError::NoTmpfsBaseDir)?;
132+
133+
if let Some(ref os_id) = self.os_id {
134+
// Use os_id as filename
135+
Ok(base_dir.join(format!("shmem_{os_id}")))
136+
} else {
137+
// Generate random filename
138+
Ok(base_dir.join(format!("shmem_{:X}", rand::random::<u64>())))
139+
}
140+
}
141+
111142
/// Create a new mapping using the current configuration
112143
pub fn create(mut self) -> Result<Shmem, ShmemError> {
113144
if self.size == 0 {
@@ -121,21 +152,51 @@ impl ShmemConf {
121152
}
122153

123154
// Create the mapping
124-
let mapping = match self.os_id {
125-
None => {
126-
// Generate random ID until one works
155+
let mapping = if self.use_tmpfs {
156+
// tmpfs mode
157+
if self.os_id.is_some() {
158+
// Use specified os_id
159+
let tmpfs_file_path = self.get_tmpfs_file_path()?;
160+
os_impl::create_mapping_tmpfs(
161+
tmpfs_file_path
162+
.to_str()
163+
.ok_or(ShmemError::UnknownOsError(0))?,
164+
self.size,
165+
self.mode,
166+
)?
167+
} else {
168+
// Generate random filename until one works
127169
loop {
128-
let cur_id = format!("/shmem_{:X}", rand::random::<u64>());
129-
match os_impl::create_mapping(&cur_id, self.size, self.mode) {
170+
let random_path = self.get_tmpfs_file_path()?;
171+
match os_impl::create_mapping_tmpfs(
172+
random_path.to_str().ok_or(ShmemError::UnknownOsError(0))?,
173+
self.size,
174+
self.mode,
175+
) {
130176
Err(ShmemError::MappingIdExists) => continue,
131177
Ok(m) => break m,
132-
Err(e) => {
133-
return Err(e);
178+
Err(e) => return Err(e),
179+
}
180+
}
181+
}
182+
} else {
183+
// shm_open mode
184+
match self.os_id {
185+
None => {
186+
// Generate random ID until one works
187+
loop {
188+
let cur_id = format!("/shmem_{:X}", rand::random::<u64>());
189+
match os_impl::create_mapping(&cur_id, self.size, self.mode) {
190+
Err(ShmemError::MappingIdExists) => continue,
191+
Ok(m) => break m,
192+
Err(e) => return Err(e),
134193
}
135-
};
194+
}
195+
}
196+
Some(ref specific_id) => {
197+
os_impl::create_mapping(specific_id, self.size, self.mode)?
136198
}
137199
}
138-
Some(ref specific_id) => os_impl::create_mapping(specific_id, self.size, self.mode)?,
139200
};
140201
debug!("Created shared memory mapping '{}'", mapping.unique_id);
141202

@@ -153,7 +214,7 @@ impl ShmemConf {
153214

154215
match open_options.open(flink_path) {
155216
Ok(mut f) => {
156-
// write the shmem uid asap
217+
// write the mapping identifier
157218
if let Err(e) = f.write(mapping.unique_id.as_bytes()) {
158219
let _ = std::fs::remove_file(flink_path);
159220
return Err(ShmemError::LinkWriteFailed(e));
@@ -183,36 +244,50 @@ impl ShmemConf {
183244

184245
/// Opens an existing mapping using the current configuration
185246
pub fn open(mut self) -> Result<Shmem, ShmemError> {
186-
// Must at least have a flink or an os_id
187-
if self.flink_path.is_none() && self.os_id.is_none() {
247+
// Must at least have a flink or an os_id (except in tmpfs mode where we might infer the path)
248+
if self.flink_path.is_none() && self.os_id.is_none() && !self.use_tmpfs {
188249
debug!("Open called with no file link or unique id...");
189250
return Err(ShmemError::NoLinkOrOsId);
190251
}
191252

192-
let mut flink_uid = String::new();
253+
let mut flink_content = String::new();
193254
let mut retry = 0;
255+
194256
loop {
195-
let unique_id = if let Some(ref unique_id) = self.os_id {
257+
let target_identifier = if let Some(ref unique_id) = self.os_id {
196258
retry = 5;
197-
unique_id.as_str()
198-
} else {
199-
let flink_path = self.flink_path.as_ref().unwrap();
259+
if self.use_tmpfs {
260+
// tmpfs mode: convert os_id to file path
261+
let tmpfs_path = self.get_tmpfs_file_path()?;
262+
tmpfs_path.to_string_lossy().to_string()
263+
} else {
264+
// shm_open mode: use os_id directly
265+
unique_id.clone()
266+
}
267+
} else if let Some(ref flink_path) = self.flink_path {
268+
// Read from flink file
200269
debug!(
201270
"Open shared memory from file link {}",
202271
flink_path.to_string_lossy()
203272
);
204-
let mut f = match File::open(flink_path) {
205-
Ok(f) => f,
206-
Err(e) => return Err(ShmemError::LinkOpenFailed(e)),
207-
};
208-
flink_uid.clear();
209-
if let Err(e) = f.read_to_string(&mut flink_uid) {
210-
return Err(ShmemError::LinkReadFailed(e));
211-
}
212-
flink_uid.as_str()
273+
let mut f = File::open(flink_path).map_err(ShmemError::LinkOpenFailed)?;
274+
flink_content.clear();
275+
f.read_to_string(&mut flink_content)
276+
.map_err(ShmemError::LinkReadFailed)?;
277+
flink_content.clone()
278+
} else {
279+
return Err(ShmemError::NoLinkOrOsId);
280+
};
281+
282+
let mapping_result = if self.use_tmpfs {
283+
// tmpfs mode: target_identifier is a file path
284+
os_impl::open_mapping_tmpfs(&target_identifier, self.size)
285+
} else {
286+
// shm_open mode: target_identifier is shm ID
287+
os_impl::open_mapping(&target_identifier, self.size, &self.ext)
213288
};
214289

215-
match os_impl::open_mapping(unique_id, self.size, &self.ext) {
290+
match mapping_result {
216291
Ok(m) => {
217292
self.size = m.map_size;
218293
self.owner = false;
@@ -222,8 +297,8 @@ impl ShmemConf {
222297
mapping: m,
223298
});
224299
}
225-
// If we got this failing os_id from the flink, try again in case the shmem owner didnt write the full
226-
// unique_id to the file
300+
// If we got this failing from the flink, try again in case the owner didn't write the full
301+
// identifier to the file yet
227302
Err(ShmemError::MapOpenFailed(_)) if self.os_id.is_none() && retry < 5 => {
228303
retry += 1;
229304
std::thread::sleep(std::time::Duration::from_millis(50));

0 commit comments

Comments
 (0)