mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 00:12:25 -04:00
automatically abort the media sync on full sync
This commit is contained in:
parent
b3752e8618
commit
683d779c3f
2 changed files with 55 additions and 77 deletions
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
i18n::{tr_args, I18n, TR},
|
i18n::{tr_args, I18n, TR},
|
||||||
latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex},
|
latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex},
|
||||||
log,
|
log,
|
||||||
log::{default_logger, Logger},
|
log::default_logger,
|
||||||
media::check::MediaChecker,
|
media::check::MediaChecker,
|
||||||
media::sync::MediaSyncProgress,
|
media::sync::MediaSyncProgress,
|
||||||
media::MediaManager,
|
media::MediaManager,
|
||||||
|
@ -49,7 +49,6 @@ use prost::Message;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{
|
use std::{
|
||||||
result,
|
result,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
|
@ -89,7 +88,6 @@ pub struct Backend {
|
||||||
i18n: I18n,
|
i18n: I18n,
|
||||||
server: bool,
|
server: bool,
|
||||||
sync_abort: Option<AbortHandle>,
|
sync_abort: Option<AbortHandle>,
|
||||||
media_sync_abort: Option<AbortHandle>,
|
|
||||||
progress_state: Arc<Mutex<ProgressState>>,
|
progress_state: Arc<Mutex<ProgressState>>,
|
||||||
runtime: Option<Runtime>,
|
runtime: Option<Runtime>,
|
||||||
state: Arc<Mutex<BackendState>>,
|
state: Arc<Mutex<BackendState>>,
|
||||||
|
@ -100,6 +98,7 @@ pub struct Backend {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct BackendState {
|
struct BackendState {
|
||||||
remote_sync_status: RemoteSyncStatus,
|
remote_sync_status: RemoteSyncStatus,
|
||||||
|
media_sync_abort: Option<AbortHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -965,15 +964,13 @@ impl BackendService for Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_collection(&mut self, input: pb::CloseCollectionIn) -> BackendResult<Empty> {
|
fn close_collection(&mut self, input: pb::CloseCollectionIn) -> BackendResult<Empty> {
|
||||||
|
self.abort_media_sync_and_wait();
|
||||||
|
|
||||||
let mut col = self.col.lock().unwrap();
|
let mut col = self.col.lock().unwrap();
|
||||||
if col.is_none() {
|
if col.is_none() {
|
||||||
return Err(AnkiError::CollectionNotOpen);
|
return Err(AnkiError::CollectionNotOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !col.as_ref().unwrap().can_close() {
|
|
||||||
return Err(AnkiError::invalid_input("can't close yet"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col_inner = col.take().unwrap();
|
let col_inner = col.take().unwrap();
|
||||||
if input.downgrade_to_schema11 {
|
if input.downgrade_to_schema11 {
|
||||||
let log = log::terminal();
|
let log = log::terminal();
|
||||||
|
@ -1011,22 +1008,7 @@ impl BackendService for Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_media(&mut self, input: pb::SyncAuth) -> BackendResult<Empty> {
|
fn sync_media(&mut self, input: pb::SyncAuth) -> BackendResult<Empty> {
|
||||||
let mut guard = self.col.lock().unwrap();
|
self.sync_media_inner(input).map(Into::into)
|
||||||
|
|
||||||
let col = guard.as_mut().unwrap();
|
|
||||||
col.set_media_sync_running()?;
|
|
||||||
|
|
||||||
let folder = col.media_folder.clone();
|
|
||||||
let db = col.media_db.clone();
|
|
||||||
let log = col.log.clone();
|
|
||||||
|
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
let res = self.sync_media_inner(input, folder, db, log);
|
|
||||||
|
|
||||||
self.with_col(|col| col.set_media_sync_finished())?;
|
|
||||||
|
|
||||||
res.map(Into::into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abort_sync(&mut self, _input: Empty) -> BackendResult<Empty> {
|
fn abort_sync(&mut self, _input: Empty) -> BackendResult<Empty> {
|
||||||
|
@ -1036,8 +1018,10 @@ impl BackendService for Backend {
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Abort the media sync. Does not wait for completion.
|
||||||
fn abort_media_sync(&mut self, _input: Empty) -> BackendResult<Empty> {
|
fn abort_media_sync(&mut self, _input: Empty) -> BackendResult<Empty> {
|
||||||
if let Some(handle) = self.media_sync_abort.take() {
|
let guard = self.state.lock().unwrap();
|
||||||
|
if let Some(handle) = &guard.media_sync_abort {
|
||||||
handle.abort();
|
handle.abort();
|
||||||
}
|
}
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
|
@ -1172,7 +1156,6 @@ impl Backend {
|
||||||
i18n,
|
i18n,
|
||||||
server,
|
server,
|
||||||
sync_abort: None,
|
sync_abort: None,
|
||||||
media_sync_abort: None,
|
|
||||||
progress_state: Arc::new(Mutex::new(ProgressState {
|
progress_state: Arc::new(Mutex::new(ProgressState {
|
||||||
want_abort: false,
|
want_abort: false,
|
||||||
last_progress: None,
|
last_progress: None,
|
||||||
|
@ -1241,16 +1224,28 @@ impl Backend {
|
||||||
self.runtime.as_ref().unwrap().handle().clone()
|
self.runtime.as_ref().unwrap().handle().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_media_inner(
|
fn sync_media_inner(&mut self, input: pb::SyncAuth) -> Result<()> {
|
||||||
&mut self,
|
// mark media sync as active
|
||||||
input: pb::SyncAuth,
|
|
||||||
folder: PathBuf,
|
|
||||||
db: PathBuf,
|
|
||||||
log: Logger,
|
|
||||||
) -> Result<()> {
|
|
||||||
let (abort_handle, abort_reg) = AbortHandle::new_pair();
|
let (abort_handle, abort_reg) = AbortHandle::new_pair();
|
||||||
self.media_sync_abort = Some(abort_handle);
|
{
|
||||||
|
let mut guard = self.state.lock().unwrap();
|
||||||
|
if guard.media_sync_abort.is_some() {
|
||||||
|
// media sync is already active
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
guard.media_sync_abort = Some(abort_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get required info from collection
|
||||||
|
let mut guard = self.col.lock().unwrap();
|
||||||
|
let col = guard.as_mut().unwrap();
|
||||||
|
let folder = col.media_folder.clone();
|
||||||
|
let db = col.media_db.clone();
|
||||||
|
let log = col.log.clone();
|
||||||
|
drop(guard);
|
||||||
|
|
||||||
|
// start the sync
|
||||||
let mut handler = self.new_progress_handler();
|
let mut handler = self.new_progress_handler();
|
||||||
let progress_fn = move |progress| handler.update(progress, true);
|
let progress_fn = move |progress| handler.update(progress, true);
|
||||||
|
|
||||||
|
@ -1258,15 +1253,35 @@ impl Backend {
|
||||||
let rt = self.runtime_handle();
|
let rt = self.runtime_handle();
|
||||||
let sync_fut = mgr.sync_media(progress_fn, input.host_number, &input.hkey, log);
|
let sync_fut = mgr.sync_media(progress_fn, input.host_number, &input.hkey, log);
|
||||||
let abortable_sync = Abortable::new(sync_fut, abort_reg);
|
let abortable_sync = Abortable::new(sync_fut, abort_reg);
|
||||||
let ret = match rt.block_on(abortable_sync) {
|
let result = rt.block_on(abortable_sync);
|
||||||
|
|
||||||
|
// mark inactive
|
||||||
|
self.state.lock().unwrap().media_sync_abort.take();
|
||||||
|
|
||||||
|
// return result
|
||||||
|
match result {
|
||||||
Ok(sync_result) => sync_result,
|
Ok(sync_result) => sync_result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// aborted sync
|
// aborted sync
|
||||||
Err(AnkiError::Interrupted)
|
Err(AnkiError::Interrupted)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
self.media_sync_abort = None;
|
}
|
||||||
ret
|
|
||||||
|
/// Abort the media sync. Won't return until aborted.
|
||||||
|
fn abort_media_sync_and_wait(&mut self) {
|
||||||
|
let guard = self.state.lock().unwrap();
|
||||||
|
if let Some(handle) = &guard.media_sync_abort {
|
||||||
|
handle.abort();
|
||||||
|
self.progress_state.lock().unwrap().want_abort = true;
|
||||||
|
}
|
||||||
|
drop(guard);
|
||||||
|
|
||||||
|
// block until it aborts
|
||||||
|
while self.state.lock().unwrap().media_sync_abort.is_some() {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
self.progress_state.lock().unwrap().want_abort = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_login_inner(&mut self, input: pb::SyncLoginIn) -> BackendResult<pb::SyncAuth> {
|
fn sync_login_inner(&mut self, input: pb::SyncLoginIn) -> BackendResult<pb::SyncAuth> {
|
||||||
|
@ -1361,15 +1376,14 @@ impl Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_sync_inner(&mut self, input: pb::SyncAuth, upload: bool) -> Result<()> {
|
fn full_sync_inner(&mut self, input: pb::SyncAuth, upload: bool) -> Result<()> {
|
||||||
|
self.abort_media_sync_and_wait();
|
||||||
|
|
||||||
let rt = self.runtime_handle();
|
let rt = self.runtime_handle();
|
||||||
|
|
||||||
let mut col = self.col.lock().unwrap();
|
let mut col = self.col.lock().unwrap();
|
||||||
if col.is_none() {
|
if col.is_none() {
|
||||||
return Err(AnkiError::CollectionNotOpen);
|
return Err(AnkiError::CollectionNotOpen);
|
||||||
}
|
}
|
||||||
if !col.as_ref().unwrap().can_close() {
|
|
||||||
return Err(AnkiError::invalid_input("can't close yet"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col_inner = col.take().unwrap();
|
let col_inner = col.take().unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use crate::err::{AnkiError, Result};
|
use crate::err::Result;
|
||||||
use crate::i18n::I18n;
|
use crate::i18n::I18n;
|
||||||
use crate::log::Logger;
|
use crate::log::Logger;
|
||||||
use crate::types::Usn;
|
use crate::types::Usn;
|
||||||
|
@ -47,25 +47,11 @@ pub fn open_test_collection() -> Collection {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct CollectionState {
|
pub struct CollectionState {
|
||||||
task_state: CollectionTaskState,
|
|
||||||
pub(crate) undo: UndoManager,
|
pub(crate) undo: UndoManager,
|
||||||
pub(crate) notetype_cache: HashMap<NoteTypeID, Arc<NoteType>>,
|
pub(crate) notetype_cache: HashMap<NoteTypeID, Arc<NoteType>>,
|
||||||
pub(crate) deck_cache: HashMap<DeckID, Arc<Deck>>,
|
pub(crate) deck_cache: HashMap<DeckID, Arc<Deck>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum CollectionTaskState {
|
|
||||||
Normal,
|
|
||||||
// in this state, the DB must not be closed
|
|
||||||
MediaSyncRunning,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CollectionTaskState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Collection {
|
pub struct Collection {
|
||||||
pub(crate) storage: SqliteStorage,
|
pub(crate) storage: SqliteStorage,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -113,28 +99,6 @@ impl Collection {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_media_sync_running(&mut self) -> Result<()> {
|
|
||||||
if self.state.task_state == CollectionTaskState::Normal {
|
|
||||||
self.state.task_state = CollectionTaskState::MediaSyncRunning;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(AnkiError::invalid_input("media sync already running"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_media_sync_finished(&mut self) -> Result<()> {
|
|
||||||
if self.state.task_state == CollectionTaskState::MediaSyncRunning {
|
|
||||||
self.state.task_state = CollectionTaskState::Normal;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(AnkiError::invalid_input("media sync not running"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn can_close(&self) -> bool {
|
|
||||||
self.state.task_state == CollectionTaskState::Normal
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn close(self, downgrade: bool) -> Result<()> {
|
pub(crate) fn close(self, downgrade: bool) -> Result<()> {
|
||||||
self.storage.close(downgrade)
|
self.storage.close(downgrade)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue