11//! Behavior-neutral shared-memory facade for fspy channels.
22
3- use std:: io ;
3+ use std:: { future :: Future , io } ;
44
55use shared_memory:: { Shmem , ShmemConf } ;
66
@@ -9,18 +9,85 @@ pub struct Shm {
99 inner : Shmem ,
1010}
1111
12+ /// A newly created shared-memory mapping and its platform service.
13+ pub struct CreatedShm {
14+ /// The owned shared-memory mapping.
15+ pub shm : Shm ,
16+ /// The service that makes this mapping available to other processes.
17+ #[ cfg( target_os = "linux" ) ]
18+ pub broker : ShmBroker ,
19+ }
20+
21+ /// The unstarted service for a Linux shared-memory mapping.
22+ ///
23+ /// The current `shared_memory` adapter needs no broker. This no-op type reserves
24+ /// the task-scoped lifecycle used by the native Linux implementation.
25+ #[ cfg( target_os = "linux" ) ]
26+ pub struct ShmBroker {
27+ _private : ( ) ,
28+ }
29+
30+ #[ cfg( target_os = "linux" ) ]
31+ impl ShmBroker {
32+ /// Starts serving this mapping until the returned handle is stopped or dropped.
33+ pub const fn start ( self ) -> ShmBrokerHandle {
34+ let Self { _private : ( ) } = self ;
35+ ShmBrokerHandle { stopped : false }
36+ }
37+ }
38+
39+ /// A running Linux shared-memory service.
40+ ///
41+ /// Dropping this handle stops the service as a failure-path backstop. Call
42+ /// [`Self::stop`] to perform an orderly shutdown and observe shutdown errors.
43+ #[ cfg( target_os = "linux" ) ]
44+ #[ must_use = "dropping the handle stops the shared-memory service" ]
45+ pub struct ShmBrokerHandle {
46+ stopped : bool ,
47+ }
48+
49+ #[ cfg( target_os = "linux" ) ]
50+ impl ShmBrokerHandle {
51+ /// Stops the service and waits for it to finish.
52+ ///
53+ /// # Errors
54+ ///
55+ /// Returns an error if the service cannot shut down cleanly.
56+ pub fn stop ( mut self ) -> impl Future < Output = io:: Result < ( ) > > {
57+ self . stop_inner ( ) ;
58+ std:: future:: ready ( Ok ( ( ) ) )
59+ }
60+
61+ const fn stop_inner ( & mut self ) {
62+ if !self . stopped {
63+ self . stopped = true ;
64+ }
65+ }
66+ }
67+
68+ #[ cfg( target_os = "linux" ) ]
69+ impl Drop for ShmBrokerHandle {
70+ fn drop ( & mut self ) {
71+ self . stop_inner ( ) ;
72+ }
73+ }
74+
1275/// Creates a shared-memory mapping of `size` bytes.
1376///
1477/// # Errors
1578///
1679/// Returns an error if the platform cannot create or map the region.
17- pub fn create ( size : usize ) -> io:: Result < Shm > {
80+ pub fn create ( size : usize ) -> io:: Result < CreatedShm > {
1881 let conf = ShmemConf :: new ( ) . size ( size) ;
1982 #[ cfg( target_os = "windows" ) ]
2083 let conf = conf. allow_raw ( true ) ;
2184
2285 let inner = conf. create ( ) . map_err ( io:: Error :: other) ?;
23- Ok ( Shm { inner } )
86+ Ok ( CreatedShm {
87+ shm : Shm { inner } ,
88+ #[ cfg( target_os = "linux" ) ]
89+ broker : ShmBroker { _private : ( ) } ,
90+ } )
2491}
2592
2693/// Opens the shared-memory mapping identified by `id`.
@@ -83,7 +150,7 @@ mod tests {
83150
84151 #[ test]
85152 fn create_and_open_are_shared ( ) {
86- let owner = create ( SIZE ) . unwrap ( ) ;
153+ let owner = create ( SIZE ) . unwrap ( ) . shm ;
87154 assert_eq ! ( owner. len( ) , SIZE ) ;
88155 assert_eq ! ( owner. as_ptr( ) as usize % align_of:: <usize >( ) , 0 ) ;
89156 // SAFETY: No writes occur while this slice is borrowed.
@@ -101,7 +168,7 @@ mod tests {
101168
102169 #[ test]
103170 fn mapping_is_visible_across_processes ( ) {
104- let owner = create ( SIZE ) . unwrap ( ) ;
171+ let owner = create ( SIZE ) . unwrap ( ) . shm ;
105172 write_byte ( & owner, 0 , 17 ) ;
106173
107174 let command = command_for_fn ! ( owner. id( ) . to_owned( ) , |id: String | {
@@ -115,7 +182,7 @@ mod tests {
115182
116183 #[ test]
117184 fn owner_drop_prevents_new_opens ( ) {
118- let owner = create ( SIZE ) . unwrap ( ) ;
185+ let owner = create ( SIZE ) . unwrap ( ) . shm ;
119186 let id = owner. id ( ) . to_owned ( ) ;
120187 drop ( owner) ;
121188
@@ -124,7 +191,7 @@ mod tests {
124191
125192 #[ test]
126193 fn opened_mapping_survives_owner_drop ( ) {
127- let owner = create ( SIZE ) . unwrap ( ) ;
194+ let owner = create ( SIZE ) . unwrap ( ) . shm ;
128195 let id = owner. id ( ) . to_owned ( ) ;
129196 let opened = open ( & id, SIZE ) . unwrap ( ) ;
130197 write_byte ( & owner, 0 , 17 ) ;
@@ -136,6 +203,13 @@ mod tests {
136203 assert_eq ! ( read_byte( & opened, SIZE - 1 ) , 29 ) ;
137204 }
138205
206+ #[ cfg( target_os = "linux" ) ]
207+ #[ test]
208+ fn broker_handle_drop_is_safe ( ) {
209+ let created = create ( SIZE ) . unwrap ( ) ;
210+ drop ( created. broker . start ( ) ) ;
211+ }
212+
139213 fn read_byte ( shm : & Shm , index : usize ) -> u8 {
140214 assert ! ( index < shm. len( ) ) ;
141215 // SAFETY: The index is in bounds and tests synchronize all accesses.
0 commit comments