From c9da4bc1a653b2ef3d0be316c86f75d7c227f2fc Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 1 Apr 2020 09:49:25 +1000 Subject: [PATCH] allow aborting a media sync while http req in flight The progress handling code needs a rethink, as we now have two separate ways to flag that the media sync should abort. In the future, it may make sense to switch to polling the backend for progress, instead of passing a callback in. --- proto/backend.proto | 2 ++ pylib/anki/rsbackend.py | 3 +++ qt/aqt/mediasync.py | 1 + rslib/Cargo.toml | 1 + rslib/src/backend/mod.rs | 34 +++++++++++++++++++++++++++++----- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/proto/backend.proto b/proto/backend.proto index 04809783c..4d387ec02 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -55,6 +55,7 @@ message BackendInput { Empty all_deck_config = 43; Empty new_deck_config = 44; int64 remove_deck_config = 45; + Empty abort_media_sync = 46; } } @@ -70,6 +71,7 @@ message BackendOutput { string format_time_span = 31; string studied_today = 32; string congrats_learn_msg = 33; + Empty abort_media_sync = 46; // fallible commands TemplateRequirementsOut template_requirements = 16; diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 886e3dc9d..8d39ff70c 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -519,6 +519,9 @@ class RustBackend: def remove_deck_config(self, dcid: int) -> None: self._run_command(pb.BackendInput(remove_deck_config=dcid)) + def abort_media_sync(self): + self._run_command(pb.BackendInput(abort_media_sync=pb.Empty())) + def translate_string_in( key: TR, **kwargs: Union[str, int, float] diff --git a/qt/aqt/mediasync.py b/qt/aqt/mediasync.py index ebafee8d6..44fc8b3e7 100644 --- a/qt/aqt/mediasync.py +++ b/qt/aqt/mediasync.py @@ -123,6 +123,7 @@ class MediaSyncer: return self._log_and_notify(tr(TR.SYNC_MEDIA_ABORTING)) self._want_stop = True + self.mw.col.backend.abort_media_sync() def is_syncing(self) -> bool: return self._syncing diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 906fb1692..099a21e0c 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -39,6 +39,7 @@ slog-envlogger = "2.2.0" serde_repr = "0.1.5" num_enum = "0.4.2" unicase = "2.6.0" +futures = "0.3.4" # pinned until rusqlite 0.22 comes out [target.'cfg(target_vendor="apple")'.dependencies.rusqlite] diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index d8c70769a..4cf7f09ee 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -30,6 +30,7 @@ use crate::timestamp::TimestampSecs; use crate::types::Usn; use crate::{backend_proto as pb, log}; use fluent::FluentValue; +use futures::future::{AbortHandle, Abortable}; use log::error; use prost::Message; use std::collections::{HashMap, HashSet}; @@ -47,6 +48,7 @@ pub struct Backend { progress_callback: Option, i18n: I18n, server: bool, + media_sync_abort: Option, } enum Progress<'a> { @@ -135,6 +137,7 @@ impl Backend { progress_callback: None, i18n, server, + media_sync_abort: None, } } @@ -274,6 +277,10 @@ impl Backend { self.remove_deck_config(dcid)?; OValue::RemoveDeckConfig(pb::Empty {}) } + Value::AbortMediaSync(_) => { + self.abort_media_sync(); + OValue::AbortMediaSync(pb::Empty {}) + } }) } @@ -481,9 +488,7 @@ impl Backend { }) } - // fixme: will block other db access - - fn sync_media(&self, input: SyncMediaIn) -> Result<()> { + fn sync_media(&mut self, input: SyncMediaIn) -> Result<()> { let mut guard = self.col.lock().unwrap(); let col = guard.as_mut().unwrap(); @@ -503,19 +508,38 @@ impl Backend { } fn sync_media_inner( - &self, + &mut self, input: pb::SyncMediaIn, folder: PathBuf, db: PathBuf, log: Logger, ) -> Result<()> { + let (abort_handle, abort_reg) = AbortHandle::new_pair(); + self.media_sync_abort = Some(abort_handle); + let callback = |progress: &MediaSyncProgress| { self.fire_progress_callback(Progress::MediaSync(progress)) }; let mgr = MediaManager::new(&folder, &db)?; let mut rt = Runtime::new().unwrap(); - rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, log)) + let sync_fut = mgr.sync_media(callback, &input.endpoint, &input.hkey, log); + let abortable_sync = Abortable::new(sync_fut, abort_reg); + let ret = match rt.block_on(abortable_sync) { + Ok(sync_result) => sync_result, + Err(_) => { + // aborted sync + Ok(()) + } + }; + self.media_sync_abort = None; + ret + } + + fn abort_media_sync(&mut self) { + if let Some(handle) = self.media_sync_abort.take() { + handle.abort(); + } } fn check_media(&self) -> Result {