Replace old pseudo-async code with blocking rust async
parent
a190805384
commit
3eadaba34e
|
@ -1,255 +0,0 @@
|
||||||
/*
|
|
||||||
* meli - async module
|
|
||||||
*
|
|
||||||
* Copyright 2017 Manos Pitsidianakis
|
|
||||||
*
|
|
||||||
* This file is part of meli.
|
|
||||||
*
|
|
||||||
* meli is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* meli is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Primitive Async/Wait implementation.
|
|
||||||
*
|
|
||||||
* To create an Async promise, create an AsyncBuilder. Ask for its channel receiver/sender with
|
|
||||||
* `tx` and `rx` methods to pass them in your worker's closure. Build an `Async<T>` with your
|
|
||||||
* `JoinHandle<T>`. The thread must communicate with the `Async<T>` object via `AsyncStatus`
|
|
||||||
* messages.
|
|
||||||
*
|
|
||||||
* When `Async<T>` receives `AsyncStatus::Finished` it joins the thread and takes its value which
|
|
||||||
* can be extracted with `extract`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crossbeam::{
|
|
||||||
bounded,
|
|
||||||
channel::{Receiver, Sender},
|
|
||||||
select,
|
|
||||||
};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct WorkContext {
|
|
||||||
pub new_work: Sender<Work>,
|
|
||||||
pub set_name: Sender<(std::thread::ThreadId, String)>,
|
|
||||||
pub set_status: Sender<(std::thread::ThreadId, String)>,
|
|
||||||
pub finished: Sender<std::thread::ThreadId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Work {
|
|
||||||
priority: u64,
|
|
||||||
pub is_static: bool,
|
|
||||||
pub closure: Box<dyn FnOnce(WorkContext) -> () + Send + Sync>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Work {
|
|
||||||
fn cmp(&self, other: &Work) -> std::cmp::Ordering {
|
|
||||||
self.priority.cmp(&other.priority)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Work {
|
|
||||||
fn partial_cmp(&self, other: &Work) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.priority.cmp(&other.priority))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Work {
|
|
||||||
fn eq(&self, other: &Work) -> bool {
|
|
||||||
self.priority == other.priority
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Work {}
|
|
||||||
|
|
||||||
impl Work {
|
|
||||||
pub fn compute(self, work_context: WorkContext) {
|
|
||||||
(self.closure)(work_context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Work {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Work object")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Messages to pass between `Async<T>` owner and its worker thread.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum AsyncStatus<T> {
|
|
||||||
NoUpdate,
|
|
||||||
Payload(T),
|
|
||||||
Finished,
|
|
||||||
///The number may hold whatever meaning the user chooses.
|
|
||||||
ProgressReport(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> fmt::Debug for AsyncStatus<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
AsyncStatus::NoUpdate => write!(f, "AsyncStatus<T>::NoUpdate"),
|
|
||||||
AsyncStatus::Payload(_) => write!(f, "AsyncStatus<T>::Payload(_)"),
|
|
||||||
AsyncStatus::Finished => write!(f, "AsyncStatus<T>::Finished"),
|
|
||||||
AsyncStatus::ProgressReport(u) => write!(f, "AsyncStatus<T>::ProgressReport({})", u),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A builder object for `Async<T>`
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct AsyncBuilder<T: Send + Sync> {
|
|
||||||
tx: Sender<AsyncStatus<T>>,
|
|
||||||
rx: Receiver<AsyncStatus<T>>,
|
|
||||||
priority: u64,
|
|
||||||
is_static: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Async<T: Send + Sync> {
|
|
||||||
work: Option<Work>,
|
|
||||||
active: bool,
|
|
||||||
tx: Sender<AsyncStatus<T>>,
|
|
||||||
rx: Receiver<AsyncStatus<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Send + Sync> Default for AsyncBuilder<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
AsyncBuilder::<T>::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsyncBuilder<T>
|
|
||||||
where
|
|
||||||
T: Send + Sync,
|
|
||||||
{
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let (sender, receiver) = bounded(8 * ::std::mem::size_of::<AsyncStatus<T>>());
|
|
||||||
AsyncBuilder {
|
|
||||||
tx: sender,
|
|
||||||
rx: receiver,
|
|
||||||
priority: 0,
|
|
||||||
is_static: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Returns the sender object of the promise's channel.
|
|
||||||
pub fn tx(&mut self) -> Sender<AsyncStatus<T>> {
|
|
||||||
self.tx.clone()
|
|
||||||
}
|
|
||||||
/// Returns the receiver object of the promise's channel.
|
|
||||||
pub fn rx(&mut self) -> Receiver<AsyncStatus<T>> {
|
|
||||||
self.rx.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_priority(&mut self, new_val: u64) -> &mut Self {
|
|
||||||
self.priority = new_val;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_is_static(&mut self, new_val: bool) -> &mut Self {
|
|
||||||
self.is_static = new_val;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `Async<T>` object that contains a `Thread` join handle that returns a `T`
|
|
||||||
pub fn build(self, work: Box<dyn FnOnce(WorkContext) -> () + Send + Sync>) -> Async<T> {
|
|
||||||
Async {
|
|
||||||
work: Some(Work {
|
|
||||||
priority: self.priority,
|
|
||||||
is_static: self.is_static,
|
|
||||||
closure: work,
|
|
||||||
}),
|
|
||||||
tx: self.tx,
|
|
||||||
rx: self.rx,
|
|
||||||
active: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Async<T>
|
|
||||||
where
|
|
||||||
T: Send + Sync,
|
|
||||||
{
|
|
||||||
pub fn work(&mut self) -> Option<Work> {
|
|
||||||
if !self.active {
|
|
||||||
self.active = true;
|
|
||||||
self.work.take()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Returns the sender object of the promise's channel.
|
|
||||||
pub fn tx(&mut self) -> Sender<AsyncStatus<T>> {
|
|
||||||
self.tx.clone()
|
|
||||||
}
|
|
||||||
/// Returns the receiver object of the promise's channel.
|
|
||||||
pub fn rx(&mut self) -> Receiver<AsyncStatus<T>> {
|
|
||||||
self.rx.clone()
|
|
||||||
}
|
|
||||||
/// Polls worker thread and returns result.
|
|
||||||
pub fn poll_block(&mut self) -> Result<AsyncStatus<T>, ()> {
|
|
||||||
if !self.active {
|
|
||||||
return Ok(AsyncStatus::Finished);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rx = &self.rx;
|
|
||||||
select! {
|
|
||||||
recv(rx) -> r => {
|
|
||||||
match r {
|
|
||||||
Ok(p @ AsyncStatus::Payload(_)) => {
|
|
||||||
Ok(p)
|
|
||||||
},
|
|
||||||
Ok(f @ AsyncStatus::Finished) => {
|
|
||||||
self.active = false;
|
|
||||||
Ok(f)
|
|
||||||
},
|
|
||||||
Ok(a) => {
|
|
||||||
Ok(a)
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Polls worker thread and returns result.
|
|
||||||
pub fn poll(&mut self) -> Result<AsyncStatus<T>, ()> {
|
|
||||||
if !self.active {
|
|
||||||
return Ok(AsyncStatus::Finished);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rx = &self.rx;
|
|
||||||
select! {
|
|
||||||
default => {
|
|
||||||
Ok(AsyncStatus::NoUpdate)
|
|
||||||
},
|
|
||||||
recv(rx) -> r => {
|
|
||||||
match r {
|
|
||||||
Ok(p @ AsyncStatus::Payload(_)) => {
|
|
||||||
Ok(p)
|
|
||||||
},
|
|
||||||
Ok(f @ AsyncStatus::Finished) => {
|
|
||||||
self.active = false;
|
|
||||||
Ok(f)
|
|
||||||
},
|
|
||||||
Ok(a) => {
|
|
||||||
Ok(a)
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -49,7 +49,6 @@ pub mod mbox;
|
||||||
pub use self::imap::ImapType;
|
pub use self::imap::ImapType;
|
||||||
#[cfg(feature = "imap_backend")]
|
#[cfg(feature = "imap_backend")]
|
||||||
pub use self::nntp::NntpType;
|
pub use self::nntp::NntpType;
|
||||||
use crate::async_workers::*;
|
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::error::{MeliError, Result};
|
use crate::error::{MeliError, Result};
|
||||||
|
|
||||||
|
@ -304,31 +303,23 @@ pub type ResultFuture<T> = Result<Pin<Box<dyn Future<Output = Result<T>> + Send
|
||||||
|
|
||||||
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||||
fn capabilities(&self) -> MailBackendCapabilities;
|
fn capabilities(&self) -> MailBackendCapabilities;
|
||||||
fn is_online(&self) -> Result<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn is_online_async(&self) -> ResultFuture<()> {
|
//fn fetch(&mut self, mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>>;
|
||||||
Err(MeliError::new("Unimplemented."))
|
fn fetch(
|
||||||
}
|
|
||||||
fn fetch(&mut self, mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>>;
|
|
||||||
fn fetch_async(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
_mailbox_hash: MailboxHash,
|
_mailbox_hash: MailboxHash,
|
||||||
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<()>> {
|
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn refresh_async(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn watch(&self, work_context: WorkContext) -> Result<std::thread::ThreadId>;
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
fn watch_async(&self) -> ResultFuture<()> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>>;
|
|
||||||
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>>;
|
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>>;
|
||||||
|
|
|
@ -36,11 +36,11 @@ mod cache;
|
||||||
pub mod managesieve;
|
pub mod managesieve;
|
||||||
mod untagged;
|
mod untagged;
|
||||||
|
|
||||||
use crate::async_workers::{Async, WorkContext};
|
|
||||||
use crate::backends::{
|
use crate::backends::{
|
||||||
RefreshEventKind::{self, *},
|
RefreshEventKind::{self, *},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::connections::timeout;
|
use crate::connections::timeout;
|
||||||
use crate::email::*;
|
use crate::email::*;
|
||||||
|
@ -260,7 +260,7 @@ impl MailBackend for ImapType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_async(
|
fn fetch(
|
||||||
&mut self,
|
&mut self,
|
||||||
mailbox_hash: MailboxHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
||||||
|
@ -289,7 +289,7 @@ impl MailBackend for ImapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_async(&mut self, mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
fn refresh(&mut self, mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
||||||
let main_conn = self.connection.clone();
|
let main_conn = self.connection.clone();
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
|
@ -304,7 +304,7 @@ impl MailBackend for ImapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
|
@ -356,12 +356,12 @@ impl MailBackend for ImapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_online_async(&self) -> ResultFuture<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
match timeout(std::time::Duration::from_secs(3), connection.lock()).await {
|
match timeout(std::time::Duration::from_secs(3), connection.lock()).await {
|
||||||
Ok(mut conn) => {
|
Ok(mut conn) => {
|
||||||
debug!("is_online_async");
|
debug!("is_online");
|
||||||
match debug!(timeout(std::time::Duration::from_secs(3), conn.connect()).await) {
|
match debug!(timeout(std::time::Duration::from_secs(3), conn.connect()).await) {
|
||||||
Ok(Ok(())) => Ok(()),
|
Ok(Ok(())) => Ok(()),
|
||||||
Err(err) | Ok(Err(err)) => {
|
Err(err) | Ok(Err(err)) => {
|
||||||
|
@ -375,20 +375,7 @@ impl MailBackend for ImapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<()>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn watch(&self, _work_context: WorkContext) -> Result<std::thread::ThreadId> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn watch_async(&self) -> ResultFuture<()> {
|
|
||||||
debug!("watch_async called");
|
|
||||||
let conn = ImapConnection::new_connection(&self.server_conf, self.uid_store.clone());
|
let conn = ImapConnection::new_connection(&self.server_conf, self.uid_store.clone());
|
||||||
let main_conn = self.connection.clone();
|
let main_conn = self.connection.clone();
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
|
@ -417,15 +404,11 @@ impl MailBackend for ImapType {
|
||||||
} else {
|
} else {
|
||||||
poll_with_examine(kit).await?;
|
poll_with_examine(kit).await?;
|
||||||
}
|
}
|
||||||
debug!("watch_async future returning");
|
debug!("watch future returning");
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
||||||
let (uid, mailbox_hash) = if let Some(v) =
|
let (uid, mailbox_hash) = if let Some(v) =
|
||||||
self.uid_store.hash_index.lock().unwrap().get(&hash)
|
self.uid_store.hash_index.lock().unwrap().get(&hash)
|
||||||
|
@ -748,7 +731,7 @@ impl MailBackend for ImapType {
|
||||||
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
|
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
let new_mailbox_fut = self.mailboxes_async();
|
let new_mailbox_fut = self.mailboxes();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
/* Must transform path to something the IMAP server will accept
|
/* Must transform path to something the IMAP server will accept
|
||||||
*
|
*
|
||||||
|
@ -819,7 +802,7 @@ impl MailBackend for ImapType {
|
||||||
) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
let new_mailbox_fut = self.mailboxes_async();
|
let new_mailbox_fut = self.mailboxes();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
let imap_path: String;
|
let imap_path: String;
|
||||||
let no_select: bool;
|
let no_select: bool;
|
||||||
|
@ -923,7 +906,7 @@ impl MailBackend for ImapType {
|
||||||
) -> ResultFuture<Mailbox> {
|
) -> ResultFuture<Mailbox> {
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
let new_mailbox_fut = self.mailboxes_async();
|
let new_mailbox_fut = self.mailboxes();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
let command: String;
|
let command: String;
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::async_workers::{Async, WorkContext};
|
|
||||||
use crate::backends::*;
|
use crate::backends::*;
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::*;
|
use crate::email::*;
|
||||||
|
@ -207,7 +206,7 @@ impl MailBackend for JmapType {
|
||||||
CAPABILITIES
|
CAPABILITIES
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_online_async(&self) -> ResultFuture<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
let online = self.online.clone();
|
let online = self.online.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
//match timeout(std::time::Duration::from_secs(3), connection.lock()).await {
|
//match timeout(std::time::Duration::from_secs(3), connection.lock()).await {
|
||||||
|
@ -221,7 +220,7 @@ impl MailBackend for JmapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_async(
|
fn fetch(
|
||||||
&mut self,
|
&mut self,
|
||||||
mailbox_hash: MailboxHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
||||||
|
@ -243,13 +242,13 @@ impl MailBackend for JmapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch_async(&self) -> ResultFuture<()> {
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
Err(MeliError::from("JMAP watch for updates is unimplemented"))
|
Err(MeliError::from("JMAP watch for updates is unimplemented"))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
let mailboxes = self.mailboxes.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
|
@ -354,18 +353,6 @@ impl MailBackend for JmapType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn watch(&self, _work_context: WorkContext) -> Result<std::thread::ThreadId> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rename_mailbox(
|
fn rename_mailbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
_mailbox_hash: MailboxHash,
|
_mailbox_hash: MailboxHash,
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{MaildirMailbox, MaildirOp, MaildirPathTrait};
|
use super::{MaildirMailbox, MaildirOp, MaildirPathTrait};
|
||||||
use crate::async_workers::*;
|
|
||||||
use crate::backends::{RefreshEventKind::*, *};
|
use crate::backends::{RefreshEventKind::*, *};
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||||
|
@ -40,10 +39,8 @@ use std::io::{self, Read, Write};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::result;
|
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(super) enum PathMod {
|
pub(super) enum PathMod {
|
||||||
|
@ -188,31 +185,20 @@ impl MailBackend for MaildirType {
|
||||||
CAPABILITIES
|
CAPABILITIES
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_online(&self) -> Result<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_online_async(&self) -> ResultFuture<()> {
|
|
||||||
Ok(Box::pin(async { Ok(()) }))
|
Ok(Box::pin(async { Ok(()) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
Ok(self
|
let res = Ok(self
|
||||||
.mailboxes
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(h, f)| (*h, BackendMailbox::clone(f)))
|
.map(|(h, f)| (*h, BackendMailbox::clone(f)))
|
||||||
.collect())
|
.collect());
|
||||||
}
|
|
||||||
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
let res = self.mailboxes();
|
|
||||||
Ok(Box::pin(async { res }))
|
Ok(Box::pin(async { res }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
|
fn fetch(
|
||||||
Ok(self.multicore(4, mailbox_hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_async(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
mailbox_hash: MailboxHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> Result<core::pin::Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>>
|
) -> Result<core::pin::Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>>
|
||||||
|
@ -224,14 +210,19 @@ impl MailBackend for MaildirType {
|
||||||
let root_path = self.path.to_path_buf();
|
let root_path = self.path.to_path_buf();
|
||||||
let map = self.hash_indexes.clone();
|
let map = self.hash_indexes.clone();
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
super::stream::MaildirStream::new(&self.name, mailbox_hash, unseen, total, path, root_path, map, mailbox_index)
|
super::stream::MaildirStream::new(
|
||||||
|
&self.name,
|
||||||
|
mailbox_hash,
|
||||||
|
unseen,
|
||||||
|
total,
|
||||||
|
path,
|
||||||
|
root_path,
|
||||||
|
map,
|
||||||
|
mailbox_index,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(
|
fn refresh(&mut self, mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
||||||
&mut self,
|
|
||||||
mailbox_hash: MailboxHash,
|
|
||||||
) -> Result<Async<()>> {
|
|
||||||
let w = AsyncBuilder::new();
|
|
||||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||||
let account_hash = {
|
let account_hash = {
|
||||||
let mut hasher = DefaultHasher::default();
|
let mut hasher = DefaultHasher::default();
|
||||||
|
@ -240,117 +231,116 @@ impl MailBackend for MaildirType {
|
||||||
};
|
};
|
||||||
let sender = self.event_consumer.clone();
|
let sender = self.event_consumer.clone();
|
||||||
|
|
||||||
let handle = {
|
let mailbox: &MaildirMailbox = &self.mailboxes[&mailbox_hash];
|
||||||
let mailbox: &MaildirMailbox = &self.mailboxes[&mailbox_hash];
|
let path: PathBuf = mailbox.fs_path().into();
|
||||||
let path: PathBuf = mailbox.fs_path().into();
|
let root_path = self.path.to_path_buf();
|
||||||
let name = format!("refresh {:?}", mailbox.name());
|
let map = self.hash_indexes.clone();
|
||||||
let root_path = self.path.to_path_buf();
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
let map = self.hash_indexes.clone();
|
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
|
||||||
|
|
||||||
Box::new(move |work_context: crate::async_workers::WorkContext| {
|
Ok(Box::pin(async move {
|
||||||
work_context
|
let thunk = move |sender: &BackendEventConsumer| {
|
||||||
.set_name
|
debug!("refreshing");
|
||||||
.send((std::thread::current().id(), name.clone()))
|
let mut path = path.clone();
|
||||||
.unwrap();
|
path.push("new");
|
||||||
let thunk = move |sender: &BackendEventConsumer| {
|
for d in path.read_dir()? {
|
||||||
debug!("refreshing");
|
if let Ok(p) = d {
|
||||||
let mut path = path.clone();
|
move_to_cur(p.path()).ok().take();
|
||||||
path.push("new");
|
|
||||||
for d in path.read_dir()? {
|
|
||||||
if let Ok(p) = d {
|
|
||||||
move_to_cur(p.path()).ok().take();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
path.pop();
|
}
|
||||||
|
path.pop();
|
||||||
|
|
||||||
path.push("cur");
|
path.push("cur");
|
||||||
let iter = path.read_dir()?;
|
let iter = path.read_dir()?;
|
||||||
let count = path.read_dir()?.count();
|
let count = path.read_dir()?.count();
|
||||||
let mut files: Vec<PathBuf> = Vec::with_capacity(count);
|
let mut files: Vec<PathBuf> = Vec::with_capacity(count);
|
||||||
for e in iter {
|
for e in iter {
|
||||||
let e = e.and_then(|x| {
|
let e = e.and_then(|x| {
|
||||||
let path = x.path();
|
let path = x.path();
|
||||||
Ok(path)
|
Ok(path)
|
||||||
})?;
|
})?;
|
||||||
files.push(e);
|
files.push(e);
|
||||||
}
|
}
|
||||||
let mut current_hashes = {
|
let mut current_hashes = {
|
||||||
|
let mut map = map.lock().unwrap();
|
||||||
|
let map = map.entry(mailbox_hash).or_default();
|
||||||
|
map.keys().cloned().collect::<HashSet<EnvelopeHash>>()
|
||||||
|
};
|
||||||
|
for file in files {
|
||||||
|
let hash = get_file_hash(&file);
|
||||||
|
{
|
||||||
let mut map = map.lock().unwrap();
|
let mut map = map.lock().unwrap();
|
||||||
let map = map.entry(mailbox_hash).or_default();
|
let map = map.entry(mailbox_hash).or_default();
|
||||||
map.keys().cloned().collect::<HashSet<EnvelopeHash>>()
|
if map.contains_key(&hash) {
|
||||||
};
|
map.remove(&hash);
|
||||||
for file in files {
|
current_hashes.remove(&hash);
|
||||||
let hash = get_file_hash(&file);
|
continue;
|
||||||
{
|
|
||||||
let mut map = map.lock().unwrap();
|
|
||||||
let map = map.entry(mailbox_hash).or_default();
|
|
||||||
if map.contains_key(&hash) {
|
|
||||||
map.remove(&hash);
|
|
||||||
current_hashes.remove(&hash);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(*map).insert(hash, PathBuf::from(&file).into());
|
|
||||||
}
|
}
|
||||||
let op = Box::new(MaildirOp::new(hash, map.clone(), mailbox_hash));
|
(*map).insert(hash, PathBuf::from(&file).into());
|
||||||
if let Ok(e) = Envelope::from_token(op, hash) {
|
}
|
||||||
mailbox_index.lock().unwrap().insert(e.hash(), mailbox_hash);
|
let op = Box::new(MaildirOp::new(hash, map.clone(), mailbox_hash));
|
||||||
let file_name = file.strip_prefix(&root_path).unwrap().to_path_buf();
|
if let Ok(e) = Envelope::from_token(op, hash) {
|
||||||
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
|
mailbox_index.lock().unwrap().insert(e.hash(), mailbox_hash);
|
||||||
/* place result in cache directory */
|
let file_name = file.strip_prefix(&root_path).unwrap().to_path_buf();
|
||||||
let f = match fs::File::create(cached) {
|
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
|
||||||
Ok(f) => f,
|
/* place result in cache directory */
|
||||||
Err(e) => {
|
let f = match fs::File::create(cached) {
|
||||||
panic!("{}", e);
|
Ok(f) => f,
|
||||||
}
|
Err(e) => {
|
||||||
};
|
panic!("{}", e);
|
||||||
let metadata = f.metadata().unwrap();
|
}
|
||||||
let mut permissions = metadata.permissions();
|
};
|
||||||
|
let metadata = f.metadata().unwrap();
|
||||||
|
let mut permissions = metadata.permissions();
|
||||||
|
|
||||||
permissions.set_mode(0o600); // Read/write for owner only.
|
permissions.set_mode(0o600); // Read/write for owner only.
|
||||||
f.set_permissions(permissions).unwrap();
|
f.set_permissions(permissions).unwrap();
|
||||||
|
|
||||||
let writer = io::BufWriter::new(f);
|
let writer = io::BufWriter::new(f);
|
||||||
bincode::serialize_into(writer, &e).unwrap();
|
bincode::serialize_into(writer, &e).unwrap();
|
||||||
}
|
}
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
kind: Create(Box::new(e)),
|
kind: Create(Box::new(e)),
|
||||||
}));
|
}),
|
||||||
} else {
|
);
|
||||||
debug!(
|
} else {
|
||||||
"DEBUG: hash {}, path: {} couldn't be parsed",
|
debug!(
|
||||||
hash,
|
"DEBUG: hash {}, path: {} couldn't be parsed",
|
||||||
file.as_path().display()
|
hash,
|
||||||
);
|
file.as_path().display()
|
||||||
continue;
|
);
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
for ev in current_hashes.into_iter().map(|h| BackendEvent::Refresh(RefreshEvent {
|
}
|
||||||
|
for ev in current_hashes.into_iter().map(|h| {
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
kind: Remove(h),
|
kind: Remove(h),
|
||||||
})) {
|
})
|
||||||
(sender)(account_hash, ev);
|
}) {
|
||||||
}
|
(sender)(account_hash, ev);
|
||||||
Ok(())
|
}
|
||||||
};
|
Ok(())
|
||||||
if let Err(err) = thunk(&sender) {
|
};
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
if let Err(err) = thunk(&sender) {
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
kind: Failure(err),
|
kind: Failure(err),
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
})
|
}
|
||||||
};
|
Ok(())
|
||||||
Ok(w.build(handle))
|
}))
|
||||||
}
|
}
|
||||||
fn watch(
|
|
||||||
&self,
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
work_context: WorkContext,
|
|
||||||
) -> Result<std::thread::ThreadId> {
|
|
||||||
let sender = self.event_consumer.clone();
|
let sender = self.event_consumer.clone();
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
|
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
|
||||||
|
@ -365,335 +355,371 @@ impl MailBackend for MaildirType {
|
||||||
debug!("watching {:?}", root_path);
|
debug!("watching {:?}", root_path);
|
||||||
let hash_indexes = self.hash_indexes.clone();
|
let hash_indexes = self.hash_indexes.clone();
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
let root_mailbox_hash: MailboxHash = self.mailboxes.values().find(|m| m.parent.is_none()).map(|m| m.hash()).unwrap();
|
let root_mailbox_hash: MailboxHash = self
|
||||||
|
.mailboxes
|
||||||
|
.values()
|
||||||
|
.find(|m| m.parent.is_none())
|
||||||
|
.map(|m| m.hash())
|
||||||
|
.unwrap();
|
||||||
let mailbox_counts = self
|
let mailbox_counts = self
|
||||||
.mailboxes
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(&k, v)| (k, (v.unseen.clone(), v.total.clone())))
|
.map(|(&k, v)| (k, (v.unseen.clone(), v.total.clone())))
|
||||||
.collect::<HashMap<MailboxHash, (Arc<Mutex<usize>>, Arc<Mutex<usize>>)>>();
|
.collect::<HashMap<MailboxHash, (Arc<Mutex<usize>>, Arc<Mutex<usize>>)>>();
|
||||||
let handle = thread::Builder::new()
|
Ok(Box::pin(async move {
|
||||||
.name("mailbox watch".to_string())
|
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
||||||
.spawn(move || {
|
let _watcher = watcher;
|
||||||
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
loop {
|
||||||
let _watcher = watcher;
|
match rx.recv() {
|
||||||
let _work_context = work_context;
|
/*
|
||||||
loop {
|
* Event types:
|
||||||
match rx.recv() {
|
*
|
||||||
/*
|
* pub enum RefreshEventKind {
|
||||||
* Event types:
|
* Update(EnvelopeHash, Envelope), // Old hash, new envelope
|
||||||
*
|
* Create(Envelope),
|
||||||
* pub enum RefreshEventKind {
|
* Remove(EnvelopeHash),
|
||||||
* Update(EnvelopeHash, Envelope), // Old hash, new envelope
|
* Rescan,
|
||||||
* Create(Envelope),
|
* }
|
||||||
* Remove(EnvelopeHash),
|
*/
|
||||||
* Rescan,
|
Ok(event) => match event {
|
||||||
* }
|
/* Create */
|
||||||
*/
|
DebouncedEvent::Create(mut pathbuf) => {
|
||||||
Ok(event) => match event {
|
debug!("DebouncedEvent::Create(path = {:?}", pathbuf);
|
||||||
/* Create */
|
if path_is_new!(pathbuf) {
|
||||||
DebouncedEvent::Create(mut pathbuf) => {
|
debug!("path_is_new");
|
||||||
debug!("DebouncedEvent::Create(path = {:?}", pathbuf);
|
/* This creates a Rename event that we will receive later */
|
||||||
if path_is_new!(pathbuf) {
|
pathbuf = match move_to_cur(pathbuf) {
|
||||||
debug!("path_is_new");
|
Ok(p) => p,
|
||||||
/* This creates a Rename event that we will receive later */
|
Err(e) => {
|
||||||
pathbuf = match move_to_cur(pathbuf) {
|
debug!("error: {}", e.to_string());
|
||||||
Ok(p) => p,
|
continue;
|
||||||
Err(e) => {
|
}
|
||||||
debug!("error: {}", e.to_string());
|
};
|
||||||
continue;
|
}
|
||||||
}
|
let mailbox_hash = get_path_hash!(pathbuf);
|
||||||
};
|
let file_name = pathbuf
|
||||||
|
.as_path()
|
||||||
|
.strip_prefix(&root_path)
|
||||||
|
.unwrap()
|
||||||
|
.to_path_buf();
|
||||||
|
if let Some(env) = add_path_to_index(
|
||||||
|
&hash_indexes,
|
||||||
|
mailbox_hash,
|
||||||
|
pathbuf.as_path(),
|
||||||
|
&cache_dir,
|
||||||
|
file_name,
|
||||||
|
) {
|
||||||
|
mailbox_index
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(env.hash(), mailbox_hash);
|
||||||
|
debug!(
|
||||||
|
"Create event {} {} {}",
|
||||||
|
env.hash(),
|
||||||
|
env.subject(),
|
||||||
|
pathbuf.display()
|
||||||
|
);
|
||||||
|
if !env.is_seen() {
|
||||||
|
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
let mailbox_hash = get_path_hash!(pathbuf);
|
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
||||||
let file_name = pathbuf
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: Create(Box::new(env)),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Update */
|
||||||
|
DebouncedEvent::NoticeWrite(pathbuf) | DebouncedEvent::Write(pathbuf) => {
|
||||||
|
debug!("DebouncedEvent::Write(path = {:?}", &pathbuf);
|
||||||
|
let mailbox_hash = get_path_hash!(pathbuf);
|
||||||
|
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||||
|
let index_lock =
|
||||||
|
&mut hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||||
|
let file_name = pathbuf
|
||||||
|
.as_path()
|
||||||
|
.strip_prefix(&root_path)
|
||||||
|
.unwrap()
|
||||||
|
.to_path_buf();
|
||||||
|
/* Linear search in hash_index to find old hash */
|
||||||
|
let old_hash: EnvelopeHash = {
|
||||||
|
if let Some((k, v)) =
|
||||||
|
index_lock.iter_mut().find(|(_, v)| *v.buf == pathbuf)
|
||||||
|
{
|
||||||
|
//TODO FIXME This doesn't make sense?
|
||||||
|
*v = pathbuf.clone().into();
|
||||||
|
*k
|
||||||
|
} else {
|
||||||
|
drop(hash_indexes_lock);
|
||||||
|
/* Did we just miss a Create event? In any case, create
|
||||||
|
* envelope. */
|
||||||
|
if let Some(env) = add_path_to_index(
|
||||||
|
&hash_indexes,
|
||||||
|
mailbox_hash,
|
||||||
|
pathbuf.as_path(),
|
||||||
|
&cache_dir,
|
||||||
|
file_name,
|
||||||
|
) {
|
||||||
|
mailbox_index
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(env.hash(), mailbox_hash);
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: Create(Box::new(env)),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let new_hash: EnvelopeHash = get_file_hash(pathbuf.as_path());
|
||||||
|
if index_lock.get_mut(&new_hash).is_none() {
|
||||||
|
debug!("write notice");
|
||||||
|
let op = Box::new(MaildirOp::new(
|
||||||
|
new_hash,
|
||||||
|
hash_indexes.clone(),
|
||||||
|
mailbox_hash,
|
||||||
|
));
|
||||||
|
if let Ok(env) = Envelope::from_token(op, new_hash) {
|
||||||
|
debug!("{}\t{:?}", new_hash, &pathbuf);
|
||||||
|
debug!(
|
||||||
|
"hash {}, path: {:?} couldn't be parsed",
|
||||||
|
new_hash, &pathbuf
|
||||||
|
);
|
||||||
|
index_lock.insert(new_hash, pathbuf.into());
|
||||||
|
|
||||||
|
/* Send Write notice */
|
||||||
|
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: Update(old_hash, Box::new(env)),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Remove */
|
||||||
|
DebouncedEvent::NoticeRemove(pathbuf) | DebouncedEvent::Remove(pathbuf) => {
|
||||||
|
debug!("DebouncedEvent::Remove(path = {:?}", pathbuf);
|
||||||
|
let mailbox_hash = get_path_hash!(pathbuf);
|
||||||
|
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||||
|
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||||
|
let hash: EnvelopeHash = if let Some((k, _)) =
|
||||||
|
index_lock.iter().find(|(_, v)| *v.buf == pathbuf)
|
||||||
|
{
|
||||||
|
*k
|
||||||
|
} else {
|
||||||
|
debug!("removed but not contained in index");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(ref modif) = &index_lock[&hash].modified {
|
||||||
|
match modif {
|
||||||
|
PathMod::Path(path) => debug!(
|
||||||
|
"envelope {} has modified path set {}",
|
||||||
|
hash,
|
||||||
|
path.display()
|
||||||
|
),
|
||||||
|
PathMod::Hash(hash) => debug!(
|
||||||
|
"envelope {} has modified path set {}",
|
||||||
|
hash,
|
||||||
|
&index_lock[&hash].buf.display()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
index_lock.entry(hash).and_modify(|e| {
|
||||||
|
e.removed = false;
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*mailbox_counts[&mailbox_hash].1.lock().unwrap() -= 1;
|
||||||
|
if !pathbuf.flags().contains(Flag::SEEN) {
|
||||||
|
*mailbox_counts[&mailbox_hash].0.lock().unwrap() -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
index_lock.entry(hash).and_modify(|e| {
|
||||||
|
e.removed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: Remove(hash),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/* Envelope hasn't changed */
|
||||||
|
DebouncedEvent::Rename(src, dest) => {
|
||||||
|
debug!("DebouncedEvent::Rename(src = {:?}, dest = {:?})", src, dest);
|
||||||
|
let mailbox_hash = get_path_hash!(src);
|
||||||
|
let old_hash: EnvelopeHash = get_file_hash(src.as_path());
|
||||||
|
let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
|
||||||
|
|
||||||
|
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||||
|
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||||
|
let old_flags = src.flags();
|
||||||
|
let new_flags = dest.flags();
|
||||||
|
let was_seen: bool = old_flags.contains(Flag::SEEN);
|
||||||
|
let is_seen: bool = new_flags.contains(Flag::SEEN);
|
||||||
|
|
||||||
|
if index_lock.contains_key(&old_hash) && !index_lock[&old_hash].removed
|
||||||
|
{
|
||||||
|
debug!("contains_old_key");
|
||||||
|
index_lock.entry(old_hash).and_modify(|e| {
|
||||||
|
debug!(&e.modified);
|
||||||
|
e.modified = Some(PathMod::Hash(new_hash));
|
||||||
|
});
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash: get_path_hash!(dest),
|
||||||
|
kind: Rename(old_hash, new_hash),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if !was_seen && is_seen {
|
||||||
|
let mut lck = mailbox_counts[&mailbox_hash].0.lock().unwrap();
|
||||||
|
*lck = lck.saturating_sub(1);
|
||||||
|
} else if was_seen && !is_seen {
|
||||||
|
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
||||||
|
}
|
||||||
|
if old_flags != new_flags {
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash: get_path_hash!(dest),
|
||||||
|
kind: NewFlags(new_hash, (new_flags, vec![])),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mailbox_index
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(new_hash, get_path_hash!(dest));
|
||||||
|
index_lock.insert(new_hash, dest.into());
|
||||||
|
continue;
|
||||||
|
} else if !index_lock.contains_key(&new_hash)
|
||||||
|
&& index_lock
|
||||||
|
.get(&old_hash)
|
||||||
|
.map(|e| e.removed)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
if index_lock
|
||||||
|
.get(&old_hash)
|
||||||
|
.map(|e| e.removed)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
index_lock.entry(old_hash).and_modify(|e| {
|
||||||
|
e.modified = Some(PathMod::Hash(new_hash));
|
||||||
|
e.removed = false;
|
||||||
|
});
|
||||||
|
debug!("contains_old_key, key was marked as removed (by external source)");
|
||||||
|
} else {
|
||||||
|
debug!("not contains_new_key");
|
||||||
|
}
|
||||||
|
let file_name = dest
|
||||||
.as_path()
|
.as_path()
|
||||||
.strip_prefix(&root_path)
|
.strip_prefix(&root_path)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
|
debug!("filename = {:?}", file_name);
|
||||||
|
drop(hash_indexes_lock);
|
||||||
if let Some(env) = add_path_to_index(
|
if let Some(env) = add_path_to_index(
|
||||||
&hash_indexes,
|
&hash_indexes,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
pathbuf.as_path(),
|
dest.as_path(),
|
||||||
&cache_dir,
|
&cache_dir,
|
||||||
file_name,
|
file_name,
|
||||||
) {
|
) {
|
||||||
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
|
mailbox_index
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(env.hash(), mailbox_hash);
|
||||||
debug!(
|
debug!(
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
env.subject(),
|
env.subject(),
|
||||||
pathbuf.display()
|
dest.display()
|
||||||
);
|
);
|
||||||
if !env.is_seen() {
|
if !env.is_seen() {
|
||||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
(sender)(
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash,
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
kind: Create(Box::new(env)),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Update */
|
|
||||||
DebouncedEvent::NoticeWrite(pathbuf)
|
|
||||||
| DebouncedEvent::Write(pathbuf) => {
|
|
||||||
debug!("DebouncedEvent::Write(path = {:?}", &pathbuf);
|
|
||||||
let mailbox_hash = get_path_hash!(pathbuf);
|
|
||||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
|
||||||
let index_lock =
|
|
||||||
&mut hash_indexes_lock.entry(mailbox_hash).or_default();
|
|
||||||
let file_name = pathbuf
|
|
||||||
.as_path()
|
|
||||||
.strip_prefix(&root_path)
|
|
||||||
.unwrap()
|
|
||||||
.to_path_buf();
|
|
||||||
/* Linear search in hash_index to find old hash */
|
|
||||||
let old_hash: EnvelopeHash = {
|
|
||||||
if let Some((k, v)) =
|
|
||||||
index_lock.iter_mut().find(|(_, v)| *v.buf == pathbuf)
|
|
||||||
{
|
|
||||||
//TODO FIXME This doesn't make sense?
|
|
||||||
*v = pathbuf.clone().into();
|
|
||||||
*k
|
|
||||||
} else {
|
|
||||||
drop(hash_indexes_lock);
|
|
||||||
/* Did we just miss a Create event? In any case, create
|
|
||||||
* envelope. */
|
|
||||||
if let Some(env) = add_path_to_index(
|
|
||||||
&hash_indexes,
|
|
||||||
mailbox_hash,
|
|
||||||
pathbuf.as_path(),
|
|
||||||
&cache_dir,
|
|
||||||
file_name,
|
|
||||||
) {
|
|
||||||
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash,
|
|
||||||
kind: Create(Box::new(env)),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let new_hash: EnvelopeHash = get_file_hash(pathbuf.as_path());
|
|
||||||
if index_lock.get_mut(&new_hash).is_none() {
|
|
||||||
debug!("write notice");
|
|
||||||
let op = Box::new(MaildirOp::new(
|
|
||||||
new_hash,
|
|
||||||
hash_indexes.clone(),
|
|
||||||
mailbox_hash,
|
|
||||||
));
|
|
||||||
if let Ok(env) = Envelope::from_token(op, new_hash) {
|
|
||||||
debug!("{}\t{:?}", new_hash, &pathbuf);
|
|
||||||
debug!(
|
|
||||||
"hash {}, path: {:?} couldn't be parsed",
|
|
||||||
new_hash, &pathbuf
|
|
||||||
);
|
|
||||||
index_lock.insert(new_hash, pathbuf.into());
|
|
||||||
|
|
||||||
/* Send Write notice */
|
|
||||||
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash,
|
|
||||||
kind: Update(old_hash, Box::new(env)),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Remove */
|
|
||||||
DebouncedEvent::NoticeRemove(pathbuf)
|
|
||||||
| DebouncedEvent::Remove(pathbuf) => {
|
|
||||||
debug!("DebouncedEvent::Remove(path = {:?}", pathbuf);
|
|
||||||
let mailbox_hash = get_path_hash!(pathbuf);
|
|
||||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
|
||||||
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
|
||||||
let hash: EnvelopeHash = if let Some((k, _)) =
|
|
||||||
index_lock.iter().find(|(_, v)| *v.buf == pathbuf)
|
|
||||||
{
|
|
||||||
*k
|
|
||||||
} else {
|
|
||||||
debug!("removed but not contained in index");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if let Some(ref modif) = &index_lock[&hash].modified {
|
|
||||||
match modif {
|
|
||||||
PathMod::Path(path) => debug!(
|
|
||||||
"envelope {} has modified path set {}",
|
|
||||||
hash,
|
|
||||||
path.display()
|
|
||||||
),
|
|
||||||
PathMod::Hash(hash) => debug!(
|
|
||||||
"envelope {} has modified path set {}",
|
|
||||||
hash,
|
|
||||||
&index_lock[&hash].buf.display()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
index_lock.entry(hash).and_modify(|e| {
|
|
||||||
e.removed = false;
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*mailbox_counts[&mailbox_hash].1.lock().unwrap() -= 1;
|
|
||||||
if !pathbuf.flags().contains(Flag::SEEN) {
|
|
||||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
index_lock.entry(hash).and_modify(|e| {
|
|
||||||
e.removed = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash,
|
|
||||||
kind: Remove(hash),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/* Envelope hasn't changed */
|
|
||||||
DebouncedEvent::Rename(src, dest) => {
|
|
||||||
debug!(
|
|
||||||
"DebouncedEvent::Rename(src = {:?}, dest = {:?})",
|
|
||||||
src, dest
|
|
||||||
);
|
|
||||||
let mailbox_hash = get_path_hash!(src);
|
|
||||||
let old_hash: EnvelopeHash = get_file_hash(src.as_path());
|
|
||||||
let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
|
|
||||||
|
|
||||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
|
||||||
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
|
||||||
let old_flags = src.flags();
|
|
||||||
let new_flags = dest.flags();
|
|
||||||
let was_seen: bool = old_flags.contains(Flag::SEEN);
|
|
||||||
let is_seen: bool = new_flags.contains(Flag::SEEN);
|
|
||||||
|
|
||||||
if index_lock.contains_key(&old_hash)
|
|
||||||
&& !index_lock[&old_hash].removed
|
|
||||||
{
|
|
||||||
debug!("contains_old_key");
|
|
||||||
index_lock.entry(old_hash).and_modify(|e| {
|
|
||||||
debug!(&e.modified);
|
|
||||||
e.modified = Some(PathMod::Hash(new_hash));
|
|
||||||
});
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash: get_path_hash!(dest),
|
|
||||||
kind: Rename(old_hash, new_hash),
|
|
||||||
}));
|
|
||||||
if !was_seen && is_seen {
|
|
||||||
let mut lck = mailbox_counts[&mailbox_hash].0.lock().unwrap();
|
|
||||||
*lck = lck.saturating_sub(1);
|
|
||||||
} else if was_seen && !is_seen {
|
|
||||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
|
||||||
}
|
|
||||||
if old_flags != new_flags {
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash: get_path_hash!(dest),
|
|
||||||
kind: NewFlags(new_hash, (new_flags, vec![])),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
mailbox_index.lock().unwrap().insert(new_hash,get_path_hash!(dest) );
|
|
||||||
index_lock.insert(new_hash, dest.into());
|
|
||||||
continue;
|
|
||||||
} else if !index_lock.contains_key(&new_hash)
|
|
||||||
&& index_lock
|
|
||||||
.get(&old_hash)
|
|
||||||
.map(|e| e.removed)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
if index_lock
|
|
||||||
.get(&old_hash)
|
|
||||||
.map(|e| e.removed)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
index_lock.entry(old_hash).and_modify(|e| {
|
|
||||||
e.modified = Some(PathMod::Hash(new_hash));
|
|
||||||
e.removed = false;
|
|
||||||
});
|
|
||||||
debug!("contains_old_key, key was marked as removed (by external source)");
|
|
||||||
} else {
|
|
||||||
debug!("not contains_new_key");
|
|
||||||
}
|
|
||||||
let file_name = dest
|
|
||||||
.as_path()
|
|
||||||
.strip_prefix(&root_path)
|
|
||||||
.unwrap()
|
|
||||||
.to_path_buf();
|
|
||||||
debug!("filename = {:?}", file_name);
|
|
||||||
drop(hash_indexes_lock);
|
|
||||||
if let Some(env) = add_path_to_index(
|
|
||||||
&hash_indexes,
|
|
||||||
mailbox_hash,
|
|
||||||
dest.as_path(),
|
|
||||||
&cache_dir,
|
|
||||||
file_name,
|
|
||||||
) {
|
|
||||||
mailbox_index.lock().unwrap().insert(env.hash(), mailbox_hash);
|
|
||||||
debug!(
|
|
||||||
"Create event {} {} {}",
|
|
||||||
env.hash(),
|
|
||||||
env.subject(),
|
|
||||||
dest.display()
|
|
||||||
);
|
|
||||||
if !env.is_seen() {
|
|
||||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
|
||||||
}
|
|
||||||
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
}));
|
}),
|
||||||
continue;
|
);
|
||||||
} else {
|
continue;
|
||||||
debug!("not valid email");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if was_seen && !is_seen {
|
debug!("not valid email");
|
||||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
}
|
||||||
}
|
} else {
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
if was_seen && !is_seen {
|
||||||
|
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
||||||
|
}
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash: get_path_hash!(dest),
|
mailbox_hash: get_path_hash!(dest),
|
||||||
kind: Rename(old_hash, new_hash),
|
kind: Rename(old_hash, new_hash),
|
||||||
}));
|
}),
|
||||||
debug!("contains_new_key");
|
);
|
||||||
if old_flags != new_flags {
|
debug!("contains_new_key");
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
if old_flags != new_flags {
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash: get_path_hash!(dest),
|
mailbox_hash: get_path_hash!(dest),
|
||||||
kind: NewFlags(new_hash, (new_flags, vec![])),
|
kind: NewFlags(new_hash, (new_flags, vec![])),
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Maybe a re-read should be triggered here just to be safe.
|
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash: get_path_hash!(dest),
|
|
||||||
kind: Rescan,
|
|
||||||
}));
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
/* Trigger rescan of mailbox */
|
|
||||||
DebouncedEvent::Rescan => {
|
/* Maybe a re-read should be triggered here just to be safe.
|
||||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash: get_path_hash!(dest),
|
||||||
|
kind: Rescan,
|
||||||
|
}));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
/* Trigger rescan of mailbox */
|
||||||
|
DebouncedEvent::Rescan => {
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash: root_mailbox_hash,
|
mailbox_hash: root_mailbox_hash,
|
||||||
kind: Rescan,
|
kind: Rescan,
|
||||||
}));
|
}),
|
||||||
}
|
);
|
||||||
_ => {}
|
}
|
||||||
},
|
_ => {}
|
||||||
Err(e) => debug!("watch error: {:?}", e),
|
},
|
||||||
}
|
Err(e) => debug!("watch error: {:?}", e),
|
||||||
}
|
}
|
||||||
})?;
|
}
|
||||||
Ok(handle.thread().id())
|
Ok(())
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
||||||
|
@ -832,8 +858,8 @@ impl MailBackend for MaildirType {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.mailboxes.insert(mailbox_hash, new_mailbox);
|
self.mailboxes.insert(mailbox_hash, new_mailbox);
|
||||||
let ret = Ok((mailbox_hash, self.mailboxes()?));
|
let ret = self.mailboxes()?;
|
||||||
Ok(Box::pin(async { ret }))
|
Ok(Box::pin(async move { Ok((mailbox_hash, ret.await?)) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_mailbox(
|
fn delete_mailbox(
|
||||||
|
@ -1019,7 +1045,12 @@ impl MaildirType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multicore(&mut self, cores: usize, mailbox_hash: MailboxHash) -> Async<Result<Vec<Envelope>>> {
|
/*
|
||||||
|
pub fn multicore(
|
||||||
|
&mut self,
|
||||||
|
cores: usize,
|
||||||
|
mailbox_hash: MailboxHash,
|
||||||
|
) -> Async<Result<Vec<Envelope>>> {
|
||||||
let mut w = AsyncBuilder::new();
|
let mut w = AsyncBuilder::new();
|
||||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||||
|
|
||||||
|
@ -1199,6 +1230,7 @@ impl MaildirType {
|
||||||
};
|
};
|
||||||
w.build(handle)
|
w.build(handle)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn save_to_mailbox(mut path: PathBuf, bytes: Vec<u8>, flags: Option<Flag>) -> Result<()> {
|
pub fn save_to_mailbox(mut path: PathBuf, bytes: Vec<u8>, flags: Option<Flag>) -> Result<()> {
|
||||||
for d in &["cur", "new", "tmp"] {
|
for d in &["cur", "new", "tmp"] {
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
* https://wiki2.dovecot.org/MailboxFormat/mbox
|
* https://wiki2.dovecot.org/MailboxFormat/mbox
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
|
||||||
use crate::backends::*;
|
use crate::backends::*;
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::parser::BytesExt;
|
use crate::email::parser::BytesExt;
|
||||||
|
@ -707,96 +706,109 @@ impl MailBackend for MboxType {
|
||||||
CAPABILITIES
|
CAPABILITIES
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_online(&self) -> Result<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
Ok(())
|
Ok(Box::pin(async { Ok(()) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
|
fn fetch(
|
||||||
let mut w = AsyncBuilder::new();
|
&mut self,
|
||||||
let handle = {
|
mailbox_hash: MailboxHash,
|
||||||
let tx = w.tx();
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
struct FetchState {
|
||||||
let mailboxes = self.mailboxes.clone();
|
mailbox_hash: MailboxHash,
|
||||||
let mailbox_path = mailboxes.lock().unwrap()[&mailbox_hash].fs_path.clone();
|
mailbox_index: Arc<Mutex<HashMap<EnvelopeHash, MailboxHash>>>,
|
||||||
let prefer_mbox_type = self.prefer_mbox_type;
|
mailboxes: Arc<Mutex<HashMap<MailboxHash, MboxMailbox>>>,
|
||||||
let closure = move |_work_context| {
|
prefer_mbox_type: Option<MboxReader>,
|
||||||
let tx = tx.clone();
|
offset: usize,
|
||||||
let file = match std::fs::OpenOptions::new()
|
file_offset: usize,
|
||||||
.read(true)
|
contents: Vec<u8>,
|
||||||
.write(true)
|
}
|
||||||
.open(&mailbox_path)
|
impl FetchState {
|
||||||
{
|
async fn fetch(&mut self) -> Result<Option<Vec<Envelope>>> {
|
||||||
Ok(f) => f,
|
let mailboxes_lck = self.mailboxes.lock().unwrap();
|
||||||
Err(e) => {
|
let index = mailboxes_lck[&self.mailbox_hash].index.clone();
|
||||||
tx.send(AsyncStatus::Payload(Err(MeliError::from(e))))
|
|
||||||
.unwrap();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
get_rw_lock_blocking(&file);
|
|
||||||
let mut buf_reader = BufReader::new(file);
|
|
||||||
let mut contents = Vec::new();
|
|
||||||
if let Err(e) = buf_reader.read_to_end(&mut contents) {
|
|
||||||
tx.send(AsyncStatus::Payload(Err(MeliError::from(e))))
|
|
||||||
.unwrap();
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mailboxes_lck = mailboxes.lock().unwrap();
|
|
||||||
let index = mailboxes_lck[&mailbox_hash].index.clone();
|
|
||||||
drop(mailboxes_lck);
|
drop(mailboxes_lck);
|
||||||
let mut message_iter = MessageIterator {
|
let mut message_iter = MessageIterator {
|
||||||
index,
|
index,
|
||||||
input: &contents.as_slice(),
|
input: &self.contents.as_slice(),
|
||||||
offset: 0,
|
offset: self.offset,
|
||||||
file_offset: 0,
|
file_offset: self.file_offset,
|
||||||
reader: prefer_mbox_type,
|
reader: self.prefer_mbox_type,
|
||||||
};
|
};
|
||||||
let mut err = None;
|
let mut payload = vec![];
|
||||||
loop {
|
let mut done = false;
|
||||||
let mut payload = vec![];
|
'iter_for_loop: for _i in 0..150 {
|
||||||
'iter_for_loop: for _i in 0..150 {
|
match message_iter.next() {
|
||||||
match message_iter.next() {
|
Some(Ok(env)) => {
|
||||||
Some(Ok(env)) => {
|
payload.push(env);
|
||||||
payload.push(env);
|
}
|
||||||
}
|
Some(Err(_err)) => {
|
||||||
Some(Err(_err)) => {
|
debug!(&_err);
|
||||||
debug!(&_err);
|
}
|
||||||
err = Some(_err);
|
None => {
|
||||||
}
|
done = true;
|
||||||
None => {
|
break 'iter_for_loop;
|
||||||
break 'iter_for_loop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !payload.is_empty() {
|
|
||||||
err = None;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let mut mailbox_index_lck = mailbox_index.lock().unwrap();
|
|
||||||
for env in &payload {
|
|
||||||
mailbox_index_lck.insert(env.hash(), mailbox_hash);
|
|
||||||
}
|
|
||||||
tx.send(AsyncStatus::Payload(Ok(payload))).unwrap();
|
|
||||||
}
|
|
||||||
if let Some(err) = err {
|
|
||||||
tx.send(AsyncStatus::Payload(Err(err))).unwrap();
|
|
||||||
}
|
}
|
||||||
|
self.offset = message_iter.offset;
|
||||||
|
self.file_offset = message_iter.file_offset;
|
||||||
{
|
{
|
||||||
let mut mailbox_lock = mailboxes.lock().unwrap();
|
let mut mailbox_index_lck = self.mailbox_index.lock().unwrap();
|
||||||
mailbox_lock
|
for env in &payload {
|
||||||
.entry(mailbox_hash)
|
mailbox_index_lck.insert(env.hash(), self.mailbox_hash);
|
||||||
.and_modify(|f| f.content = contents);
|
}
|
||||||
}
|
}
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
if done {
|
||||||
};
|
if payload.is_empty() {
|
||||||
Box::new(closure)
|
return Ok(None);
|
||||||
|
} else {
|
||||||
|
let mut mailbox_lock = self.mailboxes.lock().unwrap();
|
||||||
|
let contents = std::mem::replace(&mut self.contents, vec![]);
|
||||||
|
mailbox_lock
|
||||||
|
.entry(self.mailbox_hash)
|
||||||
|
.and_modify(|f| f.content = contents);
|
||||||
|
Ok(Some(payload))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Some(payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mailboxes = self.mailboxes.clone();
|
||||||
|
|
||||||
|
let mailbox_path = mailboxes.lock().unwrap()[&mailbox_hash].fs_path.clone();
|
||||||
|
let file = std::fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(&mailbox_path)?;
|
||||||
|
get_rw_lock_blocking(&file);
|
||||||
|
let mut buf_reader = BufReader::new(file);
|
||||||
|
let mut contents = Vec::new();
|
||||||
|
buf_reader.read_to_end(&mut contents)?;
|
||||||
|
let mut state = FetchState {
|
||||||
|
mailbox_hash,
|
||||||
|
mailboxes,
|
||||||
|
mailbox_index: self.mailbox_index.clone(),
|
||||||
|
prefer_mbox_type: self.prefer_mbox_type,
|
||||||
|
contents,
|
||||||
|
offset: 0,
|
||||||
|
file_offset: 0,
|
||||||
};
|
};
|
||||||
Ok(w.build(handle))
|
Ok(Box::pin(async_stream::try_stream! {
|
||||||
|
loop {
|
||||||
|
if let Some(res) = state.fetch().await.map_err(|err| {
|
||||||
|
debug!("fetch err {:?}", &err);
|
||||||
|
err})? {
|
||||||
|
yield res;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&self, work_context: WorkContext) -> Result<std::thread::ThreadId> {
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
let sender = self.event_consumer.clone();
|
let sender = self.event_consumer.clone();
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
let mut watcher = watcher(tx, std::time::Duration::from_secs(10))
|
let mut watcher = watcher(tx, std::time::Duration::from_secs(10))
|
||||||
|
@ -817,165 +829,154 @@ impl MailBackend for MboxType {
|
||||||
let mailboxes = self.mailboxes.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
let prefer_mbox_type = self.prefer_mbox_type;
|
let prefer_mbox_type = self.prefer_mbox_type;
|
||||||
let handle = std::thread::Builder::new()
|
Ok(Box::pin(async move {
|
||||||
.name(format!("watching {}", self.account_name,))
|
loop {
|
||||||
.spawn(move || {
|
match rx.recv() {
|
||||||
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
/*
|
||||||
let _watcher = watcher;
|
* Event types:
|
||||||
let _work_context = work_context;
|
*
|
||||||
let mailboxes = mailboxes;
|
* pub enum RefreshEventKind {
|
||||||
loop {
|
* Update(EnvelopeHash, Envelope), // Old hash, new envelope
|
||||||
match rx.recv() {
|
* Create(Envelope),
|
||||||
/*
|
* Remove(EnvelopeHash),
|
||||||
* Event types:
|
* Rescan,
|
||||||
*
|
* }
|
||||||
* pub enum RefreshEventKind {
|
*/
|
||||||
* Update(EnvelopeHash, Envelope), // Old hash, new envelope
|
Ok(event) => match event {
|
||||||
* Create(Envelope),
|
/* Update */
|
||||||
* Remove(EnvelopeHash),
|
DebouncedEvent::NoticeWrite(pathbuf) | DebouncedEvent::Write(pathbuf) => {
|
||||||
* Rescan,
|
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||||
* }
|
let file = match std::fs::OpenOptions::new()
|
||||||
*/
|
.read(true)
|
||||||
Ok(event) => match event {
|
.write(true)
|
||||||
/* Update */
|
.open(&pathbuf)
|
||||||
DebouncedEvent::NoticeWrite(pathbuf)
|
{
|
||||||
| DebouncedEvent::Write(pathbuf) => {
|
Ok(f) => f,
|
||||||
let mailbox_hash = get_path_hash!(&pathbuf);
|
Err(_) => {
|
||||||
let file = match std::fs::OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.open(&pathbuf)
|
|
||||||
{
|
|
||||||
Ok(f) => f,
|
|
||||||
Err(_) => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
get_rw_lock_blocking(&file);
|
|
||||||
let mut mailbox_lock = mailboxes.lock().unwrap();
|
|
||||||
let mut buf_reader = BufReader::new(file);
|
|
||||||
let mut contents = Vec::new();
|
|
||||||
if let Err(e) = buf_reader.read_to_end(&mut contents) {
|
|
||||||
debug!(e);
|
|
||||||
continue;
|
continue;
|
||||||
};
|
}
|
||||||
if contents
|
};
|
||||||
.starts_with(mailbox_lock[&mailbox_hash].content.as_slice())
|
get_rw_lock_blocking(&file);
|
||||||
{
|
let mut mailbox_lock = mailboxes.lock().unwrap();
|
||||||
if let Ok((_, envelopes)) = mbox_parse(
|
let mut buf_reader = BufReader::new(file);
|
||||||
mailbox_lock[&mailbox_hash].index.clone(),
|
let mut contents = Vec::new();
|
||||||
&contents,
|
if let Err(e) = buf_reader.read_to_end(&mut contents) {
|
||||||
mailbox_lock[&mailbox_hash].content.len(),
|
debug!(e);
|
||||||
prefer_mbox_type,
|
continue;
|
||||||
) {
|
};
|
||||||
let mut mailbox_index_lck = mailbox_index.lock().unwrap();
|
if contents.starts_with(mailbox_lock[&mailbox_hash].content.as_slice())
|
||||||
for env in envelopes {
|
{
|
||||||
mailbox_index_lck.insert(env.hash(), mailbox_hash);
|
if let Ok((_, envelopes)) = mbox_parse(
|
||||||
(sender)(
|
mailbox_lock[&mailbox_hash].index.clone(),
|
||||||
|
&contents,
|
||||||
|
mailbox_lock[&mailbox_hash].content.len(),
|
||||||
|
prefer_mbox_type,
|
||||||
|
) {
|
||||||
|
let mut mailbox_index_lck = mailbox_index.lock().unwrap();
|
||||||
|
for env in envelopes {
|
||||||
|
mailbox_index_lck.insert(env.hash(), mailbox_hash);
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
mailbox_hash,
|
||||||
account_hash,
|
kind: RefreshEventKind::Create(Box::new(env)),
|
||||||
mailbox_hash,
|
}),
|
||||||
kind: RefreshEventKind::Create(Box::new(env)),
|
);
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
(sender)(
|
|
||||||
account_hash,
|
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash,
|
|
||||||
kind: RefreshEventKind::Rescan,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
mailbox_lock
|
} else {
|
||||||
.entry(mailbox_hash)
|
(sender)(
|
||||||
.and_modify(|f| f.content = contents);
|
account_hash,
|
||||||
}
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
/* Remove */
|
|
||||||
DebouncedEvent::NoticeRemove(pathbuf)
|
|
||||||
| DebouncedEvent::Remove(pathbuf) => {
|
|
||||||
if mailboxes
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.values()
|
|
||||||
.any(|f| f.fs_path == pathbuf)
|
|
||||||
{
|
|
||||||
let mailbox_hash = get_path_hash!(&pathbuf);
|
|
||||||
(sender)(
|
|
||||||
account_hash,
|
account_hash,
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
mailbox_hash,
|
||||||
account_hash,
|
kind: RefreshEventKind::Rescan,
|
||||||
mailbox_hash,
|
}),
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(
|
);
|
||||||
format!(
|
|
||||||
"mbox mailbox {} was removed.",
|
|
||||||
pathbuf.display()
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DebouncedEvent::Rename(src, dest) => {
|
mailbox_lock
|
||||||
if mailboxes
|
.entry(mailbox_hash)
|
||||||
.lock()
|
.and_modify(|f| f.content = contents);
|
||||||
.unwrap()
|
}
|
||||||
.values()
|
/* Remove */
|
||||||
.any(|f| &f.fs_path == &src)
|
DebouncedEvent::NoticeRemove(pathbuf) | DebouncedEvent::Remove(pathbuf) => {
|
||||||
{
|
if mailboxes
|
||||||
let mailbox_hash = get_path_hash!(&src);
|
.lock()
|
||||||
(sender)(
|
.unwrap()
|
||||||
|
.values()
|
||||||
|
.any(|f| f.fs_path == pathbuf)
|
||||||
|
{
|
||||||
|
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
mailbox_hash,
|
||||||
account_hash,
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
mailbox_hash,
|
"mbox mailbox {} was removed.",
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(
|
pathbuf.display()
|
||||||
format!(
|
))),
|
||||||
"mbox mailbox {} was renamed to {}.",
|
}),
|
||||||
src.display(),
|
);
|
||||||
dest.display()
|
return Ok(());
|
||||||
),
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* Trigger rescan of mailboxes */
|
}
|
||||||
DebouncedEvent::Rescan => {
|
DebouncedEvent::Rename(src, dest) => {
|
||||||
for &mailbox_hash in mailboxes.lock().unwrap().keys() {
|
if mailboxes
|
||||||
(sender)(
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.values()
|
||||||
|
.any(|f| &f.fs_path == &src)
|
||||||
|
{
|
||||||
|
let mailbox_hash = get_path_hash!(&src);
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
mailbox_hash,
|
||||||
account_hash,
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
mailbox_hash,
|
"mbox mailbox {} was renamed to {}.",
|
||||||
kind: RefreshEventKind::Rescan,
|
src.display(),
|
||||||
}),
|
dest.display()
|
||||||
);
|
))),
|
||||||
}
|
}),
|
||||||
return;
|
);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
/* Trigger rescan of mailboxes */
|
||||||
Err(e) => debug!("watch error: {:?}", e),
|
DebouncedEvent::Rescan => {
|
||||||
}
|
for &mailbox_hash in mailboxes.lock().unwrap().keys() {
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: RefreshEventKind::Rescan,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Err(e) => debug!("watch error: {:?}", e),
|
||||||
}
|
}
|
||||||
})?;
|
}
|
||||||
Ok(handle.thread().id())
|
Ok(())
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
Ok(self
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
|
let ret = Ok(self
|
||||||
.mailboxes
|
.mailboxes
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(h, f)| (*h, f.clone() as Mailbox))
|
.map(|(h, f)| (*h, f.clone() as Mailbox))
|
||||||
.collect())
|
.collect());
|
||||||
|
Ok(Box::pin(async { ret }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operation(&self, env_hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
fn operation(&self, env_hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
||||||
|
|
|
@ -32,7 +32,6 @@ pub use operations::*;
|
||||||
mod connection;
|
mod connection;
|
||||||
pub use connection::*;
|
pub use connection::*;
|
||||||
|
|
||||||
use crate::async_workers::{Async, WorkContext};
|
|
||||||
use crate::backends::*;
|
use crate::backends::*;
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::*;
|
use crate::email::*;
|
||||||
|
@ -185,7 +184,7 @@ impl MailBackend for NntpType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_async(
|
fn fetch(
|
||||||
&mut self,
|
&mut self,
|
||||||
mailbox_hash: MailboxHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
||||||
|
@ -211,11 +210,11 @@ impl MailBackend for NntpType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_async(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
|
@ -229,12 +228,12 @@ impl MailBackend for NntpType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_online_async(&self) -> ResultFuture<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
match timeout(std::time::Duration::from_secs(3), connection.lock()).await {
|
match timeout(std::time::Duration::from_secs(3), connection.lock()).await {
|
||||||
Ok(mut conn) => {
|
Ok(mut conn) => {
|
||||||
debug!("is_online_async");
|
debug!("is_online");
|
||||||
match debug!(timeout(std::time::Duration::from_secs(3), conn.connect()).await) {
|
match debug!(timeout(std::time::Duration::from_secs(3), conn.connect()).await) {
|
||||||
Ok(Ok(())) => Ok(()),
|
Ok(Ok(())) => Ok(()),
|
||||||
Err(err) | Ok(Err(err)) => {
|
Err(err) | Ok(Err(err)) => {
|
||||||
|
@ -248,23 +247,7 @@ impl MailBackend for NntpType {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<()>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn watch(&self, _work_context: WorkContext) -> Result<std::thread::ThreadId> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn watch_async(&self) -> ResultFuture<()> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
|
||||||
use crate::backends::*;
|
use crate::backends::*;
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||||
|
@ -333,71 +332,56 @@ impl MailBackend for NotmuchDb {
|
||||||
CAPABILITIES
|
CAPABILITIES
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_online(&self) -> Result<()> {
|
fn is_online(&self) -> ResultFuture<()> {
|
||||||
Ok(())
|
Ok(Box::pin(async { Ok(()) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
|
fn fetch(
|
||||||
let mut w = AsyncBuilder::new();
|
&mut self,
|
||||||
let database = NotmuchDb::new_connection(self.path.as_path(), self.lib.clone(), false);
|
mailbox_hash: MailboxHash,
|
||||||
let index = self.index.clone();
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
struct FetchState {
|
||||||
let tag_index = self.tag_index.clone();
|
mailbox_hash: MailboxHash,
|
||||||
let mailboxes = self.mailboxes.clone();
|
database: Arc<DbConnection>,
|
||||||
let lib = self.lib.clone();
|
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
||||||
let handle = {
|
mailbox_index: Arc<RwLock<HashMap<EnvelopeHash, SmallVec<[MailboxHash; 16]>>>>,
|
||||||
let tx = w.tx();
|
mailboxes: Arc<RwLock<HashMap<u64, NotmuchMailbox>>>,
|
||||||
let closure = move |_work_context| {
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
if let Err(err) = database {
|
lib: Arc<libloading::Library>,
|
||||||
tx.send(AsyncStatus::Payload(Err(err))).unwrap();
|
iter: std::vec::IntoIter<CString>,
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
}
|
||||||
return;
|
impl FetchState {
|
||||||
}
|
async fn fetch(&mut self) -> Result<Option<Vec<Envelope>>> {
|
||||||
let database = Arc::new(database.unwrap());
|
|
||||||
let database_lck = database.inner.read().unwrap();
|
|
||||||
let mailboxes_lck = mailboxes.read().unwrap();
|
|
||||||
let mailbox = mailboxes_lck.get(&mailbox_hash).unwrap();
|
|
||||||
let mut unseen_count = 0;
|
let mut unseen_count = 0;
|
||||||
let query: Query =
|
|
||||||
match Query::new(lib.clone(), &database_lck, mailbox.query_str.as_str()) {
|
|
||||||
Ok(q) => q,
|
|
||||||
Err(err) => {
|
|
||||||
tx.send(AsyncStatus::Payload(Err(err))).unwrap();
|
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let iter: Vec<<MessageIterator as Iterator>::Item> = match query.search() {
|
|
||||||
Ok(i) => i.collect(),
|
|
||||||
Err(err) => {
|
|
||||||
tx.send(AsyncStatus::Payload(Err(err))).unwrap();
|
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
{
|
|
||||||
let mut total_lck = mailbox.total.lock().unwrap();
|
|
||||||
let mut unseen_lck = mailbox.unseen.lock().unwrap();
|
|
||||||
*total_lck = iter.len();
|
|
||||||
*unseen_lck = 0;
|
|
||||||
}
|
|
||||||
let chunk_size = 250;
|
let chunk_size = 250;
|
||||||
for chunk in iter.chunks(chunk_size) {
|
let mut mailbox_index_lck = self.mailbox_index.write().unwrap();
|
||||||
let mut ret: Vec<Envelope> = Vec::with_capacity(chunk_size);
|
let mut ret: Vec<Envelope> = Vec::with_capacity(chunk_size);
|
||||||
let mut mailbox_index_lck = mailbox_index.write().unwrap();
|
let mut done: bool = false;
|
||||||
for &message in chunk {
|
for _ in 0..chunk_size {
|
||||||
|
if let Some(message_id) = self.iter.next() {
|
||||||
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
||||||
|
unsafe {
|
||||||
|
call!(self.lib, notmuch_database_find_message)(
|
||||||
|
*self.database.inner.read().unwrap(),
|
||||||
|
message_id.as_ptr(),
|
||||||
|
&mut message as *mut _,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if message.is_null() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match notmuch_message_into_envelope(
|
match notmuch_message_into_envelope(
|
||||||
lib.clone(),
|
self.lib.clone(),
|
||||||
index.clone(),
|
self.index.clone(),
|
||||||
tag_index.clone(),
|
self.tag_index.clone(),
|
||||||
database.clone(),
|
self.database.clone(),
|
||||||
message,
|
message,
|
||||||
) {
|
) {
|
||||||
Ok(env) => {
|
Ok(env) => {
|
||||||
mailbox_index_lck
|
mailbox_index_lck
|
||||||
.entry(env.hash())
|
.entry(env.hash())
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(mailbox_hash);
|
.push(self.mailbox_hash);
|
||||||
if !env.is_seen() {
|
if !env.is_seen() {
|
||||||
unseen_count += 1;
|
unseen_count += 1;
|
||||||
}
|
}
|
||||||
|
@ -406,235 +390,290 @@ impl MailBackend for NotmuchDb {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!("could not parse message {:?} {}", err, {
|
debug!("could not parse message {:?} {}", err, {
|
||||||
let fs_path = unsafe {
|
let fs_path = unsafe {
|
||||||
call!(lib, notmuch_message_get_filename)(message)
|
call!(self.lib, notmuch_message_get_filename)(message)
|
||||||
};
|
};
|
||||||
let c_str = unsafe { CStr::from_ptr(fs_path) };
|
let c_str = unsafe { CStr::from_ptr(fs_path) };
|
||||||
String::from_utf8_lossy(c_str.to_bytes())
|
String::from_utf8_lossy(c_str.to_bytes())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
{
|
|
||||||
let mut unseen_lck = mailbox.unseen.lock().unwrap();
|
|
||||||
*unseen_lck = unseen_count;
|
|
||||||
}
|
|
||||||
tx.send(AsyncStatus::Payload(Ok(ret))).unwrap();
|
|
||||||
}
|
}
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
{
|
||||||
};
|
let mailboxes_lck = self.mailboxes.read().unwrap();
|
||||||
Box::new(closure)
|
let mailbox = mailboxes_lck.get(&self.mailbox_hash).unwrap();
|
||||||
|
let mut unseen_lck = mailbox.unseen.lock().unwrap();
|
||||||
|
*unseen_lck += unseen_count;
|
||||||
|
}
|
||||||
|
if done && ret.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let database = Arc::new(NotmuchDb::new_connection(
|
||||||
|
self.path.as_path(),
|
||||||
|
self.lib.clone(),
|
||||||
|
false,
|
||||||
|
)?);
|
||||||
|
let index = self.index.clone();
|
||||||
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
|
let tag_index = self.tag_index.clone();
|
||||||
|
let mailboxes = self.mailboxes.clone();
|
||||||
|
let lib = self.lib.clone();
|
||||||
|
let v: Vec<CString>;
|
||||||
|
{
|
||||||
|
let database_lck = database.inner.read().unwrap();
|
||||||
|
let mailboxes_lck = mailboxes.read().unwrap();
|
||||||
|
let mailbox = mailboxes_lck.get(&mailbox_hash).unwrap();
|
||||||
|
let query: Query =
|
||||||
|
Query::new(self.lib.clone(), &database_lck, mailbox.query_str.as_str())?;
|
||||||
|
{
|
||||||
|
let mut total_lck = mailbox.total.lock().unwrap();
|
||||||
|
let mut unseen_lck = mailbox.unseen.lock().unwrap();
|
||||||
|
*total_lck = query.count()? as usize;
|
||||||
|
*unseen_lck = 0;
|
||||||
|
}
|
||||||
|
v = query
|
||||||
|
.search()?
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| notmuch_message_insert(&lib, &index, m))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = FetchState {
|
||||||
|
mailbox_hash,
|
||||||
|
mailboxes,
|
||||||
|
database,
|
||||||
|
lib,
|
||||||
|
index,
|
||||||
|
mailbox_index,
|
||||||
|
tag_index,
|
||||||
|
iter: v.into_iter(),
|
||||||
};
|
};
|
||||||
Ok(w.build(handle))
|
Ok(Box::pin(async_stream::try_stream! {
|
||||||
|
while let Some(res) = state.fetch().await.map_err(|err| { debug!("fetch err {:?}", &err); err})? {
|
||||||
|
yield res;
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&self, _work_context: WorkContext) -> Result<std::thread::ThreadId> {
|
/*
|
||||||
extern crate notify;
|
fn watch(&self) -> ResultFuture<()> {
|
||||||
use crate::backends::RefreshEventKind::*;
|
extern crate notify;
|
||||||
use notify::{watcher, RecursiveMode, Watcher};
|
use crate::backends::RefreshEventKind::*;
|
||||||
let sender = self.event_consumer.clone();
|
use notify::{watcher, RecursiveMode, Watcher};
|
||||||
let (tx, rx) = std::sync::mpsc::channel();
|
let sender = self.event_consumer.clone();
|
||||||
let mut watcher = watcher(tx, std::time::Duration::from_secs(2)).unwrap();
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
watcher.watch(&self.path, RecursiveMode::Recursive).unwrap();
|
let mut watcher = watcher(tx, std::time::Duration::from_secs(2)).unwrap();
|
||||||
let path = self.path.clone();
|
watcher.watch(&self.path, RecursiveMode::Recursive).unwrap();
|
||||||
let lib = self.lib.clone();
|
let path = self.path.clone();
|
||||||
let tag_index = self.tag_index.clone();
|
let lib = self.lib.clone();
|
||||||
let index = self.index.clone();
|
let tag_index = self.tag_index.clone();
|
||||||
let account_hash = {
|
let index = self.index.clone();
|
||||||
let mut hasher = DefaultHasher::new();
|
let account_hash = {
|
||||||
hasher.write(self.account_name.as_bytes());
|
let mut hasher = DefaultHasher::new();
|
||||||
hasher.finish()
|
hasher.write(self.account_name.as_bytes());
|
||||||
};
|
hasher.finish()
|
||||||
let mailbox_index = self.mailbox_index.clone();
|
|
||||||
let mailboxes = self.mailboxes.clone();
|
|
||||||
{
|
|
||||||
let database = NotmuchDb::new_connection(path.as_path(), lib.clone(), false)?;
|
|
||||||
let mut revision_uuid_lck = self.revision_uuid.write().unwrap();
|
|
||||||
|
|
||||||
*revision_uuid_lck = unsafe {
|
|
||||||
call!(lib, notmuch_database_get_revision)(
|
|
||||||
*database.inner.read().unwrap(),
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
let revision_uuid = self.revision_uuid.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
|
{
|
||||||
|
let database = NotmuchDb::new_connection(path.as_path(), lib.clone(), false)?;
|
||||||
|
let mut revision_uuid_lck = self.revision_uuid.write().unwrap();
|
||||||
|
|
||||||
let handle = std::thread::Builder::new()
|
*revision_uuid_lck = unsafe {
|
||||||
.name(format!("watching {}", self.account_name))
|
call!(lib, notmuch_database_get_revision)(
|
||||||
.spawn(move || {
|
*database.inner.read().unwrap(),
|
||||||
let _watcher = watcher;
|
std::ptr::null_mut(),
|
||||||
let c = move |sender: &BackendEventConsumer| -> std::result::Result<(), MeliError> {
|
)
|
||||||
loop {
|
};
|
||||||
let _ = rx.recv().map_err(|err| err.to_string())?;
|
}
|
||||||
{
|
let revision_uuid = self.revision_uuid.clone();
|
||||||
let database =
|
|
||||||
NotmuchDb::new_connection(path.as_path(), lib.clone(), false)?;
|
|
||||||
let database_lck = database.inner.read().unwrap();
|
|
||||||
let mut revision_uuid_lck = revision_uuid.write().unwrap();
|
|
||||||
|
|
||||||
let new_revision = unsafe {
|
let handle = std::thread::Builder::new()
|
||||||
call!(lib, notmuch_database_get_revision)(
|
.name(format!("watching {}", self.account_name))
|
||||||
*database_lck,
|
.spawn(move || {
|
||||||
std::ptr::null_mut(),
|
let _watcher = watcher;
|
||||||
)
|
let c = move |sender: &BackendEventConsumer| -> std::result::Result<(), MeliError> {
|
||||||
};
|
loop {
|
||||||
if new_revision > *revision_uuid_lck {
|
let _ = rx.recv().map_err(|err| err.to_string())?;
|
||||||
let query_str =
|
{
|
||||||
format!("lastmod:{}..{}", *revision_uuid_lck, new_revision);
|
let database =
|
||||||
let query: Query =
|
NotmuchDb::new_connection(path.as_path(), lib.clone(), false)?;
|
||||||
Query::new(lib.clone(), &database_lck, &query_str)?;
|
let database_lck = database.inner.read().unwrap();
|
||||||
drop(database_lck);
|
let mut revision_uuid_lck = revision_uuid.write().unwrap();
|
||||||
let iter = query.search()?;
|
|
||||||
let mut tag_lock = tag_index.write().unwrap();
|
let new_revision = unsafe {
|
||||||
let mailbox_index_lck = mailbox_index.write().unwrap();
|
call!(lib, notmuch_database_get_revision)(
|
||||||
let mailboxes_lck = mailboxes.read().unwrap();
|
*database_lck,
|
||||||
let database = Arc::new(database);
|
std::ptr::null_mut(),
|
||||||
for message in iter {
|
)
|
||||||
let msg_id = unsafe {
|
};
|
||||||
call!(lib, notmuch_message_get_message_id)(message)
|
if new_revision > *revision_uuid_lck {
|
||||||
};
|
let query_str =
|
||||||
let c_str = unsafe { CStr::from_ptr(msg_id) };
|
format!("lastmod:{}..{}", *revision_uuid_lck, new_revision);
|
||||||
let env_hash = {
|
let query: Query =
|
||||||
let mut hasher = DefaultHasher::default();
|
Query::new(lib.clone(), &database_lck, &query_str)?;
|
||||||
c_str.hash(&mut hasher);
|
drop(database_lck);
|
||||||
hasher.finish()
|
let iter = query.search()?;
|
||||||
};
|
let mut tag_lock = tag_index.write().unwrap();
|
||||||
if let Some(mailbox_hashes) = mailbox_index_lck.get(&env_hash) {
|
let mailbox_index_lck = mailbox_index.write().unwrap();
|
||||||
let tags: (Flag, Vec<String>) =
|
let mailboxes_lck = mailboxes.read().unwrap();
|
||||||
TagIterator::new(lib.clone(), message)
|
let database = Arc::new(database);
|
||||||
.collect_flags_and_tags();
|
for message in iter {
|
||||||
for tag in tags.1.iter() {
|
let msg_id = unsafe {
|
||||||
let mut hasher = DefaultHasher::new();
|
call!(lib, notmuch_message_get_message_id)(message)
|
||||||
hasher.write(tag.as_bytes());
|
};
|
||||||
let num = hasher.finish();
|
let c_str = unsafe { CStr::from_ptr(msg_id) };
|
||||||
if !tag_lock.contains_key(&num) {
|
let env_hash = {
|
||||||
tag_lock.insert(num, tag.clone());
|
let mut hasher = DefaultHasher::default();
|
||||||
|
c_str.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
};
|
||||||
|
if let Some(mailbox_hashes) = mailbox_index_lck.get(&env_hash) {
|
||||||
|
let tags: (Flag, Vec<String>) =
|
||||||
|
TagIterator::new(lib.clone(), message)
|
||||||
|
.collect_flags_and_tags();
|
||||||
|
for tag in tags.1.iter() {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
hasher.write(tag.as_bytes());
|
||||||
|
let num = hasher.finish();
|
||||||
|
if !tag_lock.contains_key(&num) {
|
||||||
|
tag_lock.insert(num, tag.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for &mailbox_hash in mailbox_hashes {
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: NewFlags(env_hash, tags.clone()),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match notmuch_message_into_envelope(
|
||||||
|
lib.clone(),
|
||||||
|
index.clone(),
|
||||||
|
tag_index.clone(),
|
||||||
|
database.clone(),
|
||||||
|
message,
|
||||||
|
) {
|
||||||
|
Ok(env) => {
|
||||||
|
for (&mailbox_hash, m) in mailboxes_lck.iter() {
|
||||||
|
let query_str = format!(
|
||||||
|
"{} id:{}",
|
||||||
|
m.query_str.as_str(),
|
||||||
|
c_str.to_string_lossy()
|
||||||
|
);
|
||||||
|
let database_lck =
|
||||||
|
database.inner.read().unwrap();
|
||||||
|
let query: Query = Query::new(
|
||||||
|
lib.clone(),
|
||||||
|
&database_lck,
|
||||||
|
&query_str,
|
||||||
|
)?;
|
||||||
|
if query.count().unwrap_or(0) > 0 {
|
||||||
|
let mut total_lck = m.total.lock().unwrap();
|
||||||
|
let mut unseen_lck =
|
||||||
|
m.unseen.lock().unwrap();
|
||||||
|
*total_lck += 1;
|
||||||
|
if !env.is_seen() {
|
||||||
|
*unseen_lck += 1;
|
||||||
|
}
|
||||||
|
(sender)(
|
||||||
|
account_hash,
|
||||||
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
|
account_hash,
|
||||||
|
mailbox_hash,
|
||||||
|
kind: Create(Box::new(env.clone())),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
debug!("could not parse message {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for &mailbox_hash in mailbox_hashes {
|
}
|
||||||
(sender)(
|
drop(query);
|
||||||
account_hash,
|
let database_lck = database.inner.read().unwrap();
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
index.write().unwrap().retain(|&env_hash, msg_id| {
|
||||||
account_hash,
|
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
||||||
mailbox_hash,
|
if let Err(err) = unsafe {
|
||||||
kind: NewFlags(env_hash, tags.clone()),
|
try_call!(
|
||||||
}),
|
lib,
|
||||||
);
|
call!(lib, notmuch_database_find_message)(
|
||||||
}
|
*database_lck,
|
||||||
} else {
|
msg_id.as_ptr(),
|
||||||
match notmuch_message_into_envelope(
|
&mut message as *mut _,
|
||||||
lib.clone(),
|
)
|
||||||
index.clone(),
|
)
|
||||||
tag_index.clone(),
|
} {
|
||||||
database.clone(),
|
debug!(err);
|
||||||
message,
|
false
|
||||||
) {
|
} else {
|
||||||
Ok(env) => {
|
if message.is_null() {
|
||||||
for (&mailbox_hash, m) in mailboxes_lck.iter() {
|
if let Some(mailbox_hashes) =
|
||||||
let query_str = format!(
|
mailbox_index_lck.get(&env_hash)
|
||||||
"{} id:{}",
|
{
|
||||||
m.query_str.as_str(),
|
for &mailbox_hash in mailbox_hashes {
|
||||||
c_str.to_string_lossy()
|
let m = &mailboxes_lck[&mailbox_hash];
|
||||||
);
|
|
||||||
let database_lck =
|
|
||||||
database.inner.read().unwrap();
|
|
||||||
let query: Query = Query::new(
|
|
||||||
lib.clone(),
|
|
||||||
&database_lck,
|
|
||||||
&query_str,
|
|
||||||
)?;
|
|
||||||
if query.count().unwrap_or(0) > 0 {
|
|
||||||
let mut total_lck = m.total.lock().unwrap();
|
let mut total_lck = m.total.lock().unwrap();
|
||||||
let mut unseen_lck =
|
*total_lck = total_lck.saturating_sub(1);
|
||||||
m.unseen.lock().unwrap();
|
|
||||||
*total_lck += 1;
|
|
||||||
if !env.is_seen() {
|
|
||||||
*unseen_lck += 1;
|
|
||||||
}
|
|
||||||
(sender)(
|
(sender)(
|
||||||
account_hash,
|
account_hash,
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
kind: Create(Box::new(env.clone())),
|
kind: Remove(env_hash),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
!message.is_null()
|
||||||
debug!("could not parse message {:?}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
drop(query);
|
|
||||||
let database_lck = database.inner.read().unwrap();
|
|
||||||
index.write().unwrap().retain(|&env_hash, msg_id| {
|
|
||||||
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
|
|
||||||
if let Err(err) = unsafe {
|
|
||||||
try_call!(
|
|
||||||
lib,
|
|
||||||
call!(lib, notmuch_database_find_message)(
|
|
||||||
*database_lck,
|
|
||||||
msg_id.as_ptr(),
|
|
||||||
&mut message as *mut _,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} {
|
|
||||||
debug!(err);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
if message.is_null() {
|
|
||||||
if let Some(mailbox_hashes) =
|
|
||||||
mailbox_index_lck.get(&env_hash)
|
|
||||||
{
|
|
||||||
for &mailbox_hash in mailbox_hashes {
|
|
||||||
let m = &mailboxes_lck[&mailbox_hash];
|
|
||||||
let mut total_lck = m.total.lock().unwrap();
|
|
||||||
*total_lck = total_lck.saturating_sub(1);
|
|
||||||
(sender)(
|
|
||||||
account_hash,
|
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
|
||||||
mailbox_hash,
|
|
||||||
kind: Remove(env_hash),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
!message.is_null()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
*revision_uuid_lck = new_revision;
|
*revision_uuid_lck = new_revision;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = c(&sender) {
|
if let Err(err) = c(&sender) {
|
||||||
(sender)(
|
(sender)(
|
||||||
account_hash,
|
|
||||||
BackendEvent::Refresh(RefreshEvent {
|
|
||||||
account_hash,
|
account_hash,
|
||||||
mailbox_hash: 0,
|
BackendEvent::Refresh(RefreshEvent {
|
||||||
kind: Failure(err),
|
account_hash,
|
||||||
}),
|
mailbox_hash: 0,
|
||||||
);
|
kind: Failure(err),
|
||||||
}
|
}),
|
||||||
})?;
|
);
|
||||||
Ok(handle.thread().id())
|
}
|
||||||
}
|
})?;
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
Ok(handle.thread().id())
|
||||||
Ok(self
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
|
let ret = Ok(self
|
||||||
.mailboxes
|
.mailboxes
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, f)| (*k, BackendMailbox::clone(f)))
|
.map(|(k, f)| (*k, BackendMailbox::clone(f)))
|
||||||
.collect())
|
.collect());
|
||||||
|
Ok(Box::pin(async { ret }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
||||||
|
@ -851,12 +890,13 @@ impl BackendOp for NotmuchOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MessageIterator {
|
pub struct MessageIterator<'query> {
|
||||||
lib: Arc<libloading::Library>,
|
lib: Arc<libloading::Library>,
|
||||||
messages: *mut notmuch_messages_t,
|
messages: *mut notmuch_messages_t,
|
||||||
|
_ph: std::marker::PhantomData<*const Query<'query>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for MessageIterator {
|
impl Iterator for MessageIterator<'_> {
|
||||||
type Item = *mut notmuch_message_t;
|
type Item = *mut notmuch_message_t;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.messages.is_null() {
|
if self.messages.is_null() {
|
||||||
|
@ -1011,7 +1051,7 @@ impl<'s> Query<'s> {
|
||||||
Ok(count)
|
Ok(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search(&self) -> Result<MessageIterator> {
|
fn search(&'s self) -> Result<MessageIterator<'s>> {
|
||||||
let mut messages: *mut notmuch_messages_t = std::ptr::null_mut();
|
let mut messages: *mut notmuch_messages_t = std::ptr::null_mut();
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
call!(self.lib, notmuch_query_search_messages)(self.ptr, &mut messages as *mut _)
|
call!(self.lib, notmuch_query_search_messages)(self.ptr, &mut messages as *mut _)
|
||||||
|
@ -1026,6 +1066,7 @@ impl<'s> Query<'s> {
|
||||||
Ok(MessageIterator {
|
Ok(MessageIterator {
|
||||||
messages,
|
messages,
|
||||||
lib: self.lib.clone(),
|
lib: self.lib.clone(),
|
||||||
|
_ph: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1038,6 +1079,23 @@ impl Drop for Query<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn notmuch_message_insert(
|
||||||
|
lib: &libloading::Library,
|
||||||
|
index: &RwLock<HashMap<EnvelopeHash, CString>>,
|
||||||
|
message: *mut notmuch_message_t,
|
||||||
|
) -> CString {
|
||||||
|
let msg_id = unsafe { call!(lib, notmuch_message_get_message_id)(message) };
|
||||||
|
let env_hash = {
|
||||||
|
let c_str = unsafe { CStr::from_ptr(msg_id) };
|
||||||
|
let mut hasher = DefaultHasher::default();
|
||||||
|
c_str.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
};
|
||||||
|
let c_str = unsafe { CStr::from_ptr(msg_id) };
|
||||||
|
index.write().unwrap().insert(env_hash, c_str.into());
|
||||||
|
c_str.into()
|
||||||
|
}
|
||||||
|
|
||||||
fn notmuch_message_into_envelope(
|
fn notmuch_message_into_envelope(
|
||||||
lib: Arc<libloading::Library>,
|
lib: Arc<libloading::Library>,
|
||||||
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
//! values (see module `thread`)
|
//! values (see module `thread`)
|
||||||
//!
|
//!
|
||||||
//! Other exports are
|
//! Other exports are
|
||||||
//! - Thread management (see module `async_workers`)
|
|
||||||
//! - Basic mail account configuration to use with `backends` (see module `conf`)
|
//! - Basic mail account configuration to use with `backends` (see module `conf`)
|
||||||
//! - Parser combinators (see module `parsec`)
|
//! - Parser combinators (see module `parsec`)
|
||||||
//! - A `ShellExpandTrait` to expand paths like a shell.
|
//! - A `ShellExpandTrait` to expand paths like a shell.
|
||||||
|
@ -105,7 +104,6 @@ pub use self::logging::LoggingLevel::*;
|
||||||
pub use self::logging::*;
|
pub use self::logging::*;
|
||||||
|
|
||||||
pub mod addressbook;
|
pub mod addressbook;
|
||||||
pub mod async_workers;
|
|
||||||
pub mod backends;
|
pub mod backends;
|
||||||
mod collection;
|
mod collection;
|
||||||
pub mod conf;
|
pub mod conf;
|
||||||
|
|
|
@ -75,9 +75,6 @@ use crate::components::*;
|
||||||
pub mod conf;
|
pub mod conf;
|
||||||
use crate::conf::*;
|
use crate::conf::*;
|
||||||
|
|
||||||
pub mod workers;
|
|
||||||
use crate::workers::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlite3")]
|
#[cfg(feature = "sqlite3")]
|
||||||
pub mod sqlite3;
|
pub mod sqlite3;
|
||||||
|
|
||||||
|
@ -468,9 +465,6 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||||
state.check_accounts();
|
state.check_accounts();
|
||||||
state.redraw();
|
state.redraw();
|
||||||
},
|
},
|
||||||
ThreadEvent::NewThread(id, name) => {
|
|
||||||
state.new_thread(id, name);
|
|
||||||
},
|
|
||||||
ThreadEvent::JobFinished(id) => {
|
ThreadEvent::JobFinished(id) => {
|
||||||
debug!("Job finished {}", id);
|
debug!("Job finished {}", id);
|
||||||
for account in state.context.accounts.values_mut() {
|
for account in state.context.accounts.values_mut() {
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl Component for StatusPanel {
|
||||||
self.draw_accounts(context);
|
self.draw_accounts(context);
|
||||||
let (width, height) = self.content.size();
|
let (width, height) = self.content.size();
|
||||||
{
|
{
|
||||||
let (_, y) = write_string_to_grid(
|
let (_, _) = write_string_to_grid(
|
||||||
"Worker threads",
|
"Worker threads",
|
||||||
&mut self.content,
|
&mut self.content,
|
||||||
self.theme_default.fg,
|
self.theme_default.fg,
|
||||||
|
@ -59,6 +59,7 @@ impl Component for StatusPanel {
|
||||||
((1, 1), (width - 1, height - 1)),
|
((1, 1), (width - 1, height - 1)),
|
||||||
Some(1),
|
Some(1),
|
||||||
);
|
);
|
||||||
|
/*
|
||||||
let mut y = y + 1;
|
let mut y = y + 1;
|
||||||
let work_controller = context.work_controller().threads.lock().unwrap();
|
let work_controller = context.work_controller().threads.lock().unwrap();
|
||||||
let mut workers: Vec<&Worker> = work_controller.values().collect::<Vec<&Worker>>();
|
let mut workers: Vec<&Worker> = work_controller.values().collect::<Vec<&Worker>>();
|
||||||
|
@ -130,6 +131,7 @@ impl Component for StatusPanel {
|
||||||
|
|
||||||
y = y_off + 1;
|
y = y_off + 1;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
let (cols, rows) = (width!(area), height!(area));
|
let (cols, rows) = (width!(area), height!(area));
|
||||||
self.cursor = (
|
self.cursor = (
|
||||||
|
|
13
src/conf.rs
13
src/conf.rs
|
@ -350,18 +350,7 @@ impl FileSettings {
|
||||||
e.to_string()
|
e.to_string()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let mut backends = melib::backends::Backends::new();
|
let backends = melib::backends::Backends::new();
|
||||||
let plugin_manager = crate::plugins::PluginManager::new();
|
|
||||||
for (_, p) in s.plugins.clone() {
|
|
||||||
if crate::plugins::PluginKind::Backend == p.kind() {
|
|
||||||
crate::plugins::backend::PluginBackend::register(
|
|
||||||
plugin_manager.listener(),
|
|
||||||
p.clone(),
|
|
||||||
&mut backends,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let Themes {
|
let Themes {
|
||||||
light: default_light,
|
light: default_light,
|
||||||
dark: default_dark,
|
dark: default_dark,
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
use super::{AccountConf, FileMailboxConf};
|
use super::{AccountConf, FileMailboxConf};
|
||||||
use crate::jobs::{JobChannel, JobExecutor, JobId, JoinHandle};
|
use crate::jobs::{JobChannel, JobExecutor, JobId, JoinHandle};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
|
||||||
use melib::backends::*;
|
use melib::backends::*;
|
||||||
use melib::email::*;
|
use melib::email::*;
|
||||||
use melib::error::{MeliError, Result};
|
use melib::error::{MeliError, Result};
|
||||||
|
@ -72,8 +71,6 @@ macro_rules! try_recv_timeout {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MailboxStatus {
|
pub enum MailboxStatus {
|
||||||
Available,
|
Available,
|
||||||
|
@ -112,7 +109,6 @@ pub struct MailboxEntry {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub ref_mailbox: Mailbox,
|
pub ref_mailbox: Mailbox,
|
||||||
pub conf: FileMailboxConf,
|
pub conf: FileMailboxConf,
|
||||||
pub worker: Worker,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailboxEntry {
|
impl MailboxEntry {
|
||||||
|
@ -152,7 +148,6 @@ pub struct Account {
|
||||||
sent_mailbox: Option<MailboxHash>,
|
sent_mailbox: Option<MailboxHash>,
|
||||||
pub(crate) collection: Collection,
|
pub(crate) collection: Collection,
|
||||||
pub(crate) address_book: AddressBook,
|
pub(crate) address_book: AddressBook,
|
||||||
pub(crate) work_context: WorkContext,
|
|
||||||
pub(crate) settings: AccountConf,
|
pub(crate) settings: AccountConf,
|
||||||
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
||||||
|
|
||||||
|
@ -354,7 +349,6 @@ impl Account {
|
||||||
name: String,
|
name: String,
|
||||||
mut settings: AccountConf,
|
mut settings: AccountConf,
|
||||||
map: &Backends,
|
map: &Backends,
|
||||||
work_context: WorkContext,
|
|
||||||
job_executor: Arc<JobExecutor>,
|
job_executor: Arc<JobExecutor>,
|
||||||
sender: Sender<ThreadEvent>,
|
sender: Sender<ThreadEvent>,
|
||||||
event_consumer: BackendEventConsumer,
|
event_consumer: BackendEventConsumer,
|
||||||
|
@ -397,17 +391,18 @@ impl Account {
|
||||||
|
|
||||||
let mut active_jobs = HashMap::default();
|
let mut active_jobs = HashMap::default();
|
||||||
let mut active_job_instants = BTreeMap::default();
|
let mut active_job_instants = BTreeMap::default();
|
||||||
if backend.capabilities().is_async {
|
if let Ok(mailboxes_job) = backend.mailboxes() {
|
||||||
if let Ok(mailboxes_job) = backend.mailboxes_async() {
|
if let Ok(online_job) = backend.is_online() {
|
||||||
if let Ok(online_job) = backend.is_online_async() {
|
let (rcvr, handle, job_id) = if backend.capabilities().is_async {
|
||||||
let (rcvr, handle, job_id) =
|
job_executor.spawn_specialized(online_job.then(|_| mailboxes_job))
|
||||||
job_executor.spawn_specialized(online_job.then(|_| mailboxes_job));
|
} else {
|
||||||
active_jobs.insert(job_id, JobRequest::Mailboxes(handle, rcvr));
|
job_executor.spawn_blocking(online_job.then(|_| mailboxes_job))
|
||||||
active_job_instants.insert(std::time::Instant::now(), job_id);
|
};
|
||||||
}
|
active_jobs.insert(job_id, JobRequest::Mailboxes(handle, rcvr));
|
||||||
|
active_job_instants.insert(std::time::Instant::now(), job_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut ret = Account {
|
Ok(Account {
|
||||||
hash,
|
hash,
|
||||||
name,
|
name,
|
||||||
is_online: if !backend.capabilities().is_remote {
|
is_online: if !backend.capabilities().is_remote {
|
||||||
|
@ -422,7 +417,6 @@ impl Account {
|
||||||
address_book,
|
address_book,
|
||||||
sent_mailbox: Default::default(),
|
sent_mailbox: Default::default(),
|
||||||
collection: Default::default(),
|
collection: Default::default(),
|
||||||
work_context,
|
|
||||||
settings,
|
settings,
|
||||||
sender,
|
sender,
|
||||||
job_executor,
|
job_executor,
|
||||||
|
@ -431,21 +425,10 @@ impl Account {
|
||||||
event_queue: VecDeque::with_capacity(8),
|
event_queue: VecDeque::with_capacity(8),
|
||||||
backend_capabilities: backend.capabilities(),
|
backend_capabilities: backend.capabilities(),
|
||||||
backend: Arc::new(RwLock::new(backend)),
|
backend: Arc::new(RwLock::new(backend)),
|
||||||
};
|
})
|
||||||
|
|
||||||
if !ret.backend_capabilities.is_remote && !ret.backend_capabilities.is_async {
|
|
||||||
ret.init(None)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, ref_mailboxes: Option<HashMap<MailboxHash, Mailbox>>) -> Result<()> {
|
fn init(&mut self, mut ref_mailboxes: HashMap<MailboxHash, Mailbox>) -> Result<()> {
|
||||||
let mut ref_mailboxes: HashMap<MailboxHash, Mailbox> = if let Some(v) = ref_mailboxes {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
self.backend.read().unwrap().mailboxes()?
|
|
||||||
};
|
|
||||||
self.backend_capabilities = self.backend.read().unwrap().capabilities();
|
self.backend_capabilities = self.backend.read().unwrap().capabilities();
|
||||||
let mut mailbox_entries: IndexMap<MailboxHash, MailboxEntry> =
|
let mut mailbox_entries: IndexMap<MailboxHash, MailboxEntry> =
|
||||||
IndexMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default());
|
IndexMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default());
|
||||||
|
@ -493,7 +476,6 @@ impl Account {
|
||||||
name: f.path().to_string(),
|
name: f.path().to_string(),
|
||||||
status: MailboxStatus::None,
|
status: MailboxStatus::None,
|
||||||
conf: conf.clone(),
|
conf: conf.clone(),
|
||||||
worker: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -518,7 +500,6 @@ impl Account {
|
||||||
name: f.path().to_string(),
|
name: f.path().to_string(),
|
||||||
status: MailboxStatus::None,
|
status: MailboxStatus::None,
|
||||||
conf: new,
|
conf: new,
|
||||||
worker: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -574,30 +555,22 @@ impl Account {
|
||||||
{
|
{
|
||||||
let total = entry.ref_mailbox.count().ok().unwrap_or((0, 0)).1;
|
let total = entry.ref_mailbox.count().ok().unwrap_or((0, 0)).1;
|
||||||
entry.status = MailboxStatus::Parsing(0, total);
|
entry.status = MailboxStatus::Parsing(0, total);
|
||||||
if self.backend_capabilities.is_async {
|
if let Ok(mailbox_job) = self.backend.write().unwrap().fetch(*h) {
|
||||||
if let Ok(mailbox_job) = self.backend.write().unwrap().fetch_async(*h) {
|
let mailbox_job = mailbox_job.into_future();
|
||||||
let mailbox_job = mailbox_job.into_future();
|
let (rcvr, handle, job_id) = if self.backend_capabilities.is_async {
|
||||||
let (rcvr, handle, job_id) =
|
self.job_executor.spawn_specialized(mailbox_job)
|
||||||
self.job_executor.spawn_specialized(mailbox_job);
|
} else {
|
||||||
self.sender
|
self.job_executor.spawn_blocking(mailbox_job)
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
};
|
||||||
StatusEvent::NewJob(job_id),
|
self.sender
|
||||||
)))
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
||||||
.unwrap();
|
StatusEvent::NewJob(job_id),
|
||||||
self.active_jobs
|
)))
|
||||||
.insert(job_id, JobRequest::Fetch(*h, handle, rcvr));
|
.unwrap();
|
||||||
self.active_job_instants
|
self.active_jobs
|
||||||
.insert(std::time::Instant::now(), job_id);
|
.insert(job_id, JobRequest::Fetch(*h, handle, rcvr));
|
||||||
}
|
self.active_job_instants
|
||||||
} else {
|
.insert(std::time::Instant::now(), job_id);
|
||||||
entry.worker =
|
|
||||||
match Account::new_worker(&f, &mut self.backend, &self.work_context) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => {
|
|
||||||
entry.status = MailboxStatus::Failed(err);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -612,13 +585,9 @@ impl Account {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_worker(
|
fn new_worker(mailbox: &Mailbox, backend: &Arc<RwLock<Box<dyn MailBackend>>>) -> Result<()> {
|
||||||
mailbox: &Mailbox,
|
|
||||||
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
|
||||||
work_context: &WorkContext,
|
|
||||||
) -> Result<Worker> {
|
|
||||||
let mailbox_hash = mailbox.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let mut mailbox_handle = backend.write().unwrap().fetch(mailbox_hash)?;
|
let mailbox_handle = backend.write().unwrap().fetch(mailbox_hash)?;
|
||||||
let priority = match mailbox.special_usage() {
|
let priority = match mailbox.special_usage() {
|
||||||
SpecialUsageMailbox::Inbox => 0,
|
SpecialUsageMailbox::Inbox => 0,
|
||||||
SpecialUsageMailbox::Sent => 1,
|
SpecialUsageMailbox::Sent => 1,
|
||||||
|
@ -822,31 +791,7 @@ impl Account {
|
||||||
return Some(EnvelopeRemove(env_hash, thread_hash));
|
return Some(EnvelopeRemove(env_hash, thread_hash));
|
||||||
}
|
}
|
||||||
RefreshEventKind::Rescan => {
|
RefreshEventKind::Rescan => {
|
||||||
let handle = match Account::new_worker(
|
self.watch();
|
||||||
&self.mailbox_entries[&mailbox_hash].ref_mailbox,
|
|
||||||
&mut self.backend,
|
|
||||||
&self.work_context,
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => {
|
|
||||||
let ret = Some(Notification(
|
|
||||||
None,
|
|
||||||
err.to_string(),
|
|
||||||
Some(crate::types::NotificationType::ERROR),
|
|
||||||
));
|
|
||||||
self.mailbox_entries
|
|
||||||
.entry(mailbox_hash)
|
|
||||||
.and_modify(|entry| {
|
|
||||||
entry.status = MailboxStatus::Failed(err);
|
|
||||||
});
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.mailbox_entries
|
|
||||||
.entry(mailbox_hash)
|
|
||||||
.and_modify(|entry| {
|
|
||||||
entry.worker = handle;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
RefreshEventKind::Failure(err) => {
|
RefreshEventKind::Failure(err) => {
|
||||||
debug!("RefreshEvent Failure: {}", err.to_string());
|
debug!("RefreshEvent Failure: {}", err.to_string());
|
||||||
|
@ -891,62 +836,40 @@ impl Account {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if self.backend_capabilities.is_async {
|
if let Ok(refresh_job) = self.backend.write().unwrap().refresh(mailbox_hash) {
|
||||||
if let Ok(refresh_job) = self.backend.write().unwrap().refresh_async(mailbox_hash) {
|
let (rcvr, handle, job_id) = if self.backend_capabilities.is_async {
|
||||||
let (rcvr, handle, job_id) = self.job_executor.spawn_specialized(refresh_job);
|
self.job_executor.spawn_specialized(refresh_job)
|
||||||
self.sender
|
} else {
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
self.job_executor.spawn_blocking(refresh_job)
|
||||||
StatusEvent::NewJob(job_id),
|
};
|
||||||
)))
|
self.sender
|
||||||
.unwrap();
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
||||||
self.active_jobs
|
StatusEvent::NewJob(job_id),
|
||||||
.insert(job_id, JobRequest::Refresh(mailbox_hash, handle, rcvr));
|
)))
|
||||||
self.active_job_instants
|
.unwrap();
|
||||||
.insert(std::time::Instant::now(), job_id);
|
self.active_jobs
|
||||||
}
|
.insert(job_id, JobRequest::Refresh(mailbox_hash, handle, rcvr));
|
||||||
} else {
|
self.active_job_instants
|
||||||
let mut h = self.backend.write().unwrap().refresh(mailbox_hash)?;
|
.insert(std::time::Instant::now(), job_id);
|
||||||
self.work_context.new_work.send(h.work().unwrap()).unwrap();
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn watch(&mut self) {
|
pub fn watch(&mut self) {
|
||||||
if self.settings.account().manual_refresh {
|
if self.settings.account().manual_refresh {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.backend_capabilities.is_async {
|
if !self.active_jobs.values().any(|j| j.is_watch()) {
|
||||||
if !self.active_jobs.values().any(|j| j.is_watch()) {
|
match self.backend.read().unwrap().watch() {
|
||||||
match self.backend.read().unwrap().watch_async() {
|
Ok(fut) => {
|
||||||
Ok(fut) => {
|
let (_channel, handle, job_id) = if self.backend_capabilities.is_async {
|
||||||
let (handle, job_id) = self.job_executor.spawn(fut);
|
self.job_executor.spawn_specialized(fut)
|
||||||
self.active_jobs.insert(job_id, JobRequest::Watch(handle));
|
} else {
|
||||||
}
|
self.job_executor.spawn_blocking(fut)
|
||||||
Err(e) => {
|
};
|
||||||
self.sender
|
self.active_jobs.insert(job_id, JobRequest::Watch(handle));
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
||||||
StatusEvent::DisplayMessage(e.to_string()),
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match self
|
|
||||||
.backend
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.watch(self.work_context.clone())
|
|
||||||
{
|
|
||||||
Ok(id) => {
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::NewThread(
|
|
||||||
id,
|
|
||||||
format!("watching {}", self.name()).into(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
||||||
|
@ -994,95 +917,37 @@ impl Account {
|
||||||
if mailbox_hash == 0 {
|
if mailbox_hash == 0 {
|
||||||
return Err(0);
|
return Err(0);
|
||||||
}
|
}
|
||||||
loop {
|
match self.mailbox_entries[&mailbox_hash].status {
|
||||||
match self
|
MailboxStatus::Available | MailboxStatus::Parsing(_, _)
|
||||||
.mailbox_entries
|
if self
|
||||||
.get_mut(&mailbox_hash)
|
.collection
|
||||||
.unwrap()
|
.mailboxes
|
||||||
.worker
|
.read()
|
||||||
.as_mut()
|
.unwrap()
|
||||||
|
.contains_key(&mailbox_hash) =>
|
||||||
{
|
{
|
||||||
None => {
|
Ok(())
|
||||||
return match self.mailbox_entries[&mailbox_hash].status {
|
}
|
||||||
MailboxStatus::Available | MailboxStatus::Parsing(_, _)
|
MailboxStatus::None => {
|
||||||
if self
|
if !self.active_jobs.values().any(|j| j.is_fetch(mailbox_hash)) {
|
||||||
.collection
|
let mailbox_job = self.backend.write().unwrap().fetch(mailbox_hash);
|
||||||
.mailboxes
|
match mailbox_job {
|
||||||
.read()
|
Ok(mailbox_job) => {
|
||||||
.unwrap()
|
let mailbox_job = mailbox_job.into_future();
|
||||||
.contains_key(&mailbox_hash) =>
|
let (rcvr, handle, job_id) = if self.backend_capabilities.is_async {
|
||||||
{
|
self.job_executor.spawn_specialized(mailbox_job)
|
||||||
Ok(())
|
} else {
|
||||||
|
self.job_executor.spawn_blocking(mailbox_job)
|
||||||
|
};
|
||||||
|
self.sender
|
||||||
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
||||||
|
StatusEvent::NewJob(job_id),
|
||||||
|
)))
|
||||||
|
.unwrap();
|
||||||
|
self.active_jobs
|
||||||
|
.insert(job_id, JobRequest::Fetch(mailbox_hash, handle, rcvr));
|
||||||
}
|
}
|
||||||
MailboxStatus::None => {
|
Err(err) => {
|
||||||
if self.backend_capabilities.is_async {
|
|
||||||
if !self.active_jobs.values().any(|j| j.is_fetch(mailbox_hash)) {
|
|
||||||
let mailbox_job =
|
|
||||||
self.backend.write().unwrap().fetch_async(mailbox_hash);
|
|
||||||
match mailbox_job {
|
|
||||||
Ok(mailbox_job) => {
|
|
||||||
let mailbox_job = mailbox_job.into_future();
|
|
||||||
let (rcvr, handle, job_id) =
|
|
||||||
self.job_executor.spawn_specialized(mailbox_job);
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
||||||
StatusEvent::NewJob(job_id),
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
self.active_jobs.insert(
|
|
||||||
job_id,
|
|
||||||
JobRequest::Fetch(mailbox_hash, handle, rcvr),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
self.mailbox_entries.entry(mailbox_hash).and_modify(
|
|
||||||
|entry| {
|
|
||||||
entry.status = MailboxStatus::Failed(err);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(
|
|
||||||
mailbox_hash,
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if self.mailbox_entries[&mailbox_hash].worker.is_none() {
|
|
||||||
let handle = match Account::new_worker(
|
|
||||||
&self.mailbox_entries[&mailbox_hash].ref_mailbox,
|
|
||||||
&mut self.backend,
|
|
||||||
&self.work_context,
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(err) => {
|
|
||||||
self.mailbox_entries.entry(mailbox_hash).and_modify(
|
|
||||||
|entry| {
|
|
||||||
entry.status = MailboxStatus::Failed(err);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return Err(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.mailbox_entries
|
|
||||||
.entry(mailbox_hash)
|
|
||||||
.and_modify(|entry| {
|
|
||||||
entry.worker = handle;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.collection.new_mailbox(mailbox_hash);
|
|
||||||
Err(0)
|
|
||||||
}
|
|
||||||
_ => Err(0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Some(ref mut w) => match debug!(w.poll()) {
|
|
||||||
Ok(AsyncStatus::NoUpdate) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(AsyncStatus::Payload(payload)) => {
|
|
||||||
debug!("got payload in status for {}", mailbox_hash);
|
|
||||||
if let Err(err) = payload {
|
|
||||||
self.mailbox_entries
|
self.mailbox_entries
|
||||||
.entry(mailbox_hash)
|
.entry(mailbox_hash)
|
||||||
.and_modify(|entry| {
|
.and_modify(|entry| {
|
||||||
|
@ -1091,85 +956,13 @@ impl Account {
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return Err(0);
|
|
||||||
}
|
}
|
||||||
let envelopes = payload
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| (e.hash(), e))
|
|
||||||
.collect::<HashMap<EnvelopeHash, Envelope>>();
|
|
||||||
self.mailbox_entries
|
|
||||||
.entry(mailbox_hash)
|
|
||||||
.and_modify(|entry| match entry.status {
|
|
||||||
MailboxStatus::None => {
|
|
||||||
entry.status = MailboxStatus::Parsing(envelopes.len(), 0);
|
|
||||||
}
|
|
||||||
MailboxStatus::Parsing(ref mut done, _) => {
|
|
||||||
*done += envelopes.len();
|
|
||||||
}
|
|
||||||
MailboxStatus::Failed(_) => {
|
|
||||||
entry.status = MailboxStatus::Parsing(envelopes.len(), 0);
|
|
||||||
}
|
|
||||||
MailboxStatus::Available => {}
|
|
||||||
});
|
|
||||||
if let Some(updated_mailboxes) =
|
|
||||||
self.collection
|
|
||||||
.merge(envelopes, mailbox_hash, self.sent_mailbox)
|
|
||||||
{
|
|
||||||
for f in updated_mailboxes {
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
Ok(AsyncStatus::Finished) => {
|
}
|
||||||
debug!("got finished in status for {}", mailbox_hash);
|
self.collection.new_mailbox(mailbox_hash);
|
||||||
self.mailbox_entries
|
Err(0)
|
||||||
.entry(mailbox_hash)
|
}
|
||||||
.and_modify(|entry| {
|
_ => Err(0),
|
||||||
entry.status = MailboxStatus::Available;
|
|
||||||
entry.worker = None;
|
|
||||||
});
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
|
||||||
self.hash,
|
|
||||||
mailbox_hash,
|
|
||||||
))))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
Ok(AsyncStatus::ProgressReport(n)) => {
|
|
||||||
self.mailbox_entries
|
|
||||||
.entry(mailbox_hash)
|
|
||||||
.and_modify(|entry| match entry.status {
|
|
||||||
MailboxStatus::Parsing(ref mut d, _) => {
|
|
||||||
*d += n;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
});
|
|
||||||
//return Err(n);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if self.mailbox_entries[&mailbox_hash].status.is_available()
|
|
||||||
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
|
|
||||||
&& self
|
|
||||||
.collection
|
|
||||||
.mailboxes
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.contains_key(&mailbox_hash))
|
|
||||||
{
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1403,14 +1196,7 @@ impl Account {
|
||||||
parent.ref_mailbox = mailboxes.remove(&parent_hash).unwrap();
|
parent.ref_mailbox = mailboxes.remove(&parent_hash).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let (status, worker) = match Account::new_worker(
|
let status = MailboxStatus::Parsing(0, 0);
|
||||||
&mailboxes[&mailbox_hash],
|
|
||||||
&mut self.backend,
|
|
||||||
&self.work_context,
|
|
||||||
) {
|
|
||||||
Ok(v) => (MailboxStatus::Parsing(0, 0), v),
|
|
||||||
Err(err) => (MailboxStatus::Failed(err), None),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.mailbox_entries.insert(
|
self.mailbox_entries.insert(
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
|
@ -1418,7 +1204,6 @@ impl Account {
|
||||||
name: mailboxes[&mailbox_hash].path().to_string(),
|
name: mailboxes[&mailbox_hash].path().to_string(),
|
||||||
status,
|
status,
|
||||||
conf: new,
|
conf: new,
|
||||||
worker,
|
|
||||||
ref_mailbox: mailboxes.remove(&mailbox_hash).unwrap(),
|
ref_mailbox: mailboxes.remove(&mailbox_hash).unwrap(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1486,8 +1271,6 @@ impl Account {
|
||||||
&self.mailbox_entries,
|
&self.mailbox_entries,
|
||||||
&mut self.mailboxes_order,
|
&mut self.mailboxes_order,
|
||||||
);
|
);
|
||||||
// FIXME Kill worker as well
|
|
||||||
|
|
||||||
// FIXME remove from settings as well
|
// FIXME remove from settings as well
|
||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
|
@ -1577,34 +1360,21 @@ impl Account {
|
||||||
{
|
{
|
||||||
return self.is_online.clone();
|
return self.is_online.clone();
|
||||||
}
|
}
|
||||||
if self.backend_capabilities.is_async {
|
if self.is_online.is_ok() && !timeout {
|
||||||
if self.is_online.is_ok() && !timeout {
|
return Ok(());
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if !self.active_jobs.values().any(JobRequest::is_online) {
|
|
||||||
let online_job = self.backend.read().unwrap().is_online_async();
|
|
||||||
if let Ok(online_job) = online_job {
|
|
||||||
let (rcvr, handle, job_id) = self.job_executor.spawn_specialized(online_job);
|
|
||||||
self.insert_job(job_id, JobRequest::IsOnline(handle, rcvr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return self.is_online.clone();
|
|
||||||
} else {
|
|
||||||
let ret = self.backend.read().unwrap().is_online();
|
|
||||||
if ret.is_ok() != self.is_online.is_ok() {
|
|
||||||
if ret.is_ok() {
|
|
||||||
self.last_online_request = std::time::Instant::now();
|
|
||||||
self.init(None)?;
|
|
||||||
}
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::AccountStatusChange(
|
|
||||||
self.hash,
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
self.is_online = ret.clone();
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
if !self.active_jobs.values().any(JobRequest::is_online) {
|
||||||
|
let online_job = self.backend.read().unwrap().is_online();
|
||||||
|
if let Ok(online_job) = online_job {
|
||||||
|
let (rcvr, handle, job_id) = if self.backend_capabilities.is_async {
|
||||||
|
self.job_executor.spawn_specialized(online_job)
|
||||||
|
} else {
|
||||||
|
self.job_executor.spawn_blocking(online_job)
|
||||||
|
};
|
||||||
|
self.insert_job(job_id, JobRequest::IsOnline(handle, rcvr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self.is_online.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(
|
pub fn search(
|
||||||
|
@ -1657,8 +1427,7 @@ impl Account {
|
||||||
match self.active_jobs.remove(job_id).unwrap() {
|
match self.active_jobs.remove(job_id).unwrap() {
|
||||||
JobRequest::Mailboxes(_, ref mut chan) => {
|
JobRequest::Mailboxes(_, ref mut chan) => {
|
||||||
if let Some(mailboxes) = chan.try_recv().unwrap() {
|
if let Some(mailboxes) = chan.try_recv().unwrap() {
|
||||||
if let Err(err) = mailboxes.and_then(|mailboxes| self.init(Some(mailboxes)))
|
if let Err(err) = mailboxes.and_then(|mailboxes| self.init(mailboxes)) {
|
||||||
{
|
|
||||||
if err.kind.is_authentication() {
|
if err.kind.is_authentication() {
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::Notification(
|
.send(ThreadEvent::UIEvent(UIEvent::Notification(
|
||||||
|
@ -1670,9 +1439,7 @@ impl Account {
|
||||||
self.is_online = Err(err);
|
self.is_online = Err(err);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if let Ok(mailboxes_job) =
|
if let Ok(mailboxes_job) = self.backend.read().unwrap().mailboxes() {
|
||||||
self.backend.read().unwrap().mailboxes_async()
|
|
||||||
{
|
|
||||||
let (rcvr, handle, job_id) =
|
let (rcvr, handle, job_id) =
|
||||||
self.job_executor.spawn_specialized(mailboxes_job);
|
self.job_executor.spawn_specialized(mailboxes_job);
|
||||||
self.active_jobs
|
self.active_jobs
|
||||||
|
@ -1699,7 +1466,6 @@ impl Account {
|
||||||
.entry(mailbox_hash)
|
.entry(mailbox_hash)
|
||||||
.and_modify(|entry| {
|
.and_modify(|entry| {
|
||||||
entry.status = MailboxStatus::Available;
|
entry.status = MailboxStatus::Available;
|
||||||
entry.worker = None;
|
|
||||||
});
|
});
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
||||||
|
@ -1789,7 +1555,7 @@ impl Account {
|
||||||
}
|
}
|
||||||
self.is_online = is_online;
|
self.is_online = is_online;
|
||||||
}
|
}
|
||||||
if let Ok(online_job) = self.backend.read().unwrap().is_online_async() {
|
if let Ok(online_job) = self.backend.read().unwrap().is_online() {
|
||||||
let (rcvr, handle, job_id) =
|
let (rcvr, handle, job_id) =
|
||||||
self.job_executor.spawn_specialized(online_job);
|
self.job_executor.spawn_specialized(online_job);
|
||||||
self.active_jobs
|
self.active_jobs
|
||||||
|
|
12
src/jobs.rs
12
src/jobs.rs
|
@ -157,8 +157,6 @@ impl JobExecutor {
|
||||||
F: Future<Output = Result<()>> + Send + 'static,
|
F: Future<Output = Result<()>> + Send + 'static,
|
||||||
{
|
{
|
||||||
let job_id = JobId::new();
|
let job_id = JobId::new();
|
||||||
let _job_id = job_id;
|
|
||||||
let __job_id = job_id;
|
|
||||||
let finished_sender = self.sender.clone();
|
let finished_sender = self.sender.clone();
|
||||||
let injector = self.global_queue.clone();
|
let injector = self.global_queue.clone();
|
||||||
// Create a task and schedule it for execution.
|
// Create a task and schedule it for execution.
|
||||||
|
@ -166,11 +164,11 @@ impl JobExecutor {
|
||||||
async move {
|
async move {
|
||||||
let r = future.await;
|
let r = future.await;
|
||||||
finished_sender
|
finished_sender
|
||||||
.send(ThreadEvent::JobFinished(__job_id))
|
.send(ThreadEvent::JobFinished(job_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
r
|
r
|
||||||
},
|
},
|
||||||
move |task| injector.push(MeliTask { task, id: _job_id }),
|
move |task| injector.push(MeliTask { task, id: job_id }),
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
task.schedule();
|
task.schedule();
|
||||||
|
@ -191,8 +189,6 @@ impl JobExecutor {
|
||||||
let (sender, receiver) = oneshot::channel();
|
let (sender, receiver) = oneshot::channel();
|
||||||
let finished_sender = self.sender.clone();
|
let finished_sender = self.sender.clone();
|
||||||
let job_id = JobId::new();
|
let job_id = JobId::new();
|
||||||
let _job_id = job_id;
|
|
||||||
let __job_id = job_id;
|
|
||||||
let injector = self.global_queue.clone();
|
let injector = self.global_queue.clone();
|
||||||
// Create a task and schedule it for execution.
|
// Create a task and schedule it for execution.
|
||||||
let (task, handle) = async_task::spawn(
|
let (task, handle) = async_task::spawn(
|
||||||
|
@ -200,11 +196,11 @@ impl JobExecutor {
|
||||||
let res = future.await;
|
let res = future.await;
|
||||||
let _ = sender.send(res);
|
let _ = sender.send(res);
|
||||||
finished_sender
|
finished_sender
|
||||||
.send(ThreadEvent::JobFinished(__job_id))
|
.send(ThreadEvent::JobFinished(job_id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
move |task| injector.push(MeliTask { task, id: _job_id }),
|
move |task| injector.push(MeliTask { task, id: job_id }),
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
task.schedule();
|
task.schedule();
|
||||||
|
|
|
@ -63,9 +63,6 @@ use crate::components::*;
|
||||||
pub mod conf;
|
pub mod conf;
|
||||||
use crate::conf::*;
|
use crate::conf::*;
|
||||||
|
|
||||||
pub mod workers;
|
|
||||||
use crate::workers::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlite3")]
|
#[cfg(feature = "sqlite3")]
|
||||||
pub mod sqlite3;
|
pub mod sqlite3;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ use std::os::unix::net::{UnixListener, UnixStream};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub mod backend;
|
//pub mod backend;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
pub use rpc::*;
|
pub use rpc::*;
|
||||||
|
|
||||||
|
|
|
@ -191,17 +191,10 @@ impl MailBackend for PluginBackend {
|
||||||
Ok(w.build(handle))
|
Ok(w.build(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<()>> {
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
fn watch(&self, _work_context: WorkContext) -> Result<std::thread::ThreadId> {
|
|
||||||
Err(MeliError::new("Unimplemented."))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
|
|
||||||
let mut ret: HashMap<MailboxHash, Mailbox> = Default::default();
|
let mut ret: HashMap<MailboxHash, Mailbox> = Default::default();
|
||||||
ret.insert(0, Mailbox::default());
|
ret.insert(0, Mailbox::default());
|
||||||
Ok(ret)
|
Ok(Box::pin(async { Ok(ret) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
||||||
|
|
19
src/state.rs
19
src/state.rs
|
@ -113,7 +113,6 @@ pub struct Context {
|
||||||
sender: Sender<ThreadEvent>,
|
sender: Sender<ThreadEvent>,
|
||||||
receiver: Receiver<ThreadEvent>,
|
receiver: Receiver<ThreadEvent>,
|
||||||
input_thread: InputHandler,
|
input_thread: InputHandler,
|
||||||
work_controller: WorkController,
|
|
||||||
job_executor: Arc<JobExecutor>,
|
job_executor: Arc<JobExecutor>,
|
||||||
pub children: Vec<std::process::Child>,
|
pub children: Vec<std::process::Child>,
|
||||||
pub plugin_manager: PluginManager,
|
pub plugin_manager: PluginManager,
|
||||||
|
@ -167,10 +166,6 @@ impl Context {
|
||||||
let idx = self.accounts.get_index_of(&account_hash).unwrap();
|
let idx = self.accounts.get_index_of(&account_hash).unwrap();
|
||||||
self.is_online_idx(idx)
|
self.is_online_idx(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn work_controller(&self) -> &WorkController {
|
|
||||||
&self.work_controller
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A State object to manage and own components and components of the UI. `State` is responsible for
|
/// A State object to manage and own components and components of the UI. `State` is responsible for
|
||||||
|
@ -246,6 +241,7 @@ impl State {
|
||||||
};
|
};
|
||||||
let mut plugin_manager = PluginManager::new();
|
let mut plugin_manager = PluginManager::new();
|
||||||
for (_, p) in settings.plugins.clone() {
|
for (_, p) in settings.plugins.clone() {
|
||||||
|
/*
|
||||||
if crate::plugins::PluginKind::Backend == p.kind() {
|
if crate::plugins::PluginKind::Backend == p.kind() {
|
||||||
debug!("registering {:?}", &p);
|
debug!("registering {:?}", &p);
|
||||||
crate::plugins::backend::PluginBackend::register(
|
crate::plugins::backend::PluginBackend::register(
|
||||||
|
@ -254,6 +250,7 @@ impl State {
|
||||||
&mut backends,
|
&mut backends,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
plugin_manager.register(p)?;
|
plugin_manager.register(p)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +258,6 @@ impl State {
|
||||||
let cols = termsize.0 as usize;
|
let cols = termsize.0 as usize;
|
||||||
let rows = termsize.1 as usize;
|
let rows = termsize.1 as usize;
|
||||||
|
|
||||||
let work_controller = WorkController::new(sender.clone());
|
|
||||||
let job_executor = Arc::new(JobExecutor::new(sender.clone()));
|
let job_executor = Arc::new(JobExecutor::new(sender.clone()));
|
||||||
let accounts = {
|
let accounts = {
|
||||||
settings
|
settings
|
||||||
|
@ -281,7 +277,6 @@ impl State {
|
||||||
n.to_string(),
|
n.to_string(),
|
||||||
a_s.clone(),
|
a_s.clone(),
|
||||||
&backends,
|
&backends,
|
||||||
work_controller.get_context(),
|
|
||||||
job_executor.clone(),
|
job_executor.clone(),
|
||||||
sender.clone(),
|
sender.clone(),
|
||||||
BackendEventConsumer::new(Arc::new(
|
BackendEventConsumer::new(Arc::new(
|
||||||
|
@ -346,7 +341,6 @@ impl State {
|
||||||
dirty_areas: VecDeque::with_capacity(5),
|
dirty_areas: VecDeque::with_capacity(5),
|
||||||
replies: VecDeque::with_capacity(5),
|
replies: VecDeque::with_capacity(5),
|
||||||
temp_files: Vec::new(),
|
temp_files: Vec::new(),
|
||||||
work_controller,
|
|
||||||
job_executor,
|
job_executor,
|
||||||
children: vec![],
|
children: vec![],
|
||||||
plugin_manager,
|
plugin_manager,
|
||||||
|
@ -422,15 +416,6 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_thread(&mut self, id: thread::ThreadId, name: String) {
|
|
||||||
self.context
|
|
||||||
.work_controller
|
|
||||||
.static_threads
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.insert(id, name.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switch back to the terminal's main screen (The command line the user sees before opening
|
/// Switch back to the terminal's main screen (The command line the user sees before opening
|
||||||
/// the application)
|
/// the application)
|
||||||
pub fn switch_to_main_screen(&mut self) {
|
pub fn switch_to_main_screen(&mut self) {
|
||||||
|
|
|
@ -44,7 +44,6 @@ use melib::backends::{AccountHash, BackendEvent, MailboxHash};
|
||||||
use melib::{EnvelopeHash, RefreshEvent, ThreadHash};
|
use melib::{EnvelopeHash, RefreshEvent, ThreadHash};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::thread;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -62,7 +61,6 @@ pub enum StatusEvent {
|
||||||
/// to the main process.
|
/// to the main process.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ThreadEvent {
|
pub enum ThreadEvent {
|
||||||
NewThread(thread::ThreadId, String),
|
|
||||||
/// User input.
|
/// User input.
|
||||||
Input((Key, Vec<u8>)),
|
Input((Key, Vec<u8>)),
|
||||||
/// User input and input as raw bytes.
|
/// User input and input as raw bytes.
|
||||||
|
|
385
src/workers.rs
385
src/workers.rs
|
@ -1,385 +0,0 @@
|
||||||
/*
|
|
||||||
* meli
|
|
||||||
*
|
|
||||||
* Copyright 2017-2020 Manos Pitsidianakis
|
|
||||||
*
|
|
||||||
* This file is part of meli.
|
|
||||||
*
|
|
||||||
* meli is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* meli is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! Simple blocking job control.
|
|
||||||
*/
|
|
||||||
use crate::types::ThreadEvent;
|
|
||||||
use crossbeam::{
|
|
||||||
channel::{bounded, unbounded, Sender},
|
|
||||||
select,
|
|
||||||
};
|
|
||||||
use melib::async_workers::{Work, WorkContext};
|
|
||||||
use melib::datetime::{self, UnixTimestamp};
|
|
||||||
use melib::text_processing::Truncate;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
const MAX_WORKER: usize = 4;
|
|
||||||
|
|
||||||
/// Representation of a worker thread for use in `WorkController`. These values are to be displayed
|
|
||||||
/// to the user.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Worker {
|
|
||||||
pub name: String,
|
|
||||||
pub status: String,
|
|
||||||
pub heartbeat: UnixTimestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Worker {
|
|
||||||
fn from(val: String) -> Self {
|
|
||||||
Worker {
|
|
||||||
name: val,
|
|
||||||
status: String::new(),
|
|
||||||
heartbeat: datetime::now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WorkController {
|
|
||||||
pub queue: WorkQueue,
|
|
||||||
thread_end_tx: Sender<bool>,
|
|
||||||
/// Worker threads that take up on jobs from self.queue
|
|
||||||
pub threads: Arc<Mutex<HashMap<thread::ThreadId, Worker>>>,
|
|
||||||
/// Special function threads that live indefinitely (eg watching a mailbox)
|
|
||||||
pub static_threads: Arc<Mutex<HashMap<thread::ThreadId, Worker>>>,
|
|
||||||
work_context: WorkContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for WorkController {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Ok(lock) = self.threads.lock() {
|
|
||||||
for _ in 0..lock.len() {
|
|
||||||
let _ = self.thread_end_tx.send(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct WorkQueue {
|
|
||||||
inner: Arc<Mutex<Vec<Work>>>,
|
|
||||||
new_jobs_tx: Sender<bool>,
|
|
||||||
work_context: WorkContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkQueue {
|
|
||||||
fn new(new_jobs_tx: Sender<bool>, work_context: WorkContext) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
new_jobs_tx,
|
|
||||||
work_context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blocks the current thread until work is available, then
|
|
||||||
/// gets the data required to perform that work.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns None if there is no more work in the queue.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the underlying mutex became poisoned. This is exceedingly
|
|
||||||
/// unlikely.
|
|
||||||
fn get_work(&self) -> Option<Work> {
|
|
||||||
// try to get a lock on the mutex.
|
|
||||||
let maybe_queue = self.inner.lock();
|
|
||||||
if let Ok(mut queue) = maybe_queue {
|
|
||||||
if queue.is_empty() {
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
return Some(queue.swap_remove(0));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// poisoned mutex, some other thread holding the mutex has panicked!
|
|
||||||
panic!("WorkQueue::get_work() tried to lock a poisoned mutex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both the controller (main thread) and workers can use this
|
|
||||||
// function to add work to the queue.
|
|
||||||
|
|
||||||
/// Blocks the current thread until work can be added, then
|
|
||||||
/// adds that work to the end of the queue.
|
|
||||||
/// Returns the amount of work now in the queue.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the underlying mutex became poisoned. This is exceedingly
|
|
||||||
/// unlikely.
|
|
||||||
pub fn add_work(&self, work: Work) {
|
|
||||||
if work.is_static {
|
|
||||||
self.work_context.new_work.send(work).unwrap();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As above, try to get a lock on the mutex.
|
|
||||||
if let Ok(mut queue) = self.inner.lock() {
|
|
||||||
/* Insert in position that maintains the queue sorted */
|
|
||||||
let pos = match queue.binary_search_by(|probe| probe.cmp(&work)) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(p) => p,
|
|
||||||
};
|
|
||||||
queue.insert(pos, work);
|
|
||||||
|
|
||||||
/* inform threads that new job is available */
|
|
||||||
self.new_jobs_tx.send(true).unwrap();
|
|
||||||
} else {
|
|
||||||
panic!("WorkQueue::add_work() tried to lock a poisoned mutex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkController {
|
|
||||||
pub fn new(pulse: Sender<ThreadEvent>) -> WorkController {
|
|
||||||
let (new_jobs_tx, new_jobs_rx) = unbounded();
|
|
||||||
|
|
||||||
/* create a channel for jobs to send new work to Controller thread */
|
|
||||||
let (new_work_tx, new_work_rx) = unbounded();
|
|
||||||
|
|
||||||
/* create a channel for jobs to set their names */
|
|
||||||
let (set_name_tx, set_name_rx) = unbounded();
|
|
||||||
|
|
||||||
/* create a channel for jobs to set their statuses */
|
|
||||||
let (set_status_tx, set_status_rx) = unbounded();
|
|
||||||
|
|
||||||
/* create a channel for jobs to announce their demise */
|
|
||||||
let (finished_tx, finished_rx) = unbounded();
|
|
||||||
|
|
||||||
/* each associated thread will hold a copy of this context item in order to communicate
|
|
||||||
* with the controller thread */
|
|
||||||
let work_context = WorkContext {
|
|
||||||
new_work: new_work_tx,
|
|
||||||
set_name: set_name_tx,
|
|
||||||
set_status: set_status_tx,
|
|
||||||
finished: finished_tx,
|
|
||||||
};
|
|
||||||
|
|
||||||
let queue: WorkQueue = WorkQueue::new(new_jobs_tx, work_context.clone());
|
|
||||||
|
|
||||||
let active_threads = Arc::new(AtomicUsize::new(MAX_WORKER));
|
|
||||||
// Create a SyncFlag to share whether or not there are more jobs to be done.
|
|
||||||
let (thread_end_tx, thread_end_rx) = bounded(1);
|
|
||||||
|
|
||||||
let threads_lock: Arc<Mutex<HashMap<thread::ThreadId, Worker>>> =
|
|
||||||
Arc::new(Mutex::new(HashMap::default()));
|
|
||||||
|
|
||||||
let static_threads_lock: Arc<Mutex<HashMap<thread::ThreadId, Worker>>> =
|
|
||||||
Arc::new(Mutex::new(HashMap::default()));
|
|
||||||
|
|
||||||
let mut threads = threads_lock.lock().unwrap();
|
|
||||||
/* spawn worker threads */
|
|
||||||
for _ in 0..MAX_WORKER {
|
|
||||||
/* Each worker thread will wait on two channels: thread_end and new_jobs. thread_end
|
|
||||||
* informs the worker that it should quit and new_jobs informs that there is a new job
|
|
||||||
* available inside the queue. Only one worker will get each job, and others will
|
|
||||||
* go back to waiting on the channels */
|
|
||||||
let thread_queue = queue.clone();
|
|
||||||
|
|
||||||
let active_threads = active_threads.clone();
|
|
||||||
let thread_end_rx = thread_end_rx.clone();
|
|
||||||
let new_jobs_rx = new_jobs_rx.clone();
|
|
||||||
let new_jobs_rx = new_jobs_rx.clone();
|
|
||||||
|
|
||||||
let work_context = work_context.clone();
|
|
||||||
let pulse = pulse.clone();
|
|
||||||
|
|
||||||
let handle = spawn_worker(
|
|
||||||
thread_queue,
|
|
||||||
active_threads,
|
|
||||||
thread_end_rx,
|
|
||||||
new_jobs_rx,
|
|
||||||
work_context,
|
|
||||||
pulse,
|
|
||||||
);
|
|
||||||
|
|
||||||
/* add the handle for the newly spawned thread to the list of handles */
|
|
||||||
threads.insert(handle.thread().id(), String::from("idle-worker").into());
|
|
||||||
}
|
|
||||||
/* drop lock */
|
|
||||||
drop(threads);
|
|
||||||
|
|
||||||
{
|
|
||||||
/* start controller thread */
|
|
||||||
let threads_lock = threads_lock.clone();
|
|
||||||
let _static_threads_lock = static_threads_lock.clone();
|
|
||||||
let thread_queue = queue.clone();
|
|
||||||
let thread_end_rx = thread_end_rx.clone();
|
|
||||||
let work_context = work_context.clone();
|
|
||||||
|
|
||||||
let handle = thread::spawn(move || 'control_loop: loop {
|
|
||||||
select! {
|
|
||||||
recv(thread_end_rx) -> _ => {
|
|
||||||
debug!("received thread_end_rx, quitting");
|
|
||||||
break 'control_loop;
|
|
||||||
},
|
|
||||||
recv(new_work_rx) -> work => {
|
|
||||||
if let Ok(work) = work {
|
|
||||||
if work.is_static {
|
|
||||||
let work_context = work_context.clone();
|
|
||||||
let handle = thread::spawn(move || work.compute(work_context));
|
|
||||||
_static_threads_lock.lock().unwrap().insert(handle.thread().id(), String::new().into());
|
|
||||||
} else {
|
|
||||||
if active_threads.load(Ordering::SeqCst) == 0 {
|
|
||||||
let handle = spawn_worker(
|
|
||||||
thread_queue.clone(),
|
|
||||||
active_threads.clone(),
|
|
||||||
thread_end_rx.clone(),
|
|
||||||
new_jobs_rx.clone(),
|
|
||||||
work_context.clone(),
|
|
||||||
pulse.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
/* add the handle for the newly spawned thread to the list of handles */
|
|
||||||
threads_lock.lock().unwrap().insert(handle.thread().id(), String::from("idle-worker").into());
|
|
||||||
}
|
|
||||||
thread_queue.add_work(work);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recv(set_name_rx) -> new_name => {
|
|
||||||
if let Ok((thread_id, mut new_name)) = new_name {
|
|
||||||
new_name.truncate_at_boundary(256);
|
|
||||||
let mut threads = threads_lock.lock().unwrap();
|
|
||||||
let mut static_threads = _static_threads_lock.lock().unwrap();
|
|
||||||
let now = datetime::now();
|
|
||||||
if threads.contains_key(&thread_id) {
|
|
||||||
threads.entry(thread_id).and_modify(|e| {
|
|
||||||
e.name = new_name;
|
|
||||||
e.heartbeat = now;
|
|
||||||
});
|
|
||||||
} else if static_threads.contains_key(&thread_id) {
|
|
||||||
static_threads.entry(thread_id).and_modify(|e| {
|
|
||||||
|
|
||||||
e.name = new_name;
|
|
||||||
e.heartbeat = now;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
static_threads.insert(thread_id, Worker { heartbeat: now, .. new_name.into() });
|
|
||||||
static_threads.entry(thread_id).and_modify(|e| {
|
|
||||||
e.heartbeat = now;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pulse.send(ThreadEvent::Pulse).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recv(set_status_rx) -> new_status => {
|
|
||||||
if let Ok((thread_id, mut new_status)) = new_status {
|
|
||||||
new_status.truncate_at_boundary(256);
|
|
||||||
let mut threads = threads_lock.lock().unwrap();
|
|
||||||
let mut static_threads = _static_threads_lock.lock().unwrap();
|
|
||||||
let now = datetime::now();
|
|
||||||
if threads.contains_key(&thread_id) {
|
|
||||||
threads.entry(thread_id).and_modify(|e| {
|
|
||||||
e.status = new_status;
|
|
||||||
e.heartbeat = now;
|
|
||||||
});
|
|
||||||
} else if static_threads.contains_key(&thread_id) {
|
|
||||||
static_threads.entry(thread_id).and_modify(|e| {
|
|
||||||
e.status = new_status;
|
|
||||||
e.heartbeat = now;
|
|
||||||
});
|
|
||||||
debug!(&static_threads[&thread_id]);
|
|
||||||
} else {
|
|
||||||
static_threads.insert(thread_id, Worker { status: new_status, heartbeat: now, .. String::new().into() });
|
|
||||||
}
|
|
||||||
pulse.send(ThreadEvent::Pulse).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recv(finished_rx) -> dead_thread_id => {
|
|
||||||
if let Ok(thread_id) = dead_thread_id {
|
|
||||||
let mut threads = threads_lock.lock().unwrap();
|
|
||||||
let mut static_threads = _static_threads_lock.lock().unwrap();
|
|
||||||
if threads.contains_key(&thread_id) {
|
|
||||||
threads.remove(&thread_id);
|
|
||||||
} else if static_threads.contains_key(&thread_id) {
|
|
||||||
static_threads.remove(&thread_id);
|
|
||||||
} else {
|
|
||||||
/* Nothing to do */
|
|
||||||
}
|
|
||||||
pulse.send(ThreadEvent::Pulse).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut static_threads = static_threads_lock.lock().unwrap();
|
|
||||||
static_threads.insert(
|
|
||||||
handle.thread().id(),
|
|
||||||
"WorkController-thread".to_string().into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkController {
|
|
||||||
queue,
|
|
||||||
thread_end_tx,
|
|
||||||
threads: threads_lock,
|
|
||||||
static_threads: static_threads_lock,
|
|
||||||
work_context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_static_thread(&mut self, id: std::thread::ThreadId) {
|
|
||||||
self.static_threads
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.insert(id, String::new().into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_context(&self) -> WorkContext {
|
|
||||||
self.work_context.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_worker(
|
|
||||||
thread_queue: WorkQueue,
|
|
||||||
active_threads: Arc<AtomicUsize>,
|
|
||||||
thread_end_rx: crossbeam::Receiver<bool>,
|
|
||||||
new_jobs_rx: crossbeam::Receiver<bool>,
|
|
||||||
work_context: WorkContext,
|
|
||||||
pulse: crossbeam::Sender<ThreadEvent>,
|
|
||||||
) -> std::thread::JoinHandle<()> {
|
|
||||||
thread::spawn(move || 'work_loop: loop {
|
|
||||||
debug!("Waiting for work");
|
|
||||||
select! {
|
|
||||||
recv(thread_end_rx) -> _ => {
|
|
||||||
debug!("received thread_end_rx, quitting");
|
|
||||||
active_threads.fetch_sub(1, Ordering::SeqCst);
|
|
||||||
break 'work_loop;
|
|
||||||
},
|
|
||||||
recv(new_jobs_rx) -> _ => {
|
|
||||||
active_threads.fetch_sub(1, Ordering::SeqCst);
|
|
||||||
while let Some(work) = thread_queue.get_work() {
|
|
||||||
debug!("Got some work");
|
|
||||||
work.compute(work_context.clone());
|
|
||||||
debug!("finished work");
|
|
||||||
work_context.set_name.send((std::thread::current().id(), "idle-worker".to_string())).unwrap();
|
|
||||||
work_context.set_status.send((std::thread::current().id(), "inactive".to_string())).unwrap();
|
|
||||||
pulse.send(ThreadEvent::Pulse).unwrap();
|
|
||||||
|
|
||||||
std::thread::yield_now();
|
|
||||||
}
|
|
||||||
active_threads.fetch_add(1, Ordering::SeqCst);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Reference in New Issue