atagen

matrix-rust-sdk

public
Star0
Watch0

Commit 29016b0c0bae

tarball

atagen <boss@atagen.co>2026-10-28 03:43 UTC

2 files changed+14-20
event-cache: hand out room caches as clones, never as a guard over the room caches map

e4a8dbca made creation take the room caches write lock only to insert, but `all_caches_for_room` still returned a read guard, and sync held one for a room's whole update, store writes included. In 16 of 60 soak runs a creation that finished meanwhile never logged its insert and sync never reached its next room, which is what a writer stuck behind a held read guard looks like under tokio's fair lock, with every later reader queued behind the writer. `Caches` is a shallow clone, so callers now get one and the map is held only while it is read or written.
Modified file: crates/matrix-sdk/src/event_cache/caches/mod.rs+1 additions−1 deletions
@@ -117,7 +117,7 @@ impl CreationStep {
117117 }
118118
119119 /// A type to hold all the caches for a given room.
120-#[derive(Debug)]
120+#[derive(Clone, Debug)]
121121 pub(super) struct Caches {
122122 pub room: room::RoomEventCache,
123123 }
Modified file: crates/matrix-sdk/src/event_cache/mod.rs+13 additions−19 deletions
@@ -50,7 +50,7 @@ use matrix_sdk_base::{
5050 use matrix_sdk_common::timeout::timeout;
5151 use ruma::{OwnedRoomId, RoomId};
5252 use tokio::sync::{
53- Mutex, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock,
53+ Mutex, RwLock,
5454 broadcast::{Receiver, Sender, channel},
5555 mpsc, watch,
5656 };
@@ -416,7 +416,7 @@ impl EventCache {
416416 return Err(EventCacheError::NotSubscribedYet);
417417 };
418418
419- let room = self.inner.all_caches_for_room(room_id).await?.room.clone();
419+ let room = self.inner.all_caches_for_room(room_id).await?.room;
420420
421421 Ok((room, drop_handles))
422422 }
@@ -724,10 +724,11 @@ impl EventCacheInner {
724724 }
725725
726726 /// Return all the event caches associated to a specific room.
727- async fn all_caches_for_room(
728- &self,
729- room_id: &RoomId,
730- ) -> Result<OwnedRwLockReadGuard<CachesByRoom, Caches>> {
727+ ///
728+ /// A shallow clone rather than a guard. Sync handles a room for as long as its
729+ /// store writes take, and a guard held that long left a finished creation waiting
730+ /// to insert, with every later reader queued behind it.
731+ async fn all_caches_for_room(&self, room_id: &RoomId) -> Result<Caches> {
731732 // Fast path: the entry exists; let's acquire a read lock, it's cheaper than a
732733 // write lock.
733734 if let Some(caches) = self.loaded_caches(room_id).await {
@@ -764,20 +765,19 @@ impl EventCacheInner {
764765 return Err(err);
765766 }
766767 };
767- let mut by_room = self.by_room.clone().write_owned().await;
768+ let mut by_room = self.by_room.write().await;
768769 // a clear of every room that ran meanwhile wiped the store these caches were
769770 // loaded from
770771 if self.clears.load(Ordering::SeqCst) != clears {
771772 continue;
772773 }
773- by_room.insert(room_id.to_owned(), caches);
774- break by_room;
774+ by_room.insert(room_id.to_owned(), caches.clone());
775+ break caches;
775776 };
776777 self.creation_gates.lock().unwrap_or_else(PoisonError::into_inner).remove(room_id);
777778 debug!(%room_id, "created a room's caches");
778779
779- Ok(OwnedRwLockWriteGuard::try_downgrade_map(created, |by_room| by_room.get(room_id))
780- .expect("`Caches` has just been inserted"))
780+ Ok(created)
781781 }
782782
783783 fn drop_room_update(&self, room_id: &RoomId, err: EventCacheError) {
@@ -788,14 +788,8 @@ impl EventCacheInner {
788788 error!(%room_id, "dropping a room update, its caches are unavailable: {err}");
789789 }
790790
791- async fn loaded_caches(
792- &self,
793- room_id: &RoomId,
794- ) -> Option<OwnedRwLockReadGuard<CachesByRoom, Caches>> {
795- OwnedRwLockReadGuard::try_map(self.by_room.clone().read_owned().await, |by_room| {
796- by_room.get(room_id)
797- })
798- .ok()
791+ async fn loaded_caches(&self, room_id: &RoomId) -> Option<Caches> {
792+ self.by_room.read().await.get(room_id).cloned()
799793 }
800794
801795 /// Create a room's caches, abandoning an attempt that runs past [`CREATION_WAIT`] and