@@ -9,9 +9,9 @@ use std::{
99 num:: NonZeroUsize ,
1010 os:: unix:: { ffi:: OsStrExt as _, fs:: OpenOptionsExt as _, io:: IntoRawFd as _} ,
1111 path:: PathBuf ,
12+ ptr:: { self , NonNull } ,
1213} ;
1314
14- use memmap2:: { MmapOptions , MmapRaw } ;
1515use uuid:: Uuid ;
1616
1717use crate :: BACKING_PREFIX ;
@@ -39,9 +39,17 @@ pub struct ShmHandle {
3939/// A `Mapping` keeps the bytes alive until it is dropped and cannot affect the
4040/// shared memory's identifier.
4141pub struct Mapping {
42- raw : MmapRaw ,
42+ ptr : NonNull < u8 > ,
43+ len : NonZeroUsize ,
4344}
4445
46+ // SAFETY: a mapping owns no thread-affine state; access synchronization is
47+ // supplied by the fspy channel built on top of it.
48+ unsafe impl Send for Mapping { }
49+ // SAFETY: sharing a `Mapping` does not itself access its bytes, and all actual
50+ // concurrent access is synchronized by the fspy channel.
51+ unsafe impl Sync for Mapping { }
52+
4553/// Creates `size` bytes of zero-initialized shared memory.
4654///
4755/// Returns its [`ShmKeeper`] and an already opened [`ShmHandle`], so the
@@ -171,23 +179,53 @@ impl ShmHandle {
171179 ///
172180 /// Returns an error if the mapping cannot be established.
173181 pub fn map ( & self ) -> io:: Result < Mapping > {
174- let file = fspy_nostd:: AsRawFd :: as_raw_fd ( & self . file ) ;
175- Ok ( Mapping { raw : MmapOptions :: new ( ) . len ( self . size . get ( ) ) . map_raw ( file) ? } )
182+ let _slice_len = isize:: try_from ( self . size . get ( ) ) . map_err ( |_| {
183+ io:: Error :: new ( io:: ErrorKind :: InvalidData , "shared-memory size exceeds isize" )
184+ } ) ?;
185+ // SAFETY: the address is only a hint, the validated nonzero length is
186+ // representable as a Rust slice, the descriptor remains borrowed, and
187+ // the resulting shared mapping is owned by `Mapping`.
188+ let mapped = unsafe {
189+ fspy_nostd:: mm:: mmap (
190+ ptr:: null_mut ( ) ,
191+ self . size . get ( ) ,
192+ fspy_nostd:: mm:: ProtFlags :: READ | fspy_nostd:: mm:: ProtFlags :: WRITE ,
193+ fspy_nostd:: mm:: MapFlags :: SHARED ,
194+ & self . file ,
195+ 0 ,
196+ )
197+ }
198+ . map_err ( error_to_io) ?;
199+ let Some ( ptr) = NonNull :: new ( mapped. cast ( ) ) else {
200+ // Rust references cannot represent a mapping at address zero.
201+ // SAFETY: release the successful mapping before rejecting it.
202+ let _ = unsafe { fspy_nostd:: mm:: munmap ( mapped, self . size . get ( ) ) } ;
203+ return Err ( io:: Error :: other ( "mmap returned address zero" ) ) ;
204+ } ;
205+ Ok ( Mapping { ptr, len : self . size } )
206+ }
207+ }
208+
209+ impl Drop for Mapping {
210+ fn drop ( & mut self ) {
211+ // SAFETY: this is the complete mapping owned by `self`, and dropping
212+ // it proves that no safe borrow through `self` remains.
213+ let _ = unsafe { fspy_nostd:: mm:: munmap ( self . ptr . as_ptr ( ) . cast ( ) , self . len . get ( ) ) } ;
176214 }
177215}
178216
179217#[ expect( clippy:: len_without_is_empty, reason = "shared-memory mappings are always non-empty" ) ]
180218impl Mapping {
181219 /// Returns the mapped length in bytes.
182220 #[ must_use]
183- pub fn len ( & self ) -> usize {
184- self . raw . len ( )
221+ pub const fn len ( & self ) -> usize {
222+ self . len . get ( )
185223 }
186224
187225 /// Returns a raw pointer to the first mapped byte.
188226 #[ must_use]
189- pub fn as_ptr ( & self ) -> * mut u8 {
190- self . raw . as_mut_ptr ( )
227+ pub const fn as_ptr ( & self ) -> * mut u8 {
228+ self . ptr . as_ptr ( )
191229 }
192230
193231 /// Returns the mapped bytes as a shared slice.
@@ -197,7 +235,7 @@ impl Mapping {
197235 /// The caller must ensure that no process or thread mutates the mapping for
198236 /// the lifetime of the returned slice.
199237 #[ must_use]
200- pub unsafe fn as_slice ( & self ) -> & [ u8 ] {
238+ pub const unsafe fn as_slice ( & self ) -> & [ u8 ] {
201239 // SAFETY: The mapping is valid for its full length, and the caller
202240 // guarantees that it is not mutated while the slice is borrowed.
203241 unsafe { std:: slice:: from_raw_parts ( self . as_ptr ( ) . cast_const ( ) , self . len ( ) ) }
0 commit comments