melib: cache threads

closes #28
embed
Manos Pitsidianakis 2018-09-07 15:36:42 +03:00
parent 1f49dfae3b
commit 4f715af248
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
8 changed files with 94 additions and 101 deletions

View File

@ -24,7 +24,7 @@ extern crate fnv;
extern crate notify;
extern crate xdg;
use super::{MaildirFolder, MaildirOp};
use super::{MaildirFolder, MaildirOp, NotifyFn};
use async::*;
use conf::AccountSettings;
use error::Result;
@ -125,8 +125,8 @@ impl MailBackend for MaildirType {
fn folders(&self) -> Vec<Folder> {
self.folders.iter().map(|f| f.clone()).collect()
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, folder)
fn get(&mut self, folder: &Folder, notify_fn: Arc<NotifyFn>) -> Async<Result<Vec<Envelope>>> {
self.multicore(4, folder, notify_fn)
}
fn watch(&self, sender: RefreshEventConsumer) -> Result<()> {
let (tx, rx) = channel();
@ -345,7 +345,12 @@ impl MaildirType {
.0
}
pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
pub fn multicore(
&mut self,
cores: usize,
folder: &Folder,
notify_fn: Arc<NotifyFn>,
) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let root_path = self.path.to_path_buf();
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
@ -483,6 +488,7 @@ impl MaildirType {
(*map).insert(e.hash(), y);
}
tx.send(AsyncStatus::Finished);
notify_fn.notify();
Ok(r)
})
.unwrap()

View File

