release mutex before beginning media sync

And check media sync is not running on close
This commit is contained in:
Damien Elmes 2020-03-14 11:52:39 +10:00
parent 5f19048c93
commit 72bcef917e
2 changed files with 75 additions and 16 deletions

View file

@ -8,7 +8,7 @@ use crate::collection::{open_collection, Collection};
use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind}; use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind};
use crate::i18n::{tr_args, FString, I18n}; use crate::i18n::{tr_args, FString, I18n};
use crate::latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex}; use crate::latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex};
use crate::log::default_logger; use crate::log::{default_logger, Logger};
use crate::media::check::MediaChecker; use crate::media::check::MediaChecker;
use crate::media::sync::MediaSyncProgress; use crate::media::sync::MediaSyncProgress;
use crate::media::MediaManager; use crate::media::MediaManager;
@ -23,6 +23,7 @@ use crate::{backend_proto as pb, log};
use fluent::FluentValue; use fluent::FluentValue;
use prost::Message; use prost::Message;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@ -157,13 +158,13 @@ impl Backend {
/// If collection is not open, return an error. /// If collection is not open, return an error.
fn with_col<F, T>(&self, func: F) -> Result<T> fn with_col<F, T>(&self, func: F) -> Result<T>
where where
F: FnOnce(&Collection) -> Result<T>, F: FnOnce(&mut Collection) -> Result<T>,
{ {
func( func(
self.col self.col
.lock() .lock()
.unwrap() .unwrap()
.as_ref() .as_mut()
.ok_or(AnkiError::CollectionNotOpen)?, .ok_or(AnkiError::CollectionNotOpen)?,
) )
} }
@ -249,8 +250,8 @@ impl Backend {
} }
fn open_collection(&self, input: pb::OpenCollectionIn) -> Result<()> { fn open_collection(&self, input: pb::OpenCollectionIn) -> Result<()> {
let mut mutex = self.col.lock().unwrap(); let mut col = self.col.lock().unwrap();
if mutex.is_some() { if col.is_some() {
return Err(AnkiError::CollectionAlreadyOpen); return Err(AnkiError::CollectionAlreadyOpen);
} }
@ -263,7 +264,7 @@ impl Backend {
}; };
let logger = default_logger(log_path)?; let logger = default_logger(log_path)?;
let col = open_collection( let new_col = open_collection(
input.collection_path, input.collection_path,
input.media_folder_path, input.media_folder_path,
input.media_db_path, input.media_db_path,
@ -272,18 +273,22 @@ impl Backend {
logger, logger,
)?; )?;
*mutex = Some(col); *col = Some(new_col);
Ok(()) Ok(())
} }
fn close_collection(&self) -> Result<()> { fn close_collection(&self) -> Result<()> {
let mut mutex = self.col.lock().unwrap(); let mut col = self.col.lock().unwrap();
if mutex.is_none() { if col.is_none() {
return Err(AnkiError::CollectionNotOpen); return Err(AnkiError::CollectionNotOpen);
} }
*mutex = None; if !col.as_ref().unwrap().can_close() {
return Err(AnkiError::invalid_input("can't close yet"));
}
*col = None;
Ok(()) Ok(())
} }
@ -445,15 +450,38 @@ impl Backend {
// fixme: will block other db access // fixme: will block other db access
fn sync_media(&self, input: SyncMediaIn) -> Result<()> { fn sync_media(&self, input: SyncMediaIn) -> Result<()> {
let mut guard = self.col.lock().unwrap();
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
}
fn sync_media_inner(
&self,
input: pb::SyncMediaIn,
folder: PathBuf,
db: PathBuf,
log: Logger,
) -> Result<()> {
let callback = |progress: &MediaSyncProgress| { let callback = |progress: &MediaSyncProgress| {
self.fire_progress_callback(Progress::MediaSync(progress)) self.fire_progress_callback(Progress::MediaSync(progress))
}; };
self.with_col(|col| { let mgr = MediaManager::new(&folder, &db)?;
let mgr = MediaManager::new(&col.media_folder, &col.media_db)?;
let mut rt = Runtime::new().unwrap(); let mut rt = Runtime::new().unwrap();
rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, col.log.clone())) rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, log))
})
} }
fn check_media(&self) -> Result<pb::MediaCheckOut> { fn check_media(&self) -> Result<pb::MediaCheckOut> {

View file

@ -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::Result; use crate::err::{AnkiError, Result};
use crate::i18n::I18n; use crate::i18n::I18n;
use crate::log::Logger; use crate::log::Logger;
use crate::storage::{SqliteStorage, StorageContext}; use crate::storage::{SqliteStorage, StorageContext};
@ -26,11 +26,19 @@ pub fn open_collection<P: Into<PathBuf>>(
server, server,
i18n, i18n,
log, log,
state: CollectionState::Normal,
}; };
Ok(col) Ok(col)
} }
#[derive(Debug, PartialEq)]
pub enum CollectionState {
Normal,
// in this state, the DB must not be closed
MediaSyncRunning,
}
pub struct Collection { pub struct Collection {
pub(crate) storage: SqliteStorage, pub(crate) storage: SqliteStorage,
#[allow(dead_code)] #[allow(dead_code)]
@ -40,6 +48,7 @@ pub struct Collection {
pub(crate) server: bool, pub(crate) server: bool,
pub(crate) i18n: I18n, pub(crate) i18n: I18n,
pub(crate) log: Logger, pub(crate) log: Logger,
state: CollectionState,
} }
pub(crate) enum CollectionOp {} pub(crate) enum CollectionOp {}
@ -97,4 +106,26 @@ impl Collection {
res res
}) })
} }
pub(crate) fn set_media_sync_running(&mut self) -> Result<()> {
if self.state == CollectionState::Normal {
self.state = CollectionState::MediaSyncRunning;
Ok(())
} else {
Err(AnkiError::invalid_input("media sync already running"))
}
}
pub(crate) fn set_media_sync_finished(&mut self) -> Result<()> {
if self.state == CollectionState::MediaSyncRunning {
self.state = CollectionState::Normal;
Ok(())
} else {
Err(AnkiError::invalid_input("media sync not running"))
}
}
pub(crate) fn can_close(&self) -> bool {
self.state == CollectionState::Normal
}
} }