Commit f05c136873e9
tarball5 files changed+61-5
@@ -19,7 +19,10 @@ use std::{
1919
fmt::{self, Debug},2020
future::{Future, ready},2121
pin::Pin,22
- sync::{Arc, Mutex as StdMutex, RwLock as StdRwLock, Weak},22
+ sync::{23
+ Arc, Mutex as StdMutex, RwLock as StdRwLock, Weak,24
+ atomic::{AtomicU64, Ordering},25
+ },2326
time::Duration,2427
};2528
@@ -348,6 +351,9 @@ pub(crate) struct ClientInner {
348351
/// sync response.349352
pub(crate) room_updates_sender: broadcast::Sender<RoomUpdates>,350353
354
+ /// How many [`RoomUpdates`] have been sent on `room_updates_sender`.355
+ room_updates_sent: AtomicU64,356
+351357
/// Whether the client should update its homeserver URL with the discovery352358
/// information present in the login response.353359
respect_login_well_known: bool,@@ -461,6 +467,7 @@ impl ClientInner {
461467
// A single `RoomUpdates` is sent once per sync, so we assume that 32 is sufficient462468
// ballast for all observers to catch up.463469
room_updates_sender: broadcast::Sender::new(32),470
+ room_updates_sent: AtomicU64::new(0),464471
respect_login_well_known,465472
sync_beat: event_listener::Event::new(),466473
event_cache,@@ -1281,6 +1288,16 @@ impl Client {
12811288
self.inner.room_updates_sender.subscribe()12821289
}12831290
1291
+ pub(crate) fn room_updates_sent(&self) -> u64 {1292
+ self.inner.room_updates_sent.load(Ordering::SeqCst)1293
+ }1294
+1295
+ pub(crate) fn send_room_updates(&self, rooms: RoomUpdates) {1296
+ self.inner.room_updates_sent.fetch_add(1, Ordering::SeqCst);1297
+ // Ignore errors when there are no receivers.1298
+ let _ = self.inner.room_updates_sender.send(rooms);1299
+ }1300
+12841301
pub(crate) async fn notification_handlers(12851302
&self,12861303
) -> RwLockReadGuard<'_, Vec<NotificationHandlerFn>> {@@ -31,6 +31,7 @@ use std::{
3131
collections::HashMap,3232
fmt,3333
sync::{Arc, OnceLock, RwLock as StdRwLock, RwLockReadGuard, RwLockWriteGuard},34
+ time::Duration,3435
};3536
3637
use futures_util::future::try_join_all;@@ -42,11 +43,12 @@ use matrix_sdk_base::{
4243
task_monitor::BackgroundTaskHandle,4344
timer,4445
};46
+use matrix_sdk_common::timeout::timeout;4547
use ruma::{OwnedRoomId, RoomId};4648
use tokio::sync::{4749
Mutex, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock,4850
broadcast::{Receiver, Sender, channel},49
- mpsc,51
+ mpsc, watch,5052
};5153
use tracing::{debug, error, instrument, trace};5254
@@ -226,6 +228,7 @@ impl EventCache {
226228
config: StdRwLock::new(EventCacheConfig::default()),227229
store: event_cache_store,228230
multiple_room_updates_lock: Default::default(),231
+ room_updates_handled: watch::Sender::new(0),229232
by_room: Default::default(),230233
drop_handles: Default::default(),231234
auto_shrink_sender: Default::default(),@@ -271,9 +274,11 @@ impl EventCache {
271274
let task_monitor = client.task_monitor();272275
273276
// Spawn the task that will listen to all the room updates at once.277
+ let room_updates = client.subscribe_to_all_room_updates();278
+ self.inner.room_updates_handled.send_replace(client.room_updates_sent());274279
let listen_updates_task = task_monitor.spawn_infinite_task("event_cache::room_updates_task", tasks::room_updates_task(275280
self.inner.clone(),276
- client.subscribe_to_all_room_updates(),281
+ room_updates,277282
)).abort_on_drop();278283
279284
let ignore_user_list_update_task = task_monitor.spawn_infinite_task("event_cache::ignore_user_list_update_task", tasks::ignore_user_list_update_task(@@ -358,6 +363,23 @@ impl EventCache {
358363
self.inner.handle_room_updates(updates).await359364
}360365
366
+ /// Wait until every [`RoomUpdates`] the client had sent when called has367
+ /// been handled, for at most `limit`.368
+ ///369
+ /// Returns `false` if `limit` ran out first.370
+ pub(crate) async fn wait_for_room_updates(&self, limit: Duration) -> bool {371
+ let Ok(client) = self.inner.client() else {372
+ return true;373
+ };374
+ if !self.has_subscribed() {375
+ return true;376
+ }377
+ let target = client.room_updates_sent();378
+ let mut handled = self.inner.room_updates_handled.subscribe();379
+ let waited = timeout(handled.wait_for(|handled| *handled >= target), limit).await;380
+ waited.is_ok()381
+ }382
+361383
/// Check whether [`EventCache::subscribe`] has been called.362384
pub fn has_subscribed(&self) -> bool {363385
self.inner.drop_handles.get().is_some()@@ -488,6 +510,10 @@ struct EventCacheInner {
488510
// TODO: that's the place to add a cross-process lock!489511
multiple_room_updates_lock: Mutex<()>,490512
513
+ /// How many of the client's [`RoomUpdates`] have been handled, counted in514
+ /// the client's `room_updates_sent` terms.515
+ room_updates_handled: watch::Sender<u64>,516
+491517
/// Lazily-filled cache of live [`RoomEventCache`], once per room.492518
//493519
// It's behind an `Arc` to get owned locks.@@ -67,6 +67,7 @@ pub(super) async fn room_updates_task(
6767
}6868
}6969
}70
+ inner.room_updates_handled.send_modify(|handled| *handled += 1);7071
}7172
7273
Err(RecvError::Lagged(num_skipped)) => {@@ -77,6 +78,7 @@ pub(super) async fn room_updates_task(
7778
if let Err(err) = inner.clear_all_rooms().await {7879
error!("when clearing storage after lag in listen_task: {err}");7980
}81
+ inner.room_updates_handled.send_modify(|handled| *handled += num_skipped);8082
}8183
8284
Err(RecvError::Closed) => {@@ -53,6 +53,10 @@ pub use self::{builder::*, client::VersionBuilderError, error::*, list::*};
5353
use self::{cache::restore_sliding_sync_state, client::SlidingSyncResponseProcessor};5454
use crate::{Client, Result, config::RequestConfig};5555
56
+/// How long a rooms response waits for the event cache before its `pos` is57
+/// saved anyway.58
+const ROOM_UPDATES_WAIT: Duration = Duration::from_secs(10);59
+5660
/// The Sliding Sync instance.5761
///5862
/// It is OK to clone this type as much as you need: cloning it is cheap.@@ -649,6 +653,14 @@ impl SlidingSync {
649653
.handle_response(response, &mut position_guard, requested_required_states)650654
.await?;651655
656
+ // A `pos` saved before the event cache stores this response's events657
+ // resumes past them after a kill, and no gap is left to fetch them.658
+ if this.must_process_rooms_response().await659
+ && !this.inner.client.event_cache().wait_for_room_updates(ROOM_UPDATES_WAIT).await660
+ {661
+ warn!("Saving the position before the event cache handled the response");662
+ }663
+652664
this.cache_to_storage(&position_guard).await?;653665
654666
// Release the position guard lock.@@ -188,8 +188,7 @@ impl Client {
188188
self.handle_sync_events(HandlerKind::Presence, None, presence).await?;189189
self.handle_sync_to_device_events(to_device).await?;190190
191
- // Ignore errors when there are no receivers.192
- let _ = self.inner.room_updates_sender.send(rooms.clone());191
+ self.send_room_updates(rooms.clone());193192
194193
for (room_id, room_info) in &rooms.joined {195194
let Some(room) = self.get_room(room_id) else {