Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bindings/matrix-sdk-ffi/changelog.d/6677.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add methods to make QR login handlers exit cooperatively.
91 changes: 75 additions & 16 deletions bindings/matrix-sdk-ffi/src/qr_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use matrix_sdk::authentication::oauth::{
};
use matrix_sdk_base::crypto::types::qr_login::{self, QrCodeIntent};
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm, stream::StreamExt};
use tokio::sync::Notify;

use crate::{
authentication::OAuthConfiguration, runtime::get_runtime_handle, task_handle::TaskHandle,
Expand All @@ -33,11 +34,12 @@ use crate::{
pub struct LoginWithQrCodeHandler {
oauth: OAuth,
oauth_configuration: OAuthConfiguration,
cancel: Notify,
}

impl LoginWithQrCodeHandler {
pub(crate) fn new(oauth: OAuth, oauth_configuration: OAuthConfiguration) -> Self {
Self { oauth, oauth_configuration }
Self { oauth, oauth_configuration, cancel: Notify::new() }
}
}

Expand Down Expand Up @@ -82,15 +84,26 @@ impl LoginWithQrCodeHandler {

// We create this task, which will get cancelled once it's dropped, just in case
// the progress stream doesn't end.
let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
while let Some(state) = progress.next().await {
progress_listener.on_update(state.into());
}
}));

login.await?;

Ok(())
tokio::select! {
// Give priority to cancellation if both updates occur at the same time.
biased;
_ = self.cancel.notified() => {
// Stop forwarding progress to the foreign callback before tearing
// down the handler.
drop(progress_task);
Err(HumanQrLoginError::Cancelled)
}
result = login => {
result?;
Ok(())
}
}
}

/// This method allows you to log in by generating a QR code.
Expand Down Expand Up @@ -126,27 +139,45 @@ impl LoginWithQrCodeHandler {

// We create this task, which will get cancelled once it's dropped, just in case
// the progress stream doesn't end.
let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
while let Some(state) = progress.next().await {
progress_listener.on_update(state.into());
}
}));

login.await?;
tokio::select! {
// Give priority to cancellation if both updates occur at the same time.
biased;
_ = self.cancel.notified() => {
// Stop forwarding progress to the foreign callback before tearing
// down the handler.
drop(progress_task);
Comment thread
Johennes marked this conversation as resolved.
Err(HumanQrLoginError::Cancelled)
}
result = login => {
result?;
Ok(())
}
}
}

Ok(())
/// Request the handler to abort cooperatively. This will make the handler
/// tear down its running task and then return the `Cancelled` error.
pub fn abort(&self) {
self.cancel.notify_waiters();
}
}

/// Handler for granting login in with a QR code.
#[derive(uniffi::Object)]
pub struct GrantLoginWithQrCodeHandler {
oauth: OAuth,
cancel: Notify,
}

impl GrantLoginWithQrCodeHandler {
pub(crate) fn new(oauth: OAuth) -> Self {
Self { oauth }
Self { oauth, cancel: Notify::new() }
}
}

Expand Down Expand Up @@ -182,15 +213,26 @@ impl GrantLoginWithQrCodeHandler {

// We create this task, which will get cancelled once it's dropped, just in case
// the progress stream doesn't end.
let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
while let Some(state) = progress.next().await {
progress_listener.on_update(state.into());
}
}));

grant.await?;

Ok(())
tokio::select! {
// Give priority to cancellation if both updates occur at the same time.
biased;
_ = self.cancel.notified() => {
// Stop forwarding progress to the foreign callback before tearing
// down the handler.
drop(progress_task);
Err(HumanQrGrantLoginError::Cancelled)
}
result = grant => {
result?;
Ok(())
}
}
}

/// This method allows you to grant login by generating a QR code.
Expand Down Expand Up @@ -220,15 +262,32 @@ impl GrantLoginWithQrCodeHandler {

// We create this task, which will get cancelled once it's dropped, just in case
// the progress stream doesn't end.
let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
while let Some(state) = progress.next().await {
progress_listener.on_update(state.into());
}
}));

grant.await?;
tokio::select! {
// Give priority to cancellation if both updates occur at the same time.
biased;
_ = self.cancel.notified() => {
// Stop forwarding progress to the foreign callback before tearing
// down the handler.
drop(progress_task);
Err(HumanQrGrantLoginError::Cancelled)
}
result = grant => {
result?;
Ok(())
}
}
}

Ok(())
/// Request the handler to abort cooperatively. This will make the handler
/// tear down its running task and then return the `Cancelled` error.
pub fn abort(&self) {
self.cancel.notify_waiters();
}
}

Expand Down
Loading