Skip to content

Commit dcf80a6

Browse files
committed
Use an atomic int instead of abusing the strong count of an arc.
1 parent ddfd4c8 commit dcf80a6

File tree

2 files changed

+32
-34
lines changed

2 files changed

+32
-34
lines changed

crates/matrix-sdk/src/event_cache/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,14 @@ impl EventCache {
331331
/// The auto-shrink mechanism works this way:
332332
///
333333
/// - Each time there's a new subscriber to a [`RoomEventCache`], it will
334-
/// get-or-create a new shared `Arc<()>`. When that subscriber is dropped,
335-
/// and the number of shared references is about to drop to 0, we notify
336-
/// this task (below) that this is the case, with the room id.
337-
/// - This task here, owned by the [`EventCacheInner`], will listen to such
338-
/// notifications that a room may be shrunk; it will look at the number of
339-
/// shared references again, and will auto-shrink if the number of shared
340-
/// references is 0.
334+
/// increment the active number of listeners to that room, aka
335+
/// [`RoomEventCacheState::listener_count`].
336+
/// - When that subscriber is dropped, it will decrement that count; and
337+
/// notify the task below if it reached 0.
338+
/// - The task spawned here, owned by the [`EventCacheInner`], will listen
339+
/// to such notifications that a room may be shrunk. It will attempt an
340+
/// auto-shrink, by letting the inner state decide whether this is a good
341+
/// time to do so (new listeners might have spawned in the meanwhile).
341342
#[instrument(skip_all)]
342343
async fn auto_shrink_linked_chunk_task(
343344
inner: Arc<EventCacheInner>,

crates/matrix-sdk/src/event_cache/room/mod.rs

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ use std::{
1818
collections::BTreeMap,
1919
fmt,
2020
ops::{Deref, DerefMut},
21-
sync::Arc,
21+
sync::{
22+
atomic::{AtomicUsize, Ordering},
23+
Arc,
24+
},
2225
};
2326

2427
use events::Gap;
@@ -75,18 +78,15 @@ pub struct RoomEventCacheListener {
7578
auto_shrink_sender: mpsc::Sender<AutoShrinkChannelPayload>,
7679

7780
/// Shared instance of the auto-shrinker.
78-
auto_shrinker: Arc<()>,
81+
listener_count: Arc<AtomicUsize>,
7982
}
8083

8184
impl Drop for RoomEventCacheListener {
8285
fn drop(&mut self) {
83-
if Arc::strong_count(&self.auto_shrinker) == 2 {
84-
// There are only two instances of the auto-shrinker: the one in the
85-
// `RoomEventCacheState`, and the one here.
86-
//
87-
// The one here is about to be dropped, such that there'll remain only the one
88-
// in the `RoomEventCacheState`; notify the auto-shrink task that it
89-
// may have to trigger for this room.
86+
let previous_listener_count = self.listener_count.fetch_sub(1, Ordering::SeqCst);
87+
if previous_listener_count == 1 {
88+
// We were the last instance of the listener; let the auto-shrinker know by
89+
// notifying it of our room id.
9090

9191
let mut room_id = self.room_id.clone();
9292

@@ -145,16 +145,17 @@ impl RoomEventCache {
145145
/// Subscribe to this room updates, after getting the initial list of
146146
/// events.
147147
pub async fn subscribe(&self) -> (Vec<TimelineEvent>, RoomEventCacheListener) {
148-
let mut state = self.inner.state.write().await;
148+
let state = self.inner.state.read().await;
149149
let events = state.events().events().map(|(_position, item)| item.clone()).collect();
150150

151-
let listener_count = state.auto_shrinker.get_or_insert_with(|| Arc::new(())).clone();
151+
state.listener_count.fetch_add(1, Ordering::SeqCst);
152+
152153
let recv = self.inner.sender.subscribe();
153154
let listener = RoomEventCacheListener {
154155
recv,
155-
auto_shrinker: listener_count,
156156
room_id: self.inner.room_id.clone(),
157157
auto_shrink_sender: self.inner.auto_shrink_sender.clone(),
158+
listener_count: state.listener_count.clone(),
158159
};
159160

160161
(events, listener)
@@ -627,7 +628,7 @@ pub(super) enum LoadMoreEventsBackwardsOutcome {
627628

628629
// Use a private module to hide `events` to this parent module.
629630
mod private {
630-
use std::sync::Arc;
631+
use std::sync::{atomic::AtomicUsize, Arc};
631632

632633
use eyeball_im::VectorDiff;
633634
use matrix_sdk_base::{
@@ -669,10 +670,8 @@ mod private {
669670
/// that upon clearing the timeline events.
670671
pub waited_for_initial_prev_token: bool,
671672

672-
/// A shared auto-shrinker, shared among all the live subscribers.
673-
///
674-
/// If set to `None`, means there are no
675-
pub(super) auto_shrinker: Option<Arc<()>>,
673+
/// An atomic count of the current number of listeners.
674+
pub(super) listener_count: Arc<AtomicUsize>,
676675
}
677676

678677
impl RoomEventCacheState {
@@ -729,7 +728,7 @@ mod private {
729728
events,
730729
deduplicator,
731730
waited_for_initial_prev_token: false,
732-
auto_shrinker: None,
731+
listener_count: Default::default(),
733732
})
734733
}
735734

@@ -911,21 +910,19 @@ mod private {
911910
Ok(Some(self.events.updates_as_vector_diffs()))
912911
}
913912

914-
/// Automatically shrink the room if there are no listeners.
913+
/// Automatically shrink the room if there are no listeners, as
914+
/// indicated by the atomic number of active listeners.
915915
#[must_use = "Updates as `VectorDiff` must probably be propagated via `RoomEventCacheUpdate`"]
916916
pub(crate) async fn auto_shrink_if_no_listeners(
917917
&mut self,
918918
) -> Result<Option<Vec<VectorDiff<TimelineEvent>>>, EventCacheError> {
919-
let mut diffs = None;
920-
if let Some(auto_shrinker) = self.auto_shrinker.as_mut() {
919+
if self.listener_count.load(std::sync::atomic::Ordering::SeqCst) == 0 {
921920
// If we are the last strong reference to the auto-shrinker, we can shrink the
922921
// events data structure to its last chunk.
923-
if Arc::strong_count(auto_shrinker) == 1 {
924-
self.auto_shrinker = None;
925-
diffs = self.shrink_to_last_chunk().await?;
926-
}
922+
self.shrink_to_last_chunk().await
923+
} else {
924+
Ok(None)
927925
}
928-
Ok(diffs)
929926
}
930927

931928
/// Removes the bundled relations from an event, if they were present.
@@ -2203,7 +2200,7 @@ mod tests {
22032200
{
22042201
// Check the inner state: there's no more shared auto-shrinker.
22052202
let state = room_event_cache.inner.state.read().await;
2206-
assert!(state.auto_shrinker.is_none());
2203+
assert_eq!(state.listener_count.load(std::sync::atomic::Ordering::SeqCst), 0);
22072204
}
22082205

22092206
// Getting the events will only give us the latest chunk.

0 commit comments

Comments
 (0)