Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/shm persistency on mac #25

Merged
merged 11 commits into from
Mar 5, 2025
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions commons/zenoh-shm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ winapi = { workspace = true }

[dev-dependencies]
libc = { workspace = true }

[build-dependencies]
cfg_aliases = "0.2.1"
30 changes: 30 additions & 0 deletions commons/zenoh-shm/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use cfg_aliases::cfg_aliases;

fn main() {
// these aliases should at least be included in the same aliases of Nix crate:
// ___________________
// | |
// | Nix aliases |
// | ___________ |
// | | Our | |
// | | aliases | |
// | |_________| |
// |_________________|
cfg_aliases! {
dragonfly: { target_os = "dragonfly" },
ios: { target_os = "ios" },
freebsd: { target_os = "freebsd" },
macos: { target_os = "macos" },
netbsd: { target_os = "netbsd" },
openbsd: { target_os = "openbsd" },
watchos: { target_os = "watchos" },
tvos: { target_os = "tvos" },
visionos: { target_os = "visionos" },

apple_targets: { any(ios, macos, watchos, tvos, visionos) },
bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) },
}

println!("cargo:rustc-check-cfg=cfg(apple_targets)");
println!("cargo:rustc-check-cfg=cfg(bsd)");
}
3 changes: 3 additions & 0 deletions commons/zenoh-shm/src/posix_shm/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ mod platform {
}

