Skip to content

Commit f61de71

Browse files
committed
fix(sdk): Fix a race-condition in EventCache.
This patch ensures that operations on `RoomEvents` happen in one block, by sharing the same lock. 2 new methods are created: `replace_all_events_by` and `append_new_events`.
1 parent fa5bbad commit f61de71

File tree

1 file changed

+84
-25
lines changed
  • crates/matrix-sdk/src/event_cache

1 file changed

+84
-25
lines changed

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

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use ruma::{
6262
use tokio::{
6363
sync::{
6464
broadcast::{error::RecvError, Receiver, Sender},
65-
Mutex, Notify, RwLock, RwLockReadGuard,
65+
Mutex, Notify, RwLock, RwLockReadGuard, RwLockWriteGuard,
6666
},
6767
time::timeout,
6868
};
@@ -249,13 +249,14 @@ impl EventCache {
249249
// We could have received events during a previous sync; remove them all, since
250250
// we can't know where to insert the "initial events" with respect to
251251
// them.
252-
room_cache.inner.events.write().await.reset();
252+
// let mut room_events = room_cache.inner.events.write().await;
253+
// room_events.reset();
253254

254-
let _ = room_cache.inner.sender.send(RoomEventCacheUpdate::Clear);
255+
// let _ = room_cache.inner.sender.send(RoomEventCacheUpdate::Clear);
255256

256257
room_cache
257258
.inner
258-
.append_events(
259+
.replace_all_events_by(
259260
events,
260261
prev_batch,
261262
Default::default(),
@@ -482,26 +483,30 @@ impl RoomEventCacheInner {
482483
// Ideally we'd try to reconcile existing events against those received in the
483484
// timeline, but we're not there yet. In the meanwhile, clear the
484485
// items from the room. TODO: implement Smart Matching™.
485-
trace!("limited timeline, clearing all previous events");
486-
487-
// Clear internal state (events, pagination tokens, etc.).
488-
self.events.write().await.reset();
489-
490-
// Propagate to observers.
491-
let _ = self.sender.send(RoomEventCacheUpdate::Clear);
486+
trace!("limited timeline, clearing all previous events and pushing new events");
487+
488+
self.replace_all_events_by(
489+
timeline.events,
490+
timeline.prev_batch,
491+
account_data,
492+
ephemeral,
493+
ambiguity_changes,
494+
)
495+
.await?;
496+
} else {
497+
// Add all the events to the backend.
498+
trace!("adding new events");
499+
500+
self.append_new_events(
501+
timeline.events,
502+
timeline.prev_batch,
503+
account_data,
504+
ephemeral,
505+
ambiguity_changes,
506+
)
507+
.await?;
492508
}
493509

494-
// Add all the events to the backend.
495-
trace!("adding new events");
496-
self.append_events(
497-
timeline.events,
498-
timeline.prev_batch,
499-
account_data,
500-
ephemeral,
501-
ambiguity_changes,
502-
)
503-
.await?;
504-
505510
Ok(())
506511
}
507512

@@ -511,10 +516,66 @@ impl RoomEventCacheInner {
511516
Ok(())
512517
}
513518

519+
// Remove existing events, and append a set of events to the room cache and
520+
// storage, notifying observers.
521+
async fn replace_all_events_by(
522+
&self,
523+
events: Vec<SyncTimelineEvent>,
524+
prev_batch: Option<String>,
525+
account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
526+
ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
527+
ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
528+
) -> Result<()> {
529+
// Acquire the lock.
530+
let mut room_events = self.events.write().await;
531+
532+
// Reset the events.
533+
room_events.reset();
534+
535+
// Propagate to observers.
536+
let _ = self.sender.send(RoomEventCacheUpdate::Clear);
537+
538+
// Push the new events.
539+
self.append_events_locked_impl(
540+
room_events,
541+
events,
542+
prev_batch,
543+
account_data,
544+
ephemeral,
545+
ambiguity_changes,
546+
)
547+
.await
548+
}
549+
514550
/// Append a set of events to the room cache and storage, notifying
515551
/// observers.
516-
async fn append_events(
552+
async fn append_new_events(
553+
&self,
554+
events: Vec<SyncTimelineEvent>,
555+
prev_batch: Option<String>,
556+
account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
557+
ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
558+
ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
559+
) -> Result<()> {
560+
self.append_events_locked_impl(
561+
self.events.write().await,
562+
events,
563+
prev_batch,
564+
account_data,
565+
ephemeral,
566+
ambiguity_changes,
567+
)
568+
.await
569+
}
570+
571+
/// Append a set of events, with an attached lock.
572+
///
573+
/// If the lock `room_events` is `None`, one will be created.
574+
///
575+
/// This is a private implementation. It must not be exposed publicly.
576+
async fn append_events_locked_impl(
517577
&self,
578+
mut room_events: RwLockWriteGuard<'_, RoomEvents>,
518579
events: Vec<SyncTimelineEvent>,
519580
prev_batch: Option<String>,
520581
account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
@@ -533,8 +594,6 @@ impl RoomEventCacheInner {
533594
// Add the previous back-pagination token (if present), followed by the timeline
534595
// events themselves.
535596
{
536-
let mut room_events = self.events.write().await;
537-
538597
if let Some(prev_token) = &prev_batch {
539598
room_events.push_gap(Gap { prev_token: PaginationToken(prev_token.clone()) });
540599
}

0 commit comments

Comments
 (0)