28
28
#![ forbid( missing_docs) ]
29
29
30
30
use std:: {
31
- collections:: BTreeMap ,
31
+ collections:: { BTreeMap , HashMap } ,
32
32
fmt,
33
33
sync:: { Arc , OnceLock } ,
34
34
} ;
@@ -44,6 +44,7 @@ use matrix_sdk_base::{
44
44
} ,
45
45
executor:: AbortOnDrop ,
46
46
linked_chunk:: { self , lazy_loader:: LazyLoaderError , OwnedLinkedChunkId } ,
47
+ store:: SerializableEventContent ,
47
48
store_locks:: LockStoreError ,
48
49
sync:: RoomUpdates ,
49
50
timer,
@@ -52,7 +53,11 @@ use matrix_sdk_common::executor::{spawn, JoinHandle};
52
53
#[ cfg( feature = "experimental-search" ) ]
53
54
use matrix_sdk_search:: error:: IndexError ;
54
55
use room:: RoomEventCacheState ;
55
- use ruma:: { events:: AnySyncEphemeralRoomEvent , serde:: Raw , OwnedEventId , OwnedRoomId , RoomId } ;
56
+ use ruma:: {
57
+ events:: { room:: encrypted, AnySyncEphemeralRoomEvent } ,
58
+ serde:: Raw ,
59
+ OwnedEventId , OwnedRoomId , OwnedTransactionId , RoomId ,
60
+ } ;
56
61
use tokio:: {
57
62
select,
58
63
sync:: {
@@ -62,7 +67,11 @@ use tokio::{
62
67
} ;
63
68
use tracing:: { debug, error, info, info_span, instrument, trace, warn, Instrument as _, Span } ;
64
69
65
- use crate :: { client:: WeakClient , Client } ;
70
+ use crate :: {
71
+ client:: WeakClient ,
72
+ send_queue:: { LocalEchoContent , RoomSendQueueUpdate , SendQueueUpdate } ,
73
+ Client ,
74
+ } ;
66
75
67
76
mod deduplicator;
68
77
mod pagination;
@@ -425,6 +434,7 @@ impl EventCache {
425
434
self . inner . generic_update_sender . subscribe ( )
426
435
}
427
436
437
+ #[ instrument( skip( client, thread_subscriber_sender) ) ]
428
438
async fn handle_thread_subscriber_linked_chunk_update (
429
439
client : & WeakClient ,
430
440
thread_subscriber_sender : & Sender < ( ) > ,
@@ -508,7 +518,102 @@ impl EventCache {
508
518
}
509
519
}
510
520
511
- return true ;
521
+ true
522
+ }
523
+
524
+ #[ instrument( skip( client, thread_subscriber_sender) ) ]
525
+ async fn handle_thread_subscriber_send_queue_update (
526
+ client : & WeakClient ,
527
+ thread_subscriber_sender : & Sender < ( ) > ,
528
+ events_being_sent : & mut HashMap < OwnedTransactionId , OwnedEventId > ,
529
+ up : SendQueueUpdate ,
530
+ ) -> bool {
531
+ let Some ( client) = client. get ( ) else {
532
+ // Client shutting down.
533
+ debug ! ( "Client is shutting down, exiting thread subscriber task" ) ;
534
+ return false ;
535
+ } ;
536
+
537
+ let room_id = up. room_id ;
538
+ let Some ( room) = client. get_room ( & room_id) else {
539
+ warn ! ( %room_id, "unknown room" ) ;
540
+ return true ;
541
+ } ;
542
+
543
+ let extract_thread_root = |serialized_event : SerializableEventContent | {
544
+ match serialized_event. deserialize ( ) {
545
+ Ok ( content) => {
546
+ if let Some ( encrypted:: Relation :: Thread ( thread) ) = content. relation ( ) {
547
+ return Some ( thread. event_id ) ;
548
+ }
549
+ }
550
+ Err ( err) => {
551
+ warn ! ( "error when deserializing content of a local echo: {err}" ) ;
552
+ }
553
+ }
554
+ None
555
+ } ;
556
+
557
+ let ( thread_root, subscribe_up_to) = match up. update {
558
+ RoomSendQueueUpdate :: NewLocalEvent ( local_echo) => {
559
+ match local_echo. content {
560
+ LocalEchoContent :: Event { serialized_event, .. } => {
561
+ if let Some ( thread_root) = extract_thread_root ( serialized_event) {
562
+ events_being_sent. insert ( local_echo. transaction_id , thread_root) ;
563
+ }
564
+ }
565
+ LocalEchoContent :: React { .. } => {
566
+ // Nothing to do, reactions don't count as a thread
567
+ // subscription.
568
+ }
569
+ }
570
+ return true ;
571
+ }
572
+
573
+ RoomSendQueueUpdate :: CancelledLocalEvent { transaction_id } => {
574
+ events_being_sent. remove ( & transaction_id) ;
575
+ return true ;
576
+ }
577
+
578
+ RoomSendQueueUpdate :: ReplacedLocalEvent { transaction_id, new_content } => {
579
+ if let Some ( thread_root) = extract_thread_root ( new_content) {
580
+ events_being_sent. insert ( transaction_id, thread_root) ;
581
+ } else {
582
+ // It could be that the event isn't part of a thread anymore; handle that by
583
+ // removing the pending transaction id.
584
+ events_being_sent. remove ( & transaction_id) ;
585
+ }
586
+ return true ;
587
+ }
588
+
589
+ RoomSendQueueUpdate :: SentEvent { transaction_id, event_id } => {
590
+ if let Some ( thread_root) = events_being_sent. remove ( & transaction_id) {
591
+ ( thread_root, event_id)
592
+ } else {
593
+ // We don't know about the event that has been sent, so ignore it.
594
+ trace ! ( %transaction_id, "received a sent event that we didn't know about, ignoring" ) ;
595
+ return true ;
596
+ }
597
+ }
598
+
599
+ RoomSendQueueUpdate :: SendError { .. }
600
+ | RoomSendQueueUpdate :: RetryEvent { .. }
601
+ | RoomSendQueueUpdate :: MediaUpload { .. } => {
602
+ // Nothing to do for these bad boys.
603
+ return true ;
604
+ }
605
+ } ;
606
+
607
+ // And if we've found such a mention, subscribe to the thread up to this event.
608
+ trace ! ( thread = %thread_root, up_to = %subscribe_up_to, "found a new thread to subscribe to" ) ;
609
+ if let Err ( err) = room. subscribe_thread_if_needed ( & thread_root, Some ( subscribe_up_to) ) . await
610
+ {
611
+ warn ! ( %err, "Failed to subscribe to thread" ) ;
612
+ } else {
613
+ let _ = thread_subscriber_sender. send ( ( ) ) ;
614
+ }
615
+
616
+ true
512
617
}
513
618
514
619
#[ instrument( skip_all) ]
@@ -517,16 +622,46 @@ impl EventCache {
517
622
linked_chunk_update_sender : Sender < RoomEventCacheLinkedChunkUpdate > ,
518
623
thread_subscriber_sender : Sender < ( ) > ,
519
624
) {
520
- if client. get ( ) . map_or ( false , |client| !client. enabled_thread_subscriptions ( ) ) {
521
- trace ! ( "Not spawning the thread subscriber task, because the client is shutting down or is not interested in those" ) ;
625
+ let mut send_q_rx = if let Some ( client) = client. get ( ) {
626
+ if !client. enabled_thread_subscriptions ( ) {
627
+ trace ! ( "Thread subscriptions are not enabled, not spawning thread subscriber task" ) ;
628
+ return ;
629
+ }
630
+
631
+ client. send_queue ( ) . subscribe ( )
632
+ } else {
633
+ trace ! ( "Client is shutting down, not spawning thread subscriber task" ) ;
522
634
return ;
523
- }
635
+ } ;
636
+
637
+ let mut linked_chunk_rx = linked_chunk_update_sender. subscribe ( ) ;
524
638
525
- let mut rx = linked_chunk_update_sender. subscribe ( ) ;
639
+ // A mapping of local echoes (events being sent), to their thread root, if
640
+ // they're in an in-thread reply.
641
+ //
642
+ // Entirely managed by `handle_thread_subscriber_send_queue_update`.
643
+ let mut events_being_sent = HashMap :: new ( ) ;
526
644
527
645
loop {
528
646
select ! {
529
- res = rx. recv( ) => {
647
+ res = send_q_rx. recv( ) => {
648
+ match res {
649
+ Ok ( up) => {
650
+ if !Self :: handle_thread_subscriber_send_queue_update( & client, & thread_subscriber_sender, & mut events_being_sent, up) . await {
651
+ break ;
652
+ }
653
+ }
654
+ Err ( RecvError :: Closed ) => {
655
+ debug!( "Linked chunk update channel has been closed, exiting thread subscriber task" ) ;
656
+ break ;
657
+ }
658
+ Err ( RecvError :: Lagged ( num_skipped) ) => {
659
+ warn!( num_skipped, "Lagged behind linked chunk updates" ) ;
660
+ }
661
+ }
662
+ }
663
+
664
+ res = linked_chunk_rx. recv( ) => {
530
665
match res {
531
666
Ok ( up) => {
532
667
if !Self :: handle_thread_subscriber_linked_chunk_update( & client, & thread_subscriber_sender, up) . await {
0 commit comments