fn cleanup_orphaned_segments_inner() -> ZResult<()> {
#[cfg(any(bsd, target_os = "redox"))]
let shm_files = fs::read_dir(std::env::temp_dir())?;
#[cfg(not(any(bsd, target_os = "redox")))]
let shm_files = fs::read_dir("/dev/shm")?;

for segment_file in shm_files.filter_map(Result::ok).filter(|f| {
Expand Down
2 changes: 1 addition & 1 deletion commons/zenoh-shm/src/shm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl<ID: SegmentID> Segment<ID> {
}

pub fn ensure_not_persistent(id: ID) {
platform::SegmentImpl::ensure_not_persistent(id);
let _ = platform::SegmentImpl::open(id);
}
}

Expand Down
137 changes: 83 additions & 54 deletions commons/zenoh-shm/src/shm/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@
// ZettaScale Zenoh Team, <[email protected]>
//

#[cfg(any(bsd, target_os = "redox"))]
use std::os::fd::FromRawFd;
use std::{
ffi::c_void,
num::NonZeroUsize,
os::fd::{AsRawFd, OwnedFd},
ptr::NonNull,
};

// todo: flock() doesn't work on Mac in some cases, but we can fix it
#[cfg(target_os = "linux")]
use advisory_lock::{AdvisoryFileLock, FileLockMode};
#[cfg(any(bsd, target_os = "redox"))]
use nix::fcntl::open;
use nix::{
fcntl::OFlag,
sys::{
Expand All @@ -34,8 +36,7 @@ use nix::{
use super::{SegmentCreateError, SegmentID, SegmentOpenError, ShmCreateResult, ShmOpenResult};

pub struct SegmentImpl<ID: SegmentID> {
#[cfg(target_os = "linux")]
fd: OwnedFd,
lock_fd: OwnedFd,
len: NonZeroUsize,
data_ptr: NonNull<c_void>,
id: ID,
Expand All @@ -44,11 +45,28 @@ pub struct SegmentImpl<ID: SegmentID> {
// PUBLIC
impl<ID: SegmentID> SegmentImpl<ID> {
pub fn create(id: ID, len: NonZeroUsize) -> ShmCreateResult<Self> {
// we use separate lockfile on non-tmpfs for bsd
#[cfg(any(bsd, target_os = "redox"))]
let lock_fd = unsafe {
OwnedFd::from_raw_fd({
let lockpath = std::env::temp_dir().join(Self::id_str(id));
let flags = OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_RDWR;
let mode = Mode::S_IRUSR | Mode::S_IWUSR;
open(&lockpath, flags, mode).map_err(|e| match e {
nix::Error::EEXIST => SegmentCreateError::SegmentExists,
e => SegmentCreateError::OsError(e as u32),
})
}?)
};

// create unique shm fd
let fd = {
let id = Self::id_str(id);
let flags = OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_RDWR;

// todo: these flags probably can be exposed to the config
let mode = Mode::S_IRUSR | Mode::S_IWUSR;

tracing::trace!("shm_open(name={}, flag={:?}, mode={:?})", id, flags, mode);
match shm_open(id.as_str(), flags, mode) {
Ok(v) => v,
Expand All @@ -57,10 +75,18 @@ impl<ID: SegmentID> SegmentImpl<ID> {
}
};

// todo: flock() doesn't work on Mac in some cases, but we can fix it
#[cfg(target_os = "linux")]
// put shared advisory lock on shm fd
fd.as_raw_fd()
// on non-bsd we use our SHM file also for locking
#[cfg(not(any(bsd, target_os = "redox")))]
let lock_fd = fd;
#[cfg(not(any(bsd, target_os = "redox")))]
let fd = &lock_fd;

#[cfg(any(bsd, target_os = "redox"))]
let fd = &fd;

// put shared advisory lock on lock_fd
lock_fd
.as_raw_fd()
.try_lock(FileLockMode::Shared)
.map_err(|e| match e {
advisory_lock::FileLockError::AlreadyLocked => SegmentCreateError::SegmentExists,
Expand All @@ -71,7 +97,7 @@ impl<ID: SegmentID> SegmentImpl<ID> {

// resize shm segment to requested size
tracing::trace!("ftruncate(fd={}, len={})", fd.as_raw_fd(), len);
ftruncate(&fd, len.get() as _).map_err(|e| SegmentCreateError::OsError(e as u32))?;
ftruncate(fd, len.get() as _).map_err(|e| SegmentCreateError::OsError(e as u32))?;

// get real segment size
let len = {
Expand All @@ -80,31 +106,55 @@ impl<ID: SegmentID> SegmentImpl<ID> {
};

// map segment into our address space
let data_ptr = Self::map(len, &fd).map_err(|e| SegmentCreateError::OsError(e as _))?;
let data_ptr = Self::map(len, fd).map_err(|e| SegmentCreateError::OsError(e as _))?;

Ok(Self {
#[cfg(target_os = "linux")]
fd,
lock_fd,
len,
data_ptr,
id,
})
}

pub fn open(id: ID) -> ShmOpenResult<Self> {
// we use separate lockfile on non-tmpfs for bsd
#[cfg(any(bsd, target_os = "redox"))]
let lock_fd = unsafe {
OwnedFd::from_raw_fd({
let lockpath = std::env::temp_dir().join(Self::id_str(id));
let flags = OFlag::O_RDWR;
let mode = Mode::S_IRUSR | Mode::S_IWUSR;
open(&lockpath, flags, mode).map_err(|e| SegmentOpenError::OsError(e as _))
}?)
};

// open shm fd
let fd = {
let id = Self::id_str(id);
let flags = OFlag::O_RDWR;
let mode = Mode::S_IRUSR;

// todo: these flags probably can be exposed to the config
let mode = Mode::S_IRUSR | Mode::S_IWUSR;

tracing::trace!("shm_open(name={}, flag={:?}, mode={:?})", id, flags, mode);
shm_open(id.as_str(), flags, mode).map_err(|e| SegmentOpenError::OsError(e as u32))?
match shm_open(id.as_str(), flags, mode) {
Ok(v) => v,
Err(e) => return Err(SegmentOpenError::OsError(e as u32)),
}
};

// todo: flock() doesn't work on Mac in some cases, but we can fix it
#[cfg(target_os = "linux")]
// put shared advisory lock on shm fd
fd.as_raw_fd()
// on non-bsd we use our SHM file also for locking
#[cfg(not(any(bsd, target_os = "redox")))]
let lock_fd = fd;
#[cfg(not(any(bsd, target_os = "redox")))]
let fd = &lock_fd;

#[cfg(any(bsd, target_os = "redox"))]
let fd = &fd;

// put shared advisory lock on lock_fd
lock_fd
.as_raw_fd()
.try_lock(FileLockMode::Shared)
.map_err(|e| match e {
advisory_lock::FileLockError::AlreadyLocked => SegmentOpenError::InvalidatedSegment,
Expand All @@ -120,32 +170,16 @@ impl<ID: SegmentID> SegmentImpl<ID> {
};

// map segment into our address space
let data_ptr = Self::map(len, &fd).map_err(|e| SegmentOpenError::OsError(e as _))?;
let data_ptr = Self::map(len, fd).map_err(|e| SegmentOpenError::OsError(e as _))?;

Ok(Self {
#[cfg(target_os = "linux")]
fd,
lock_fd,
len,
data_ptr,
id,
})
}

pub fn ensure_not_persistent(id: ID) {
// open shm fd
let fd = {
let id = Self::id_str(id);
let flags = OFlag::O_RDWR;
let mode = Mode::S_IRUSR;
tracing::trace!("shm_open(name={}, flag={:?}, mode={:?})", id, flags, mode);
shm_open(id.as_str(), flags, mode)
};

if let Ok(fd) = fd {
Self::unlink_if_unique(id, &fd);
}
}

pub fn id(&self) -> ID {
self.id
}
Expand All @@ -162,7 +196,7 @@ impl<ID: SegmentID> SegmentImpl<ID> {
// PRIVATE
impl<ID: SegmentID> SegmentImpl<ID> {
fn id_str(id: ID) -> String {
format!("/{}.zenoh", id)
format!("{id}.zenoh")
}

fn map(len: NonZeroUsize, fd: &OwnedFd) -> nix::Result<NonNull<c_void>> {
Expand All @@ -179,34 +213,29 @@ impl<ID: SegmentID> SegmentImpl<ID> {

unsafe { mmap(None, len, prot, flags, fd, 0) }
}

#[allow(unused_variables)]
fn unlink_if_unique(id: ID, fd: &OwnedFd) {
// todo: flock() doesn't work on Mac in some cases, but we can fix it
#[cfg(target_os = "linux")]
if fd.as_raw_fd().try_lock(FileLockMode::Exclusive).is_ok() {
let id = Self::id_str(id);
tracing::trace!("shm_unlink(name={})", id);
let _ = shm_unlink(id.as_str());
}
}
}

impl<ID: SegmentID> Drop for SegmentImpl<ID> {
fn drop(&mut self) {
tracing::trace!("munmap(addr={:p},len={})", self.data_ptr, self.len);
if let Err(_e) = unsafe { munmap(self.data_ptr, self.len.get()) } {
tracing::debug!("munmap() failed : {}", _e);
if let Err(e) = unsafe { munmap(self.data_ptr, self.len.get()) } {
tracing::debug!("munmap() failed : {}", e);
};

#[cfg(target_os = "linux")]
Self::unlink_if_unique(self.id, &self.fd);

#[cfg(not(target_os = "linux"))]
if self
.lock_fd
.as_raw_fd()
.try_lock(FileLockMode::Exclusive)
.is_ok()
{
let id = Self::id_str(self.id);
tracing::trace!("shm_unlink(name={})", id);
let _ = shm_unlink(id.as_str());
#[cfg(any(bsd, target_os = "redox"))]
{
let lockpath = std::env::temp_dir().join(id);
let _ = std::fs::remove_file(lockpath);
}
}
}
}
4 changes: 1 addition & 3 deletions commons/zenoh-shm/src/shm/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ impl<ID: SegmentID> SegmentImpl<ID> {
})
}

pub fn ensure_not_persistent(_id: ID) {}

pub fn id(&self) -> ID {
self.id
}
Expand All @@ -127,7 +125,7 @@ impl<ID: SegmentID> SegmentImpl<ID> {
// PRIVATE
impl<ID: SegmentID> SegmentImpl<ID> {
fn id_str(id: ID) -> String {
format!("/{}.zenoh", id)
format!("{}.zenoh", id)
}

fn map(fd: &FileMapping) -> Result<(ViewOfFile, usize), Error> {
Expand Down
4 changes: 0 additions & 4 deletions commons/zenoh-shm/tests/shm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ fn create_and_open_amd_reopen() {
assert!(opened_segment2.len() >= len);
}

// todo: flock() doesn't work on Mac in some cases, but we can fix it
#[cfg(not(target_os = "macos"))]
#[test]
fn create_and_open_amd_reopen_and_open_closed() {
let id = line!();
Expand All @@ -70,8 +68,6 @@ fn create_and_open_amd_reopen_and_open_closed() {
assert!(opened_segment2.len() >= len);
}

// todo: flock() doesn't work on Mac in some cases, but we can fix it
#[cfg(not(target_os = "macos"))]
#[test]
fn no_persistency() {
let id = line!();
Expand Down
Loading