@ -31,6 +31,7 @@ use mailbox::backends::maildir::MaildirType;
use mailbox::email::{Envelope, EnvelopeHash, Flag};
use std::fmt;
use std::fmt::Debug;
use std::sync::Arc;
extern crate fnv;
use self::fnv::FnvHashMap;
@ -117,8 +118,33 @@ impl RefreshEventConsumer {
self.0(r);
}
}
pub struct NotifyFn(Box<Fn() -> ()>);
unsafe impl Send for NotifyFn {}
unsafe impl Sync for NotifyFn {}
impl fmt::Debug for NotifyFn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NotifyFn Box")
}
}
impl From<Box<Fn() -> ()>> for NotifyFn {
fn from(kind: Box<Fn() -> ()>) -> Self {
NotifyFn(kind)
}
}
impl NotifyFn {
pub fn new(b: Box<Fn() -> ()>) -> Self {
NotifyFn(b)
}
pub fn notify(&self) -> () {
self.0();
}
}
pub trait MailBackend: ::std::fmt::Debug {
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
fn get(&mut self, folder: &Folder, notify_fn: Arc<NotifyFn>) -> Async<Result<Vec<Envelope>>>;
fn watch(&self, sender: RefreshEventConsumer) -> Result<()>;
fn folders(&self) -> Vec<Folder>;
fn operation(&self, hash: EnvelopeHash) -> Box<BackendOp>;

View File

@ -1,6 +1,12 @@
extern crate bincode;
extern crate xdg;
use super::*;
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::ops::{Deref, DerefMut};
use std::result;
extern crate fnv;
use self::fnv::FnvHashMap;
@ -15,7 +21,7 @@ pub struct Collection {
}
impl Collection {
pub fn new(vec: Vec<Envelope>) -> Collection {
pub fn new(vec: Vec<Envelope>, name: &str) -> Collection {
let mut envelopes: FnvHashMap<EnvelopeHash, Envelope> =
FnvHashMap::with_capacity_and_hasher(vec.len(), Default::default());
for e in vec {
@ -24,7 +30,19 @@ impl Collection {
let date_index = BTreeMap::new();
let subject_index = None;
let threads = Threads::new(&mut envelopes); // sent_folder);
let cache_dir = xdg::BaseDirectories::with_profile("meli", name).unwrap();
let threads = if let Some(cached) = cache_dir.find_cache_file("threads") {
let reader = io::BufReader::new(fs::File::open(cached).unwrap());
let result: result::Result<Threads, _> = bincode::deserialize_from(reader);
if let Ok(mut cached_t) = result {
cached_t.update(&mut envelopes);
cached_t
} else {
Threads::new(&mut envelopes) // sent_folder);
}
} else {
Threads::new(&mut envelopes) // sent_folder);
};
Collection {
envelopes,
date_index,

View File

@ -71,7 +71,7 @@ impl Mailbox {
) -> Result<Mailbox> {
let mut envelopes: Vec<Envelope> = envelopes?;
envelopes.sort_by(|a, b| a.date().cmp(&b.date()));
let collection = Collection::new(envelopes);
let collection = Collection::new(envelopes, folder.name());
Ok(Mailbox {
folder: (*folder).clone(),
collection,

View File

@ -28,17 +28,18 @@ use mailbox::email::*;
extern crate fnv;
use self::fnv::{FnvHashMap, FnvHashSet};
use std::cell::RefCell;
use std::iter::FromIterator;
use std::ops::Index;
use std::result::Result as StdResult;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Copy)]
#[derive(Debug, Clone, PartialEq, Copy, Deserialize)]
pub enum SortOrder {
Asc,
Desc,
}
#[derive(Debug, Clone, PartialEq, Copy)]
#[derive(Debug, Clone, PartialEq, Copy, Deserialize)]
pub enum SortField {
Subject,
Date,
@ -78,7 +79,7 @@ impl FromStr for SortOrder {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Deserialize)]
pub struct ThreadNode {
message: Option<EnvelopeHash>,
parent: Option<usize>,
@ -157,7 +158,7 @@ impl ThreadNode {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Threads {
thread_nodes: Vec<ThreadNode>,
root_set: Vec<usize>,
@ -226,6 +227,16 @@ impl Threads {
t
}
pub fn update(&mut self, collection: &mut FnvHashMap<EnvelopeHash, Envelope>) {
let new_hash_set = FnvHashSet::from_iter(collection.keys().cloned());
let difference: Vec<EnvelopeHash> =
new_hash_set.difference(&self.hash_set).cloned().collect();
for h in difference {
self.insert(collection.entry(h).or_default());
}
}
pub fn insert(&mut self, envelope: &mut Envelope) {
link_envelope(
&mut self.thread_nodes,

View File

@ -147,7 +147,6 @@ fn main() {
break 'inner; // `goto` 'reap loop, and wait on child.
}
ThreadEvent::UIEvent(UIEventType::StartupCheck) => {
let mut flag = false;
let mut render_flag = false;
for idx_a in 0..state.context.accounts.len() {
let len = state.context.accounts[idx_a].len();
@ -156,16 +155,10 @@ fn main() {
Ok(true) => {
render_flag = true;
},
Ok(false) => {},
Err(_) => {
flag |= true;
}
Ok(false) | Err(_) => {}
}
}
}
if !flag {
state.finish_startup();
}
if render_flag {
state.render();
}

View File

@ -29,14 +29,13 @@
*/
use super::*;
use melib::backends::FolderHash;
use melib::backends::{FolderHash, NotifyFn};
use chan::{Receiver, Sender};
use fnv::FnvHashMap;
use std::io::Write;
use std::result;
use std::thread;
use std::time;
use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen;
use termion::{clear, cursor, style};
@ -122,9 +121,6 @@ pub struct State {
pub mode: UIMode,
entities: Vec<Entity>,
pub context: Context,
startup_thread: Option<chan::Sender<bool>>,
threads: FnvHashMap<thread::ThreadId, (chan::Sender<bool>, thread::JoinHandle<()>)>,
}
@ -174,34 +170,19 @@ impl State {
let mut accounts: Vec<Account> = settings
.accounts
.iter()
.map(|(n, a_s)| Account::new(n.to_string(), a_s.clone(), &backends))
.map(|(n, a_s)| {
let sender = sender.clone();
Account::new(
n.to_string(),
a_s.clone(),
&backends,
NotifyFn::new(Box::new(move || {
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck))
})),
)
})
.collect();
accounts.sort_by(|a, b| a.name().cmp(&b.name()));
let (startup_tx, startup_rx) = chan::async();
let startup_thread = {
let sender = sender.clone();
let startup_rx = startup_rx.clone();
thread::Builder::new()
.name("startup-thread".to_string())
.spawn(move || {
let dur = time::Duration::from_millis(800);
loop {
chan_select! {
default => {},
startup_rx.recv() -> _ => {
sender.send(ThreadEvent::ThreadJoin(thread::current().id()));
break;
}
}
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck));
thread::sleep(dur);
}
startup_rx.recv();
return;
})
.unwrap()
};
let mut s = State {
cols,
rows,
@ -228,13 +209,8 @@ impl State {
tx: input_thread.0,
},
},
startup_thread: Some(startup_tx.clone()),
threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
};
s.threads.insert(
startup_thread.thread().id(),
(startup_tx.clone(), startup_thread),
);
write!(
s.stdout(),
"{}{}{}",
@ -271,34 +247,6 @@ impl State {
event_type: notification,
});
}
let (startup_tx, startup_rx) = chan::async();
let startup_thread = {
let sender = self.context.sender.clone();
let startup_rx = startup_rx.clone();
thread::Builder::new()
.name("startup-thread".to_string())
.spawn(move || {
let dur = time::Duration::from_millis(100);
loop {
chan_select! {
default => {},
startup_rx.recv() -> _ => {
sender.send(ThreadEvent::UIEvent(UIEventType::MailboxUpdate((idxa,idxm))));
sender.send(ThreadEvent::ThreadJoin(thread::current().id()));
return;
}
}
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck));
thread::sleep(dur);
}
})
.expect("Failed to spawn startup-thread in hash_to_folder()")
};
self.startup_thread = Some(startup_tx.clone());
self.threads
.insert(startup_thread.thread().id(), (startup_tx, startup_thread));
} else {
eprintln!(
"BUG: mailbox with hash {} not found in mailbox_hashes.",
@ -315,19 +263,6 @@ impl State {
handle.join().unwrap();
}
/// If startup has finished, inform startup thread that it doesn't need to tick us with startup
/// check reminders and let it die.
pub fn finish_startup(&mut self) {
// TODO: Encode startup process with the type system if possible
if self.startup_thread.is_none() {
return;
}
{
let tx = self.startup_thread.take().unwrap();
tx.send(true);
}
}
/// Switch back to the terminal's main screen (The command line the user sees before opening
/// the application)
pub fn switch_to_main_screen(&mut self) {

View File

@ -26,12 +26,13 @@
use async::*;
use conf::AccountConf;
use mailbox::backends::{
Backends, Folder, MailBackend, RefreshEvent, RefreshEventConsumer, RefreshEventKind,
Backends, Folder, MailBackend, NotifyFn, RefreshEvent, RefreshEventConsumer, RefreshEventKind,
};
use mailbox::*;
use melib::error::Result;
use std::ops::{Index, IndexMut};
use std::result;
use std::sync::Arc;
use types::UIEventType::{self, Notification};
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
@ -48,10 +49,11 @@ pub struct Account {
pub settings: AccountConf,
pub runtime_settings: AccountConf,
pub backend: Box<MailBackend>,
notify_fn: Arc<NotifyFn>,
}
impl Account {
pub fn new(name: String, settings: AccountConf, map: &Backends) -> Self {
pub fn new(name: String, settings: AccountConf, map: &Backends, notify_fn: NotifyFn) -> Self {
let mut backend = map.get(settings.account().format())(settings.account());
let ref_folders: Vec<Folder> = backend.folders();
let mut folders: Vec<Option<Result<Mailbox>>> = Vec::with_capacity(ref_folders.len());
@ -59,9 +61,10 @@ impl Account {
let sent_folder = ref_folders
.iter()
.position(|x: &Folder| x.name() == settings.account().sent_folder);
let notify_fn = Arc::new(notify_fn);
for f in ref_folders {
folders.push(None);
let handle = backend.get(&f);
let handle = backend.get(&f, notify_fn.clone());
workers.push(Some(handle));
}
Account {
@ -72,6 +75,7 @@ impl Account {
settings: settings.clone(),
runtime_settings: settings,
backend,
notify_fn,
}
}
pub fn reload(&mut self, event: RefreshEvent, idx: usize) -> Option<UIEventType> {
@ -99,7 +103,7 @@ impl Account {
}
RefreshEventKind::Rescan => {
let ref_folders: Vec<Folder> = self.backend.folders();
let handle = self.backend.get(&ref_folders[idx]);
let handle = self.backend.get(&ref_folders[idx], self.notify_fn.clone());
self.workers[idx] = Some(handle);
}
}