diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..93b9832 --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,87 @@ +//! Extensions +use super::*; +use std::{ + ops, + borrow::{ + Borrow, BorrowMut + }, +}; + +/// Defer an expression call +macro_rules! defer { + (move => $expr:expr) => { + $crate::ext::Deferred(move || { + $expr + }) + }; + (ref => $expr:expr) => { + $crate::ext::Deferred(|| { + $expr + }) + }; + (move $value:expr => $expr:expr) => { + $crate::ext::DeferredDrop($value, move |a| { + $expr(a) + }) + }; + (ref $value:expr => $expr:expr) => { + $crate::ext::DeferredDrop($value, |a| { + $expr(a) + }) + }; +} +pub(crate) use defer; + +/// Defer calling `F` until the destructor is ran +pub struct Deferred ()>(F); + +/// Defer dropping this value until the container is dropped. The function `F` will be called on the value at drop time. +pub struct DeferredDrop ()>(T,F); + +impl ()> ops::Drop for Deferred +{ + #[inline] + fn drop(&mut self) { + self.0(); + } +} + +impl ()> ops::Drop for DeferredDrop +{ + #[inline] + fn drop(&mut self) { + self.1(self.0); + } +} + +impl ()> ops::DerefMut for DeferredDrop +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl ()> ops::Deref for DeferredDrop +{ + type Target = T; + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ()> Borrow for DeferredDrop +{ + #[inline(always)] + fn borrow(&self) -> &T { + &self.0 + } +} + +impl ()> BorrowMut for DeferredDrop +{ + #[inline(always)] + fn borrow_mut(&mut self) -> &mut T { + &mut self.0 + } +} diff --git a/src/lib.rs b/src/lib.rs index 9d89ae3..4971a2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,10 +25,16 @@ use std::{ mod ffi; use ffi::c_try; + pub mod hugetlb; #[cfg(feature="file")] pub mod file; +pub mod ring; //TODO +use ring::buffer; + +mod ext; use ext::*; + use hugetlb::{ HugePage, MapHugeFlag, @@ -69,6 +75,12 @@ pub struct MappedFile file: T, map: MappedSlice, } +#[inline(never)] +#[cold] +fn _panic_invalid_address() -> ! +{ + panic!("Invalid/unsupported address returned from mmap()") +} impl MappedFile { /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and mapping flags provided by `flags`. @@ -83,12 +95,7 @@ impl MappedFile { /// If `mmap()` succeeds, but returns an invalid address (e.g. 0) pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result> { - #[inline(never)] - #[cold] - fn _panic_invalid_address() -> ! - { - panic!("Invalid/unsupported address returned from mmap()") - } + const NULL: *mut libc::c_void = ptr::null_mut(); let fd = file.as_raw_fd(); let slice = match unsafe { @@ -111,6 +118,76 @@ impl MappedFile { map: MappedSlice(slice) }) } + + /// Returns a dual mapping `(tx, rx)`, into the same file. + /// + /// # Note + /// `len` **must** be a multiple of the used page size (or hugepage size, if `flags` is set to use one) for this to work. + pub fn try_new_buffer>(file: T, len: usize, rings: impl Into>, allow_unsafe_writes: bool, flags: impl flags::MapFlags) -> Result<(MappedFile, MappedFile), TryNewError> + { + const NULL: *mut libc::c_void = ptr::null_mut(); + use std::{ + //rc::Rc, + cell::RefCell, + }; + + let mut defer_set = { + let mut defer_set: Vec<(*mut u8, usize)> = Vec::new(); + defer!(ref defer_set => |set: Vec<(*mut u8, usize)>| { + for (ptr, len) in set { + unsafe { + libc::munmap(ptr as *mut _, len); + } + } + }); + defer_set + }; + + macro_rules! try_map { + ($($tt:tt)*) => { + match unsafe { + mmap($($tt)*) + } { + MAP_FAILED => return Err(TryNewError::wrap_last_error(file)), + NULL => _panic_invalid_address(), + ptr => unsafe { + defer_set.push((ptr as *mut u8, len)); + + UniqueSlice { + mem: NonNull::new_unchecked(ptr as *mut u8), + end: match NonNull::new((ptr as *mut u8).add(len)) { + Some(n) => n, + _ => _panic_invalid_address(), + } + } + } + } + }; + } + let (prot_r, prot_w) = if allow_unsafe_writes { + let p = Perm::ReadWrite.get_prot(); + (p, p) + } else { + (Perm::Writeonly.get_prot(), Perm::Readonly.get_prot()) + }; + match rings.into() { + None => { + // No rings, just create two mappings at same addr. + let flags = flags.get_mmap_flags(); + let tm = try_map!(NULL, len, prot_w, flags, file.as_raw_fd(), 0); + let wm = try_map!(tm.mem.as_ptr() as *mut _, len, prot_r, flags | libc::MAP_FIXED, file.as_raw_fd(), 0); + //TODO... How to create the first `A?rc` over `file`? + }, + Some(pages) => { + // Create anon mapping + try_map!(NULL, len, libc::PROT_NONE, flags.get_mmap_flags() | libc::MAP_ANONYMOUS, file.as_raw_fd(), 0); + } + + } + + // Must happen at the end, so that maps dont get defer free'd + defer_set.clear(); + } /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and /// mapping flags provided by `flags`. @@ -140,8 +217,8 @@ impl MappedFile { match unsafe { msync(self.map.0.as_mut_ptr() as *mut _, self.map.0.len(), flush.get_ms()) } { - 0 => Ok(()), - _ => Err(io::Error::last_os_error()) + 0 => Ok(()), + _ => Err(io::Error::last_os_error()) } } @@ -188,14 +265,14 @@ impl MappedFile { pub fn advise(&mut self, adv: Advice, needed: Option) -> io::Result<()> { use libc::{ - madvise, - MADV_WILLNEED, - MADV_DONTNEED + madvise, + MADV_WILLNEED, + MADV_DONTNEED }; let (addr, len) = self.raw_parts(); match unsafe { madvise(addr as *mut _, len, adv.get_madv() | needed.map(|n| n.then(|| MADV_WILLNEED).unwrap_or(MADV_DONTNEED)).unwrap_or(0)) } { - 0 => Ok(()), - _ => Err(io::Error::last_os_error()) + 0 => Ok(()), + _ => Err(io::Error::last_os_error()) } } @@ -207,7 +284,7 @@ impl MappedFile { pub fn try_with_advice(mut self, adv: Advice, needed: Option) -> Result> { match self.advise(adv, needed) { - Ok(_) => Ok(self), + Ok(_) => Ok(self), Err(error) => Err(TryNewError { error: Box::new(error), value: self, @@ -237,8 +314,8 @@ impl MappedFile { { let MappedFile{ file, map } = self; (MappedFile { - file: other, - map + file: other, + map }, file) } diff --git a/src/ring.rs b/src/ring.rs new file mode 100644 index 0000000..f54a85c --- /dev/null +++ b/src/ring.rs @@ -0,0 +1,6 @@ +//! `mmap()` based (regular or huge) page-sized ring-buffers over arbitrary files +use super::*; + +//TODO: Implement this w/ MAP_FIXED + +pub mod buffer; diff --git a/src/ring/buffer.rs b/src/ring/buffer.rs new file mode 100644 index 0000000..15ff6b1 --- /dev/null +++ b/src/ring/buffer.rs @@ -0,0 +1,78 @@ +//! Traits and types used for mapping a R^W send-recv buffer `(tx, rx)` +//! +//! See `MappedFile::try_new_buffer()` +use super::*; +use std::{ + borrow::Borrow, + ops, + sync, + rc, +}; + +pub trait TwoBufferProvider +{ + type ControlWrapper: Borrow; + + fn as_wrapper(&self) -> &Self::ControlWrapper; + + #[inline(always)] + fn inner(&self) -> &T + { + self.as_wrapper().borrow() + } + + fn from_boxed(value: Box) -> Box; +//TODO: How do we give enough info to caller to create this? +} + +/// For thread-sharable buffer holds +#[derive(Debug, Clone)] +pub struct Shared(sync::Arc); + +/// For non thread-sharable buffer holds +#[derive(Debug, Clone)] +pub struct Private(rc::Rc); + +impl TwoBufferProvider for Shared { + type ControlWrapper = sync::Arc; + + #[inline(always)] + fn as_wrapper(&self) -> &Self::ControlWrapper { + &self.0 + } + + #[inline] + fn from_boxed(value: Box) ->Box { + Box::new(Self(value.into())) + } +} + +impl AsRawFd for Shared +{ + #[inline(always)] + fn as_raw_fd(&self) -> RawFd { + self.as_wrapper().as_raw_fd() + } +} + +impl TwoBufferProvider for Private { + type ControlWrapper = rc::Rc; + + #[inline(always)] + fn as_wrapper(&self) -> &Self::ControlWrapper { + &self.0 + } + + #[inline] + fn from_boxed(value: Box) ->Box { + Box::new(Self(value.into())) + } +} + +impl AsRawFd for Private +{ + #[inline(always)] + fn as_raw_fd(&self) -> RawFd { + self.as_wrapper().as_raw_fd() + } +}