wrap the collection in a mutex so DB access is thread safe
This commit is contained in:
parent
47c142a74c
commit
14546c8a8b
@ -8,7 +8,7 @@ use crate::collection::{open_collection, Collection};
|
||||
use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind};
|
||||
use crate::i18n::{tr_args, FString, I18n};
|
||||
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::sync::MediaSyncProgress;
|
||||
use crate::media::MediaManager;
|
||||
@ -24,6 +24,7 @@ use fluent::FluentValue;
|
||||
use prost::Message;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
mod dbproxy;
|
||||
@ -31,12 +32,14 @@ mod dbproxy;
|
||||
pub type ProtoProgressCallback = Box<dyn Fn(Vec<u8>) -> bool + Send>;
|
||||
|
||||
pub struct Backend {
|
||||
col: Collection,
|
||||
col: Arc<Mutex<Collection>>,
|
||||
#[allow(dead_code)]
|
||||
col_path: PathBuf,
|
||||
media_folder: PathBuf,
|
||||
media_db: String,
|
||||
progress_callback: Option<ProtoProgressCallback>,
|
||||
i18n: I18n,
|
||||
log: Logger,
|
||||
}
|
||||
|
||||
enum Progress<'a> {
|
||||
@ -122,14 +125,21 @@ pub fn init_backend(init_msg: &[u8]) -> std::result::Result<Backend, String> {
|
||||
log::terminal(),
|
||||
);
|
||||
|
||||
let col = open_collection(&input.collection_path, input.server, i18n, logger)
|
||||
.map_err(|e| format!("Unable to open collection: {:?}", e))?;
|
||||
let col = open_collection(
|
||||
&input.collection_path,
|
||||
input.server,
|
||||
i18n.clone(),
|
||||
logger.clone(),
|
||||
)
|
||||
.map_err(|e| format!("Unable to open collection: {:?}", e))?;
|
||||
|
||||
match Backend::new(
|
||||
col,
|
||||
&input.collection_path,
|
||||
&input.media_folder_path,
|
||||
&input.media_db_path,
|
||||
i18n,
|
||||
logger,
|
||||
) {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
@ -142,18 +152,22 @@ impl Backend {
|
||||
col_path: &str,
|
||||
media_folder: &str,
|
||||
media_db: &str,
|
||||
i18n: I18n,
|
||||
log: Logger,
|
||||
) -> Result<Backend> {
|
||||
Ok(Backend {
|
||||
col,
|
||||
col: Arc::new(Mutex::new(col)),
|
||||
col_path: col_path.into(),
|
||||
media_folder: media_folder.into(),
|
||||
media_db: media_db.into(),
|
||||
progress_callback: None,
|
||||
i18n,
|
||||
log,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn i18n(&self) -> &I18n {
|
||||
&self.col.i18n
|
||||
&self.i18n
|
||||
}
|
||||
|
||||
/// Decode a request, process it, and return the encoded result.
|
||||
@ -165,7 +179,7 @@ impl Backend {
|
||||
Err(_e) => {
|
||||
// unable to decode
|
||||
let err = AnkiError::invalid_input("couldn't decode backend request");
|
||||
let oerr = anki_error_to_proto_error(err, &self.col.i18n);
|
||||
let oerr = anki_error_to_proto_error(err, &self.i18n);
|
||||
let output = pb::BackendOutput {
|
||||
value: Some(oerr.into()),
|
||||
};
|
||||
@ -183,12 +197,12 @@ impl Backend {
|
||||
let oval = if let Some(ival) = input.value {
|
||||
match self.run_command_inner(ival) {
|
||||
Ok(output) => output,
|
||||
Err(err) => anki_error_to_proto_error(err, &self.col.i18n).into(),
|
||||
Err(err) => anki_error_to_proto_error(err, &self.i18n).into(),
|
||||
}
|
||||
} else {
|
||||
anki_error_to_proto_error(
|
||||
AnkiError::invalid_input("unrecognized backend input value"),
|
||||
&self.col.i18n,
|
||||
&self.i18n,
|
||||
)
|
||||
.into()
|
||||
};
|
||||
@ -233,12 +247,12 @@ impl Backend {
|
||||
Value::StudiedToday(input) => OValue::StudiedToday(studied_today(
|
||||
input.cards as usize,
|
||||
input.seconds as f32,
|
||||
&self.col.i18n,
|
||||
&self.i18n,
|
||||
)),
|
||||
Value::CongratsLearnMsg(input) => OValue::CongratsLearnMsg(learning_congrats(
|
||||
input.remaining as usize,
|
||||
input.next_due,
|
||||
&self.col.i18n,
|
||||
&self.i18n,
|
||||
)),
|
||||
Value::EmptyTrash(_) => {
|
||||
self.empty_trash()?;
|
||||
@ -253,7 +267,7 @@ impl Backend {
|
||||
|
||||
fn fire_progress_callback(&self, progress: Progress) -> bool {
|
||||
if let Some(cb) = &self.progress_callback {
|
||||
let bytes = progress_to_proto_bytes(progress, &self.col.i18n);
|
||||
let bytes = progress_to_proto_bytes(progress, &self.i18n);
|
||||
cb(bytes)
|
||||
} else {
|
||||
true
|
||||
@ -333,7 +347,7 @@ impl Backend {
|
||||
&input.answer_template,
|
||||
&fields,
|
||||
input.card_ordinal as u16,
|
||||
&self.col.i18n,
|
||||
&self.i18n,
|
||||
)?;
|
||||
|
||||
// return
|
||||
@ -411,7 +425,7 @@ impl Backend {
|
||||
};
|
||||
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, self.col.log.clone()))
|
||||
rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, self.log.clone()))
|
||||
}
|
||||
|
||||
fn check_media(&self) -> Result<pb::MediaCheckOut> {
|
||||
@ -419,7 +433,7 @@ impl Backend {
|
||||
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
|
||||
|
||||
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
||||
self.col.transact(None, |ctx| {
|
||||
self.col.lock().unwrap().transact(None, |ctx| {
|
||||
let mut checker = MediaChecker::new(ctx, &mgr, callback);
|
||||
let mut output = checker.check()?;
|
||||
|
||||
@ -452,7 +466,7 @@ impl Backend {
|
||||
.map(|(k, v)| (k.as_str(), translate_arg_to_fluent_val(&v)))
|
||||
.collect();
|
||||
|
||||
self.col.i18n.trn(key, map)
|
||||
self.i18n.trn(key, map)
|
||||
}
|
||||
|
||||
fn format_time_span(&self, input: pb::FormatTimeSpanIn) -> String {
|
||||
@ -461,14 +475,12 @@ impl Backend {
|
||||
None => return "".to_string(),
|
||||
};
|
||||
match context {
|
||||
pb::format_time_span_in::Context::Precise => {
|
||||
time_span(input.seconds, &self.col.i18n, true)
|
||||
}
|
||||
pb::format_time_span_in::Context::Precise => time_span(input.seconds, &self.i18n, true),
|
||||
pb::format_time_span_in::Context::Intervals => {
|
||||
time_span(input.seconds, &self.col.i18n, false)
|
||||
time_span(input.seconds, &self.i18n, false)
|
||||
}
|
||||
pb::format_time_span_in::Context::AnswerButtons => {
|
||||
answer_button_time(input.seconds, &self.col.i18n)
|
||||
answer_button_time(input.seconds, &self.i18n)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -478,7 +490,7 @@ impl Backend {
|
||||
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
|
||||
|
||||
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
||||
self.col.transact(None, |ctx| {
|
||||
self.col.lock().unwrap().transact(None, |ctx| {
|
||||
let mut checker = MediaChecker::new(ctx, &mgr, callback);
|
||||
|
||||
checker.empty_trash()
|
||||
@ -490,7 +502,7 @@ impl Backend {
|
||||
|progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32));
|
||||
|
||||
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
||||
self.col.transact(None, |ctx| {
|
||||
self.col.lock().unwrap().transact(None, |ctx| {
|
||||
let mut checker = MediaChecker::new(ctx, &mgr, callback);
|
||||
|
||||
checker.restore_trash()
|
||||
@ -498,7 +510,10 @@ impl Backend {
|
||||
}
|
||||
|
||||
pub fn db_command(&self, input: &[u8]) -> Result<String> {
|
||||
db_command_bytes(&self.col.storage.context(self.col.server), input)
|
||||
self.col
|
||||
.lock()
|
||||
.unwrap()
|
||||
.with_ctx(|ctx| db_command_bytes(&ctx.storage, input))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user