Merge branch 'lock_requests'

master
Damir Jelić 2021-03-16 17:01:04 +01:00
commit 9aad775f01
3 changed files with 91 additions and 35 deletions

View File

@ -88,16 +88,15 @@ use matrix_sdk_common::{
}; };
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_common::{ use matrix_sdk_common::api::r0::{
api::r0::{
keys::{get_keys, upload_keys, upload_signing_keys::Request as UploadSigningKeysRequest}, keys::{get_keys, upload_keys, upload_signing_keys::Request as UploadSigningKeysRequest},
to_device::send_event_to_device::{ to_device::send_event_to_device::{
Request as RumaToDeviceRequest, Response as ToDeviceResponse, Request as RumaToDeviceRequest, Response as ToDeviceResponse,
}, },
},
locks::Mutex,
}; };
use matrix_sdk_common::locks::Mutex;
use crate::{ use crate::{
error::HttpError, error::HttpError,
http_client::{client_with_config, HttpClient, HttpSend}, http_client::{client_with_config, HttpClient, HttpSend},
@ -134,10 +133,12 @@ pub struct Client {
/// Locks making sure we only have one group session sharing request in /// Locks making sure we only have one group session sharing request in
/// flight per room. /// flight per room.
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
pub(crate) group_session_locks: DashMap<RoomId, Arc<Mutex<()>>>, pub(crate) group_session_locks: Arc<DashMap<RoomId, Arc<Mutex<()>>>>,
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
/// Lock making sure we're only doing one key claim request at a time. /// Lock making sure we're only doing one key claim request at a time.
key_claim_lock: Arc<Mutex<()>>, key_claim_lock: Arc<Mutex<()>>,
pub(crate) members_request_locks: Arc<DashMap<RoomId, Arc<Mutex<()>>>>,
pub(crate) typing_notice_times: Arc<DashMap<RoomId, Instant>>,
} }
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -390,9 +391,11 @@ impl Client {
http_client, http_client,
base_client, base_client,
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
group_session_locks: DashMap::new(), group_session_locks: Arc::new(DashMap::new()),
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
key_claim_lock: Arc::new(Mutex::new(())), key_claim_lock: Arc::new(Mutex::new(())),
members_request_locks: Arc::new(DashMap::new()),
typing_notice_times: Arc::new(DashMap::new()),
}) })
} }
@ -1668,7 +1671,6 @@ impl Client {
/// # use std::{path::PathBuf, time::Duration}; /// # use std::{path::PathBuf, time::Duration};
/// # use matrix_sdk::{ /// # use matrix_sdk::{
/// # Client, SyncSettings, /// # Client, SyncSettings,
/// # api::r0::typing::create_typing_event::Typing,
/// # identifiers::room_id, /// # identifiers::room_id,
/// # }; /// # };
/// # use futures::executor::block_on; /// # use futures::executor::block_on;
@ -1746,7 +1748,6 @@ impl Client {
/// # use std::{path::PathBuf, time::Duration}; /// # use std::{path::PathBuf, time::Duration};
/// # use matrix_sdk::{ /// # use matrix_sdk::{
/// # Client, SyncSettings, /// # Client, SyncSettings,
/// # api::r0::typing::create_typing_event::Typing,
/// # identifiers::room_id, /// # identifiers::room_id,
/// # }; /// # };
/// # use futures::executor::block_on; /// # use futures::executor::block_on;
@ -1801,7 +1802,7 @@ mod test {
api::r0::{ api::r0::{
account::register::Request as RegistrationRequest, account::register::Request as RegistrationRequest,
directory::get_public_rooms_filtered::Request as PublicRoomsFilterRequest, directory::get_public_rooms_filtered::Request as PublicRoomsFilterRequest,
membership::Invite3pid, typing::create_typing_event::Typing, uiaa::AuthData, membership::Invite3pid, uiaa::AuthData,
}, },
assign, assign,
directory::Filter, directory::Filter,
@ -2450,9 +2451,7 @@ mod test {
.get_joined_room(&room_id!("!SVkFJHzfwvuaIEawgC:localhost")) .get_joined_room(&room_id!("!SVkFJHzfwvuaIEawgC:localhost"))
.unwrap(); .unwrap();
room.typing_notice(Typing::Yes(std::time::Duration::from_secs(1))) room.typing_notice(true).await.unwrap();
.await
.unwrap();
} }
#[tokio::test] #[tokio::test]

View File

@ -2,7 +2,9 @@ use matrix_sdk_common::api::r0::{
membership::{get_member_events, join_room_by_id, leave_room}, membership::{get_member_events, join_room_by_id, leave_room},
message::get_message_events, message::get_message_events,
}; };
use std::ops::Deref; use matrix_sdk_common::locks::Mutex;
use std::{ops::Deref, sync::Arc};
use crate::{Client, Result, Room, RoomMember}; use crate::{Client, Result, Room, RoomMember};
@ -96,7 +98,22 @@ impl Common {
} }
pub(crate) async fn request_members(&self) -> Result<()> { pub(crate) async fn request_members(&self) -> Result<()> {
// TODO: don't send a request if a request is being sent #[allow(clippy::map_clone)]
if let Some(mutex) = self
.client
.members_request_locks
.get(self.inner.room_id())
.map(|m| m.clone())
{
mutex.lock().await;
} else {
let mutex = Arc::new(Mutex::new(()));
self.client
.members_request_locks
.insert(self.inner.room_id().clone(), mutex.clone());
let _guard = mutex.lock().await;
let request = get_member_events::Request::new(self.inner.room_id()); let request = get_member_events::Request::new(self.inner.room_id());
let response = self.client.send(request, None).await?; let response = self.client.send(request, None).await?;
@ -105,6 +122,11 @@ impl Common {
.receive_members(self.inner.room_id(), &response) .receive_members(self.inner.room_id(), &response)
.await?; .await?;
self.client
.members_request_locks
.remove(self.inner.room_id());
}
Ok(()) Ok(())
} }

View File

@ -27,6 +27,7 @@ use matrix_sdk_common::{
AnyMessageEventContent, AnyStateEventContent, AnyMessageEventContent, AnyStateEventContent,
}, },
identifiers::{EventId, UserId}, identifiers::{EventId, UserId},
instant::{Duration, Instant},
uuid::Uuid, uuid::Uuid,
}; };
@ -41,6 +42,9 @@ use matrix_sdk_base::crypto::AttachmentEncryptor;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use tracing::instrument; use tracing::instrument;
const TYPING_NOTICE_TIMEOUT: Duration = Duration::from_secs(4);
const TYPING_NOTICE_RESEND_TIMEOUT: Duration = Duration::from_secs(3);
/// A room in the joined state. /// A room in the joined state.
/// ///
/// The `JoinedRoom` contains all methodes specific to a `Room` with type `RoomType::Joined`. /// The `JoinedRoom` contains all methodes specific to a `Room` with type `RoomType::Joined`.
@ -138,11 +142,16 @@ impl Joined {
Ok(()) Ok(())
} }
/// Send a request to notify this room of a user typing. /// Activate typing notice for this room.
///
/// The typing notice remains active for 4s. It can be deactivate at any point by setting
/// typing to `false`. If this method is called while the typing notice is active nothing will happen.
/// This method can be called on every key stroke, since it will do nothing while typing is
/// active.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `typing` - Whether the user is typing, and how long. /// * `typing` - Whether the user is typing or has stopped typing.
/// ///
/// # Examples /// # Examples
/// ///
@ -164,20 +173,46 @@ impl Joined {
/// # .unwrap(); /// # .unwrap();
/// ///
/// room /// room
/// .typing_notice(Typing::Yes(Duration::from_secs(4))) /// .typing_notice(true)
/// .await /// .await
/// .expect("Can't get devices from server"); /// .expect("Can't get devices from server");
/// # }); /// # });
/// ``` /// ```
pub async fn typing_notice(&self, typing: impl Into<Typing>) -> Result<()> { pub async fn typing_notice(&self, typing: bool) -> Result<()> {
// TODO: don't send a request if a typing notice is being sent or is already active // Only send a request to the homeserver if the old timeout has elapsed or the typing
let request = TypingRequest::new( // notice changed state within the TYPING_NOTICE_TIMEOUT
self.inner.own_user_id(), let send =
self.inner.room_id(), if let Some(typing_time) = self.client.typing_notice_times.get(self.inner.room_id()) {
typing.into(), if typing_time.elapsed() > TYPING_NOTICE_RESEND_TIMEOUT {
); // We always reactivate the typing notice if typing is true or we may need to
// deactivate it if it's currently active if typing is false
typing || typing_time.elapsed() <= TYPING_NOTICE_TIMEOUT
} else {
// Only send a request when we need to deactivate typing
!typing
}
} else {
// Typing notice is currently deactivated, therefore, send a request only when it's
// about to be activated
typing
};
if send {
let typing = if typing {
self.client
.typing_notice_times
.insert(self.inner.room_id().clone(), Instant::now());
Typing::Yes(TYPING_NOTICE_TIMEOUT)
} else {
self.client.typing_notice_times.remove(self.inner.room_id());
Typing::No
};
let request =
TypingRequest::new(self.inner.own_user_id(), self.inner.room_id(), typing);
self.client.send(request, None).await?; self.client.send(request, None).await?;
}
Ok(()) Ok(())
} }