refactor: make run take the callback and avoid boxing

This commit is contained in:
dignifiedquire
2020-01-29 14:19:36 +01:00
parent 29bb2ec58b
commit 41a7e06332
4 changed files with 38 additions and 31 deletions

View File

@@ -30,15 +30,14 @@ fn main() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile); println!("creating database {:?}", dbfile);
let ctx = let ctx = Context::new("FakeOs".into(), dbfile).expect("Failed to create context");
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
let info = ctx.get_info(); let info = ctx.get_info();
let duration = time::Duration::from_millis(8000); let duration = time::Duration::from_millis(8000);
println!("info: {:#?}", info); println!("info: {:#?}", info);
crossbeam::scope(|s| { crossbeam::scope(|s| {
let t1 = s.spawn(|_| { let t1 = s.spawn(|_| {
ctx.run(); ctx.run(cb);
}); });
println!("configuring"); println!("configuring");

View File

@@ -37,7 +37,7 @@ use crate::sql::Sql;
/// * `data2` - Depends on the event parameter, see [Event]. /// * `data2` - Depends on the event parameter, see [Event].
pub type ContextCallback = dyn Fn(&Context, Event) -> () + Send + Sync; pub type ContextCallback = dyn Fn(&Context, Event) -> () + Send + Sync;
#[derive(DebugStub)] #[derive(Debug)]
pub struct Context { pub struct Context {
/// Database file path /// Database file path
dbfile: PathBuf, dbfile: PathBuf,
@@ -61,9 +61,6 @@ pub struct Context {
pub generating_key_mutex: Mutex<()>, pub generating_key_mutex: Mutex<()>,
pub translated_stockstrings: RwLock<HashMap<usize, String>>, pub translated_stockstrings: RwLock<HashMap<usize, String>>,
#[debug_stub = "Callback"]
cb: Box<ContextCallback>,
event_sender: Sender<Event>, event_sender: Sender<Event>,
event_receiver: Receiver<Event>, event_receiver: Receiver<Event>,
shutdown_sender: Sender<()>, shutdown_sender: Sender<()>,
@@ -105,9 +102,7 @@ macro_rules! while_running {
impl Context { impl Context {
/// Creates new context. /// Creates new context.
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> { pub fn new(os_name: String, dbfile: PathBuf) -> Result<Context> {
pretty_env_logger::try_init_timed().ok();
let mut blob_fname = OsString::new(); let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default()); blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs"); blob_fname.push("-blobs");
@@ -115,15 +110,10 @@ impl Context {
if !blobdir.exists() { if !blobdir.exists() {
std::fs::create_dir_all(&blobdir)?; std::fs::create_dir_all(&blobdir)?;
} }
Context::with_blobdir(cb, os_name, dbfile, blobdir) Context::with_blobdir(os_name, dbfile, blobdir)
} }
pub fn with_blobdir( pub fn with_blobdir(os_name: String, dbfile: PathBuf, blobdir: PathBuf) -> Result<Context> {
cb: Box<ContextCallback>,
os_name: String,
dbfile: PathBuf,
blobdir: PathBuf,
) -> Result<Context> {
ensure!( ensure!(
blobdir.is_dir(), blobdir.is_dir(),
"Blobdir does not exist: {}", "Blobdir does not exist: {}",
@@ -136,7 +126,6 @@ impl Context {
let ctx = Context { let ctx = Context {
blobdir, blobdir,
dbfile, dbfile,
cb,
os_name: Some(os_name), os_name: Some(os_name),
running_state: Arc::new(RwLock::new(Default::default())), running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(), sql: Sql::new(),
@@ -195,9 +184,13 @@ impl Context {
} }
/// Start the run loop. /// Start the run loop.
pub fn run(&self) { pub fn run<F>(&self, cb: F)
use crossbeam::channel::select; where
F: Fn(&Context, Event) -> () + Send + Sync,
{
// TODO: ensure this can be only called once.
use crossbeam::channel::select;
self.is_running.store(true, Ordering::Relaxed); self.is_running.store(true, Ordering::Relaxed);
crossbeam::scope(|s| { crossbeam::scope(|s| {
@@ -240,7 +233,7 @@ impl Context {
recv(self.event_receiver) -> event => { recv(self.event_receiver) -> event => {
// This gurantees that the callback is always called from the thread // This gurantees that the callback is always called from the thread
// that called `run`. // that called `run`.
(*self.cb)(self, event.unwrap()) cb(self, event.unwrap())
}, },
recv(self.shutdown_receiver) -> _ => break, recv(self.shutdown_receiver) -> _ => break,
} }
@@ -639,7 +632,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap(); std::fs::write(&dbfile, b"123").unwrap();
let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile); let res = Context::new("FakeOs".into(), dbfile);
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -654,7 +647,7 @@ mod tests {
fn test_blobdir_exists() { fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).unwrap(); Context::new("FakeOS".into(), dbfile).unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir()); assert!(blobdir.is_dir());
} }
@@ -665,7 +658,7 @@ mod tests {
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap(); std::fs::write(&blobdir, b"123").unwrap();
let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile); let res = Context::new("FakeOS".into(), dbfile);
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -675,7 +668,7 @@ mod tests {
let subdir = tmp.path().join("subdir"); let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite"); let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone(); let dbfile2 = dbfile.clone();
Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).unwrap(); Context::new("FakeOS".into(), dbfile).unwrap();
assert!(subdir.is_dir()); assert!(subdir.is_dir());
assert!(dbfile2.is_file()); assert!(dbfile2.is_file());
} }
@@ -685,7 +678,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new(); let blobdir = PathBuf::new();
let res = Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir); let res = Context::with_blobdir("FakeOS".into(), dbfile, blobdir);
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -694,7 +687,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs"); let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir); let res = Context::with_blobdir("FakeOS".into(), dbfile, blobdir);
assert!(res.is_err()); assert!(res.is_err());
} }

View File

@@ -2,6 +2,8 @@
//! //!
//! This module is only compiled for test runs. //! This module is only compiled for test runs.
use std::sync::Arc;
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
use crate::config::Config; use crate::config::Config;
@@ -15,7 +17,7 @@ use crate::key;
/// The temporary directory can be used to store the SQLite database, /// The temporary directory can be used to store the SQLite database,
/// see e.g. [test_context] which does this. /// see e.g. [test_context] which does this.
pub struct TestContext { pub struct TestContext {
pub ctx: Context, pub ctx: Arc<Context>,
pub dir: TempDir, pub dir: TempDir,
} }
@@ -32,7 +34,13 @@ pub fn test_context(callback: Option<Box<ContextCallback>>) -> TestContext {
Some(cb) => cb, Some(cb) => cb,
None => Box::new(|_, _| ()), None => Box::new(|_, _| ()),
}; };
let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap(); let ctx = Arc::new(Context::new("FakeOs".into(), dbfile).unwrap());
let ctx_1 = ctx.clone();
std::thread::spawn(move || {
ctx_1.run(|context, event| (*cb)(context, event));
});
TestContext { ctx, dir } TestContext { ctx, dir }
} }

View File

@@ -1,6 +1,7 @@
//! Stress some functions for testing; if used as a lib, this file is obsolete. //! Stress some functions for testing; if used as a lib, this file is obsolete.
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Arc;
use deltachat::config; use deltachat::config;
use deltachat::context::*; use deltachat::context::*;
@@ -207,14 +208,20 @@ fn cb(_context: &Context, _event: Event) {}
#[allow(dead_code)] #[allow(dead_code)]
struct TestContext { struct TestContext {
ctx: Context, ctx: Arc<Context>,
dir: TempDir, dir: TempDir,
} }
fn create_test_context() -> TestContext { fn create_test_context() -> TestContext {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap(); let ctx = Arc::new(Context::new("FakeOs".into(), dbfile).unwrap());
let ctx_1 = ctx.clone();
std::thread::spawn(move || {
ctx_1.run(cb);
});
TestContext { ctx, dir } TestContext { ctx, dir }
} }