From 14546c8a8b417347bd448a1b869158c3ceef856b Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 6 Mar 2020 09:22:46 +1000 Subject: [PATCH] wrap the collection in a mutex so DB access is thread safe --- rslib/src/backend/mod.rs | 63 +++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 43b18bb0e..ca51c560f 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -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) -> bool + Send>; pub struct Backend { - col: Collection, + col: Arc>, #[allow(dead_code)] col_path: PathBuf, media_folder: PathBuf, media_db: String, progress_callback: Option, + i18n: I18n, + log: Logger, } enum Progress<'a> { @@ -122,14 +125,21 @@ pub fn init_backend(init_msg: &[u8]) -> std::result::Result { 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 { 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 { @@ -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 { - db_command_bytes(&self.col.storage.context(self.col.server), input) + self.col + .lock() + .unwrap() + .with_ctx(|ctx| db_command_bytes(&ctx.storage, input)) } }