matrix_sdk/event_cache/caches/
subscriber.rs1use std::{
18 ops::{Deref, DerefMut},
19 sync::{Arc, Weak},
20};
21
22use ruma::{OwnedEventId, OwnedRoomId};
23use tokio::sync::{broadcast::Receiver, mpsc};
24use tracing::{trace, warn};
25
26#[derive(Default)]
29pub struct SubscribersHandle(Arc<()>);
30
31impl SubscribersHandle {
32 pub fn count(&self) -> usize {
36 Arc::weak_count(&self.0)
37 }
38
39 pub fn new_subscriber_handle(&self) -> SubscriberHandle {
41 SubscriberHandle(Arc::downgrade(&self.0))
42 }
43}
44
45pub struct SubscriberHandle(Weak<()>);
57
58impl SubscriberHandle {
59 pub fn count(&self) -> usize {
63 Weak::weak_count(&self.0)
64 }
65}
66
67#[allow(missing_debug_implementations)]
77pub struct Subscriber<T> {
78 subscriber_receiver: Receiver<T>,
80
81 auto_shrink_message: Option<AutoShrinkMessage>,
86
87 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
89
90 subscriber_handle: Option<SubscriberHandle>,
99}
100
101impl<T> Subscriber<T> {
102 pub(super) fn new(
104 subscriber_receiver: Receiver<T>,
105 auto_shrink_message: AutoShrinkMessage,
106 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
107 subscribers_handle: &SubscribersHandle,
108 ) -> Self {
109 Self {
110 subscriber_receiver,
111 auto_shrink_message: Some(auto_shrink_message),
112 auto_shrink_sender,
113 subscriber_handle: Some(subscribers_handle.new_subscriber_handle()),
114 }
115 }
116}
117
118impl<T> Drop for Subscriber<T> {
119 fn drop(&mut self) {
120 let number_of_subscribers = self
121 .subscriber_handle
122 .take()
125 .expect("Unreachable: `subscriber_handle` must be `Some`")
126 .count();
127
128 trace!("dropping a room event cache subscriber; count: {number_of_subscribers}");
129
130 if number_of_subscribers == 1 {
131 let mut message = self
140 .auto_shrink_message
141 .take()
142 .expect("Unreachable: `auto_shrink_message` must be `Some`");
143 let mut num_attempts = 0;
144
145 while let Err(err) = self.auto_shrink_sender.try_send(message) {
146 num_attempts += 1;
147
148 if num_attempts > 1024 {
149 warn!(
152 "couldn't send notification to the auto-shrink channel \
153 after 1024 attempts; giving up"
154 );
155 return;
156 }
157
158 match err {
159 mpsc::error::TrySendError::Full(stolen_message) => {
160 message = stolen_message;
161 }
162 mpsc::error::TrySendError::Closed(_) => return,
163 }
164 }
165
166 trace!("sent notification to the parent channel that we were the last subscriber");
167 }
168 }
169}
170
171impl<T> Deref for Subscriber<T> {
172 type Target = Receiver<T>;
173
174 fn deref(&self) -> &Self::Target {
175 &self.subscriber_receiver
176 }
177}
178
179impl<T> DerefMut for Subscriber<T> {
180 fn deref_mut(&mut self) -> &mut Self::Target {
181 &mut self.subscriber_receiver
182 }
183}
184
185#[derive(Debug)]
190pub enum AutoShrinkMessage {
191 Room {
195 room_id: OwnedRoomId,
197 },
198
199 Thread {
203 room_id: OwnedRoomId,
205 thread_id: OwnedEventId,
207 },
208}
209
210#[cfg(test)]
211mod tests {
212 use assert_matches::assert_matches;
213 use ruma::owned_room_id;
214 use tokio::sync::{broadcast, mpsc};
215
216 use super::{AutoShrinkMessage, Subscriber, SubscribersHandle};
217
218 #[test]
219 fn test_subscribers_handle() {
220 let subscribers_handle = SubscribersHandle::default();
221
222 assert_eq!(subscribers_handle.count(), 0);
224
225 let handle0 = subscribers_handle.new_subscriber_handle();
227 assert_eq!(subscribers_handle.count(), 1);
228 assert_eq!(handle0.count(), 1);
229
230 let handle1 = subscribers_handle.new_subscriber_handle();
232 assert_eq!(subscribers_handle.count(), 2);
233 assert_eq!(handle0.count(), 2);
234 assert_eq!(handle1.count(), 2);
235
236 drop(handle0);
238 assert_eq!(subscribers_handle.count(), 1);
239 assert_eq!(handle1.count(), 1);
240
241 drop(handle1);
243 assert_eq!(subscribers_handle.count(), 0);
244
245 let handle2 = subscribers_handle.new_subscriber_handle();
247
248 drop(subscribers_handle);
250 assert_eq!(handle2.count(), 0);
251 }
255
256 #[test]
257 fn test_subscriber_t_derefs_to_t() {
258 let (auto_shrink_sender, _auto_shrink_receiver) = mpsc::channel(1);
259 let (subscriber_sender, subscriber_receiver) = broadcast::channel(1);
260 let subscribers_handle = SubscribersHandle::default();
261
262 let mut subscriber = Subscriber::new(
263 subscriber_receiver,
264 AutoShrinkMessage::Room { room_id: owned_room_id!("!r0") },
265 auto_shrink_sender,
266 &subscribers_handle,
267 );
268
269 subscriber_sender.send('a').unwrap();
270
271 assert_eq!(subscriber.try_recv().unwrap(), 'a');
273
274 assert!(subscriber.is_empty());
276 }
277
278 #[test]
279 fn test_subscriber_send_auto_shrink_message_on_last_drop() {
280 let (auto_shrink_sender, mut auto_shrink_receiver) = mpsc::channel(1);
281 let (_subscriber_sender, subscriber_receiver) = broadcast::channel::<()>(1);
282 let subscribers_handle = SubscribersHandle::default();
283 let room_id = owned_room_id!("!r0");
284 let auto_shrink_message = AutoShrinkMessage::Room { room_id: room_id.clone() };
285
286 let subscriber0 = Subscriber::new(
287 subscriber_receiver.resubscribe(),
288 AutoShrinkMessage::Room { room_id: room_id.clone() },
289 auto_shrink_sender.clone(),
290 &subscribers_handle,
291 );
292
293 let subscriber1 = Subscriber::new(
294 subscriber_receiver,
295 auto_shrink_message,
296 auto_shrink_sender,
297 &subscribers_handle,
298 );
299
300 drop(subscriber0);
302 assert!(auto_shrink_receiver.is_empty());
303
304 drop(subscriber1);
306 assert_matches!(
307 auto_shrink_receiver.try_recv().unwrap(),
308 AutoShrinkMessage::Room { room_id: expected_room_id } => {
309 assert_eq!(expected_room_id, room_id);
310 }
311 );
312 assert!(auto_shrink_receiver.is_empty());
313 }
314
315 #[test]
316 fn test_subscriber_send_auto_shrink_message_with_full_channel() {
317 let (auto_shrink_sender, mut auto_shrink_receiver) = mpsc::channel(1);
318 let (_subscriber_sender, subscriber_receiver) = broadcast::channel::<()>(1);
319 let subscribers_handle = SubscribersHandle::default();
320 let noisy_room_id = owned_room_id!("!r1");
321 let auto_shrink_noisy_message = AutoShrinkMessage::Room { room_id: noisy_room_id.clone() };
322 let room_id = owned_room_id!("!r0");
323 let auto_shrink_message = AutoShrinkMessage::Room { room_id };
324
325 auto_shrink_sender.try_send(auto_shrink_noisy_message).unwrap();
327
328 let subscriber = Subscriber::new(
329 subscriber_receiver,
330 auto_shrink_message,
331 auto_shrink_sender,
332 &subscribers_handle,
333 );
334
335 drop(subscriber);
339
340 assert_matches!(
343 auto_shrink_receiver.try_recv().unwrap(),
344 AutoShrinkMessage::Room { room_id: expected_room_id } => {
345 assert_eq!(expected_room_id, noisy_room_id);
346 }
347 );
348
349 assert!(auto_shrink_receiver.is_empty());
351 }
352
353 #[test]
354 fn test_subscriber_send_auto_shrink_message_with_closed_channel() {
355 let (auto_shrink_sender, auto_shrink_receiver) = mpsc::channel(1);
356 let (_subscriber_sender, subscriber_receiver) = broadcast::channel::<()>(1);
357 let subscribers_handle = SubscribersHandle::default();
358 let room_id = owned_room_id!("!r0");
359 let auto_shrink_message = AutoShrinkMessage::Room { room_id };
360
361 let subscriber = Subscriber::new(
362 subscriber_receiver,
363 auto_shrink_message,
364 auto_shrink_sender,
365 &subscribers_handle,
366 );
367
368 drop(auto_shrink_receiver);
370
371 drop(subscriber);
375
376 }
379}