1
+ use ahash:: HashMap ;
2
+ use itertools:: Itertools as _;
1
3
use parking_lot:: RwLock ;
4
+ use re_log_types:: StoreId ;
2
5
3
6
use crate :: { ChunkStore , ChunkStoreEvent } ;
4
7
@@ -57,6 +60,20 @@ pub trait ChunkStoreSubscriber: std::any::Any + Send + Sync {
57
60
fn on_events ( & mut self , events : & [ ChunkStoreEvent ] ) ;
58
61
}
59
62
63
+ /// A [`ChunkStoreSubscriber`] that is instantiated for each unique [`StoreId`].
64
+ pub trait PerStoreChunkSubscriber : Send + Sync + Default {
65
+ /// Arbitrary name for the subscriber.
66
+ ///
67
+ /// Does not need to be unique.
68
+ fn name ( ) -> String ;
69
+
70
+ /// Get notified of changes happening in a [`ChunkStore`], see [`ChunkStoreSubscriber::on_events`].
71
+ ///
72
+ /// Unlike [`ChunkStoreSubscriber::on_events`], all items are guaranteed to have the same [`StoreId`]
73
+ /// which does not change per invocation.
74
+ fn on_events < ' a > ( & mut self , events : impl Iterator < Item = & ' a ChunkStoreEvent > ) ;
75
+ }
76
+
60
77
/// All registered [`ChunkStoreSubscriber`]s.
61
78
static SUBSCRIBERS : once_cell:: sync:: Lazy < RwLock < Vec < SharedStoreSubscriber > > > =
62
79
once_cell:: sync:: Lazy :: new ( || RwLock :: new ( Vec :: new ( ) ) ) ;
@@ -143,6 +160,79 @@ impl ChunkStore {
143
160
} )
144
161
}
145
162
163
+ /// Registers a [`PerStoreChunkSubscriber`] type so it gets automatically notified when data gets added and/or
164
+ /// removed to/from a [`ChunkStore`].
165
+ pub fn register_per_store_subscriber < S : PerStoreChunkSubscriber + Default + ' static > (
166
+ ) -> ChunkStoreSubscriberHandle {
167
+ let mut subscribers = SUBSCRIBERS . write ( ) ;
168
+ subscribers. push ( RwLock :: new ( Box :: new (
169
+ PerStoreStoreSubscriberWrapper :: < S > :: default ( ) ,
170
+ ) ) ) ;
171
+ ChunkStoreSubscriberHandle ( subscribers. len ( ) as u32 - 1 )
172
+ }
173
+
174
+ /// Passes a reference to the downcasted per-store subscriber to the given `FnMut` callback.
175
+ ///
176
+ /// Returns `None` if the subscriber doesn't exist or downcasting failed.
177
+ pub fn with_per_store_subscriber < S : PerStoreChunkSubscriber + ' static , T , F : FnMut ( & S ) -> T > (
178
+ ChunkStoreSubscriberHandle ( handle) : ChunkStoreSubscriberHandle ,
179
+ store_id : & StoreId ,
180
+ mut f : F ,
181
+ ) -> Option < T > {
182
+ let subscribers = SUBSCRIBERS . read ( ) ;
183
+ subscribers. get ( handle as usize ) . and_then ( |subscriber| {
184
+ let subscriber = subscriber. read ( ) ;
185
+ subscriber
186
+ . as_any ( )
187
+ . downcast_ref :: < PerStoreStoreSubscriberWrapper < S > > ( )
188
+ . and_then ( |wrapper| wrapper. get ( store_id) . map ( & mut f) )
189
+ } )
190
+ }
191
+
192
+ /// Passes a reference to the downcasted per-store subscriber to the given `FnOnce` callback.
193
+ ///
194
+ /// Returns `None` if the subscriber doesn't exist or downcasting failed.
195
+ pub fn with_per_store_subscriber_once <
196
+ S : PerStoreChunkSubscriber + ' static ,
197
+ T ,
198
+ F : FnOnce ( & S ) -> T ,
199
+ > (
200
+ ChunkStoreSubscriberHandle ( handle) : ChunkStoreSubscriberHandle ,
201
+ store_id : & StoreId ,
202
+ f : F ,
203
+ ) -> Option < T > {
204
+ let subscribers = SUBSCRIBERS . read ( ) ;
205
+ subscribers. get ( handle as usize ) . and_then ( |subscriber| {
206
+ let subscriber = subscriber. read ( ) ;
207
+ subscriber
208
+ . as_any ( )
209
+ . downcast_ref :: < PerStoreStoreSubscriberWrapper < S > > ( )
210
+ . and_then ( |wrapper| wrapper. get ( store_id) . map ( f) )
211
+ } )
212
+ }
213
+
214
+ /// Passes a mutable reference to the downcasted per-store subscriber to the given callback.
215
+ ///
216
+ /// Returns `None` if the subscriber doesn't exist or downcasting failed.
217
+ pub fn with_per_store_subscriber_mut <
218
+ S : PerStoreChunkSubscriber + ' static ,
219
+ T ,
220
+ F : FnMut ( & mut S ) -> T ,
221
+ > (
222
+ ChunkStoreSubscriberHandle ( handle) : ChunkStoreSubscriberHandle ,
223
+ store_id : & StoreId ,
224
+ mut f : F ,
225
+ ) -> Option < T > {
226
+ let subscribers = SUBSCRIBERS . read ( ) ;
227
+ subscribers. get ( handle as usize ) . and_then ( |subscriber| {
228
+ let mut subscriber = subscriber. write ( ) ;
229
+ subscriber
230
+ . as_any_mut ( )
231
+ . downcast_mut :: < PerStoreStoreSubscriberWrapper < S > > ( )
232
+ . and_then ( |wrapper| wrapper. get_mut ( store_id) . map ( & mut f) )
233
+ } )
234
+ }
235
+
146
236
/// Called by [`ChunkStore`]'s mutating methods to notify subscriber subscribers of upcoming events.
147
237
pub ( crate ) fn on_events ( events : & [ ChunkStoreEvent ] ) {
148
238
re_tracing:: profile_function!( ) ;
@@ -154,6 +244,47 @@ impl ChunkStore {
154
244
}
155
245
}
156
246
247
+ /// Utility that makes a [`PerStoreChunkSubscriber`] a [`ChunkStoreSubscriber`].
248
+ #[ derive( Default ) ]
249
+ struct PerStoreStoreSubscriberWrapper < S : PerStoreChunkSubscriber > {
250
+ subscribers : HashMap < StoreId , Box < S > > ,
251
+ }
252
+
253
+ impl < S : PerStoreChunkSubscriber + ' static > PerStoreStoreSubscriberWrapper < S > {
254
+ fn get ( & self , store_id : & StoreId ) -> Option < & S > {
255
+ self . subscribers . get ( store_id) . map ( |s| s. as_ref ( ) )
256
+ }
257
+
258
+ fn get_mut ( & mut self , store_id : & StoreId ) -> Option < & mut S > {
259
+ self . subscribers . get_mut ( store_id) . map ( |s| s. as_mut ( ) )
260
+ }
261
+ }
262
+
263
+ impl < S : PerStoreChunkSubscriber + ' static > ChunkStoreSubscriber
264
+ for PerStoreStoreSubscriberWrapper < S >
265
+ {
266
+ fn name ( & self ) -> String {
267
+ S :: name ( )
268
+ }
269
+
270
+ fn as_any ( & self ) -> & dyn std:: any:: Any {
271
+ self
272
+ }
273
+
274
+ fn as_any_mut ( & mut self ) -> & mut dyn std:: any:: Any {
275
+ self
276
+ }
277
+
278
+ fn on_events ( & mut self , events : & [ ChunkStoreEvent ] ) {
279
+ for ( store_id, events) in & events. iter ( ) . chunk_by ( |e| e. store_id . clone ( ) ) {
280
+ self . subscribers
281
+ . entry ( store_id)
282
+ . or_default ( )
283
+ . on_events ( events) ;
284
+ }
285
+ }
286
+ }
287
+
157
288
#[ cfg( test) ]
158
289
mod tests {
159
290
use std:: sync:: Arc ;
0 commit comments