44//! and poll-based notifications built on `mio` and `eventfd`.
55
66use std:: collections:: HashMap ;
7- use std:: os:: fd:: { AsRawFd , FromRawFd , OwnedFd } ;
8- use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
7+ use std:: mem:: MaybeUninit ;
8+ use std:: os:: fd:: { AsRawFd , FromRawFd , OwnedFd , RawFd } ;
9+ use std:: sync:: atomic:: { AtomicU32 , AtomicUsize , Ordering } ;
910use std:: sync:: { Arc , LazyLock , Mutex , RwLock } ;
1011use std:: time:: Duration ;
1112
@@ -15,6 +16,57 @@ use mio::{Events, Poll, Token};
1516pub trait MorbDataType : Send + Sync + ' static + Clone { }
1617impl < T > MorbDataType for T where T : Send + Sync + ' static + Clone { }
1718
19+ struct RingBuffer < T > {
20+ slots : Box < [ MaybeUninit < T > ] > ,
21+ initialized : Box < [ bool ] > ,
22+ }
23+
24+ impl < T > RingBuffer < T > {
25+ fn new ( size : usize ) -> Self {
26+ let mut slots = Vec :: with_capacity ( size) ;
27+ slots. resize_with ( size, MaybeUninit :: uninit) ;
28+
29+ Self {
30+ slots : slots. into_boxed_slice ( ) ,
31+ initialized : vec ! [ false ; size] . into_boxed_slice ( ) ,
32+ }
33+ }
34+
35+ fn write ( & mut self , index : usize , value : T ) {
36+ if self . initialized [ index] {
37+ unsafe {
38+ self . slots [ index] . assume_init_drop ( ) ;
39+ }
40+ }
41+
42+ self . slots [ index] . write ( value) ;
43+ self . initialized [ index] = true ;
44+ }
45+
46+ fn read_cloned ( & self , index : usize ) -> Option < T >
47+ where
48+ T : Clone ,
49+ {
50+ if !self . initialized [ index] {
51+ return None ;
52+ }
53+
54+ Some ( unsafe { self . slots [ index] . assume_init_ref ( ) . clone ( ) } )
55+ }
56+ }
57+
58+ impl < T > Drop for RingBuffer < T > {
59+ fn drop ( & mut self ) {
60+ for ( index, initialized) in self . initialized . iter ( ) . copied ( ) . enumerate ( ) {
61+ if initialized {
62+ unsafe {
63+ self . slots [ index] . assume_init_drop ( ) ;
64+ }
65+ }
66+ }
67+ }
68+ }
69+
1870/// Stores all globally registered topics.
1971pub struct TopicManager {
2072 topics : HashMap < String , Box < dyn std:: any:: Any + Send + Sync > > ,
@@ -92,7 +144,7 @@ impl<T: MorbDataType> Publisher<T> {
92144 let mut fifo = self . topic . fifo . lock ( ) . unwrap ( ) ;
93145 let index = self . topic . generation . load ( Ordering :: Acquire ) as usize
94146 % ( self . topic . queue_size as usize ) ;
95- fifo[ index] = Some ( data) ;
147+ fifo. write ( index, data) ;
96148 self . topic . generation . fetch_add ( 1 , Ordering :: AcqRel ) ;
97149 }
98150 self . topic . notify ( ) ;
@@ -128,14 +180,20 @@ impl<T: MorbDataType> Subscriber<T> {
128180 }
129181 let index = ( self . sub_generation as usize ) % ( self . topic . queue_size as usize ) ;
130182 self . sub_generation += 1 ;
131- self . topic . fifo . lock ( ) . unwrap ( ) [ index ] . clone ( )
183+ self . topic . fifo . lock ( ) . unwrap ( ) . read_cloned ( index )
132184 }
133185}
134186
135187/// Waits for updates from one or more topics.
136188pub struct TopicPoller {
137189 poll : Poll ,
138190 events : Events ,
191+ registrations : Vec < TopicPollerRegistration > ,
192+ }
193+
194+ struct TopicPollerRegistration {
195+ eventfd : RawFd ,
196+ poller_count : Arc < AtomicUsize > ,
139197}
140198
141199impl Default for TopicPoller {
@@ -150,6 +208,7 @@ impl TopicPoller {
150208 Self {
151209 poll : Poll :: new ( ) . unwrap ( ) ,
152210 events : Events :: with_capacity ( 1024 ) ,
211+ registrations : Vec :: new ( ) ,
153212 }
154213 }
155214
@@ -159,12 +218,31 @@ impl TopicPoller {
159218 self . poll . registry ( ) ,
160219 topic. token ,
161220 mio:: Interest :: READABLE ,
162- )
221+ ) ?;
222+
223+ topic. poller_count . fetch_add ( 1 , Ordering :: Relaxed ) ;
224+ self . registrations . push ( TopicPollerRegistration {
225+ eventfd : topic. eventfd . as_raw_fd ( ) ,
226+ poller_count : Arc :: clone ( & topic. poller_count ) ,
227+ } ) ;
228+
229+ Ok ( ( ) )
163230 }
164231
165232 /// Removes a topic from the poller.
166233 pub fn remove_topic < T : MorbDataType > ( & mut self , topic : & Topic < T > ) -> std:: io:: Result < ( ) > {
167- mio:: unix:: SourceFd ( & topic. eventfd . as_raw_fd ( ) ) . deregister ( self . poll . registry ( ) )
234+ mio:: unix:: SourceFd ( & topic. eventfd . as_raw_fd ( ) ) . deregister ( self . poll . registry ( ) ) ?;
235+
236+ if let Some ( index) = self
237+ . registrations
238+ . iter ( )
239+ . position ( |registration| registration. eventfd == topic. eventfd . as_raw_fd ( ) )
240+ {
241+ let registration = self . registrations . swap_remove ( index) ;
242+ registration. poller_count . fetch_sub ( 1 , Ordering :: Relaxed ) ;
243+ }
244+
245+ Ok ( ( ) )
168246 }
169247
170248 /// Waits until at least one registered topic becomes readable or the timeout expires.
@@ -178,14 +256,23 @@ impl TopicPoller {
178256 }
179257}
180258
259+ impl Drop for TopicPoller {
260+ fn drop ( & mut self ) {
261+ for registration in self . registrations . drain ( ..) {
262+ registration. poller_count . fetch_sub ( 1 , Ordering :: Relaxed ) ;
263+ }
264+ }
265+ }
266+
181267/// A named message channel with fixed-size retention and poll notifications.
182268pub struct Topic < T : MorbDataType > {
183269 name : String ,
184- fifo : Mutex < Vec < Option < T > > > ,
270+ fifo : Mutex < RingBuffer < T > > ,
185271 pub ( crate ) generation : AtomicU32 ,
186272 queue_size : u16 ,
187273 token : mio:: Token ,
188274 eventfd : OwnedFd ,
275+ poller_count : Arc < AtomicUsize > ,
189276}
190277
191278impl < T : MorbDataType > Topic < T > {
@@ -194,15 +281,20 @@ impl<T: MorbDataType> Topic<T> {
194281
195282 Self {
196283 name,
197- fifo : Mutex :: new ( vec ! [ None ; queue_size as usize ] ) ,
284+ fifo : Mutex :: new ( RingBuffer :: new ( queue_size as usize ) ) ,
198285 generation : AtomicU32 :: new ( 0 ) ,
199286 queue_size,
200287 token : Token ( topic_id) ,
201288 eventfd : unsafe { OwnedFd :: from_raw_fd ( libc:: eventfd ( 0 , libc:: EFD_NONBLOCK ) ) } ,
289+ poller_count : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
202290 }
203291 }
204292
205293 fn notify ( & self ) {
294+ if self . poller_count . load ( Ordering :: Relaxed ) == 0 {
295+ return ;
296+ }
297+
206298 let value = usize:: from ( self . token ) as u64 ;
207299 unsafe {
208300 libc:: write (
0 commit comments