mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 17:06:35 +03:00
accounts: update EventEmitter on add_account (#2559)
* accounts: update EventEmitter on add_account * accounts: do not lock waiting for EventEmitter in dc_accounts_add_account Otherwise dc_accounts_add_account blocks until an event arrives on some existing account.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use async_std::channel::{Receiver, Sender};
|
||||||
use async_std::fs;
|
use async_std::fs;
|
||||||
use async_std::path::PathBuf;
|
use async_std::path::PathBuf;
|
||||||
use async_std::prelude::*;
|
use async_std::prelude::*;
|
||||||
@@ -18,6 +19,7 @@ pub struct Accounts {
|
|||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
config: Config,
|
config: Config,
|
||||||
accounts: Arc<RwLock<BTreeMap<u32, Context>>>,
|
accounts: Arc<RwLock<BTreeMap<u32, Context>>>,
|
||||||
|
emitter: EventEmitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Accounts {
|
impl Accounts {
|
||||||
@@ -52,10 +54,16 @@ impl Accounts {
|
|||||||
let config = Config::from_file(config_file).await?;
|
let config = Config::from_file(config_file).await?;
|
||||||
let accounts = config.load_accounts().await?;
|
let accounts = config.load_accounts().await?;
|
||||||
|
|
||||||
|
let emitter = EventEmitter::new();
|
||||||
|
for account in accounts.values() {
|
||||||
|
emitter.add_account(account).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
dir,
|
dir,
|
||||||
config,
|
config,
|
||||||
accounts: Arc::new(RwLock::new(accounts)),
|
accounts: Arc::new(RwLock::new(accounts)),
|
||||||
|
emitter,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +91,7 @@ impl Accounts {
|
|||||||
let account_config = self.config.new_account(&self.dir).await?;
|
let account_config = self.config.new_account(&self.dir).await?;
|
||||||
|
|
||||||
let ctx = Context::new(os_name, account_config.dbfile().into(), account_config.id).await?;
|
let ctx = Context::new(os_name, account_config.dbfile().into(), account_config.id).await?;
|
||||||
|
self.emitter.add_account(&ctx).await?;
|
||||||
self.accounts.write().await.insert(account_config.id, ctx);
|
self.accounts.write().await.insert(account_config.id, ctx);
|
||||||
|
|
||||||
Ok(account_config.id)
|
Ok(account_config.id)
|
||||||
@@ -228,30 +237,60 @@ impl Accounts {
|
|||||||
|
|
||||||
/// Unified event emitter.
|
/// Unified event emitter.
|
||||||
pub async fn get_event_emitter(&self) -> EventEmitter {
|
pub async fn get_event_emitter(&self) -> EventEmitter {
|
||||||
let emitters: Vec<_> = self
|
self.emitter.clone()
|
||||||
.accounts
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.iter()
|
|
||||||
.map(|(_id, a)| a.get_event_emitter())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
EventEmitter(futures::stream::select_all(emitters))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EventEmitter(futures::stream::SelectAll<crate::events::EventEmitter>);
|
pub struct EventEmitter {
|
||||||
|
/// Aggregate stream of events from all accounts.
|
||||||
|
stream: Arc<RwLock<futures::stream::SelectAll<crate::events::EventEmitter>>>,
|
||||||
|
|
||||||
|
/// Sender for the channel where new account emitters will be pushed.
|
||||||
|
sender: Sender<crate::events::EventEmitter>,
|
||||||
|
|
||||||
|
/// Receiver for the channel where new account emitters will be pushed.
|
||||||
|
receiver: Receiver<crate::events::EventEmitter>,
|
||||||
|
}
|
||||||
|
|
||||||
impl EventEmitter {
|
impl EventEmitter {
|
||||||
/// Blocking recv of an event. Return `None` if all `Sender`s have been droped.
|
pub fn new() -> Self {
|
||||||
pub fn recv_sync(&mut self) -> Option<Event> {
|
let (sender, receiver) = async_std::channel::unbounded();
|
||||||
async_std::task::block_on(self.recv())
|
Self {
|
||||||
|
stream: Arc::new(RwLock::new(futures::stream::SelectAll::new())),
|
||||||
|
sender,
|
||||||
|
receiver,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Async recv of an event. Return `None` if all `Sender`s have been droped.
|
/// Blocking recv of an event. Return `None` if all `Sender`s have been droped.
|
||||||
pub async fn recv(&mut self) -> Option<Event> {
|
pub fn recv_sync(&mut self) -> Option<Event> {
|
||||||
self.0.next().await
|
async_std::task::block_on(self.recv()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Async recv of an event. Return `None` if all `Sender`s have been dropped.
|
||||||
|
pub async fn recv(&mut self) -> Result<Option<Event>> {
|
||||||
|
let mut stream = self.stream.write().await;
|
||||||
|
loop {
|
||||||
|
match futures::future::select(self.receiver.recv(), stream.next()).await {
|
||||||
|
futures::future::Either::Left((emitter, _)) => {
|
||||||
|
stream.push(emitter?);
|
||||||
|
}
|
||||||
|
futures::future::Either::Right((ev, _)) => return Ok(ev),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add event emitter of a new account to the aggregate event emitter.
|
||||||
|
pub async fn add_account(&self, context: &Context) -> Result<()> {
|
||||||
|
self.sender.send(context.get_event_emitter()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EventEmitter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +301,7 @@ impl async_std::stream::Stream for EventEmitter {
|
|||||||
mut self: std::pin::Pin<&mut Self>,
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Option<Self::Item>> {
|
) -> std::task::Poll<Option<Self::Item>> {
|
||||||
std::pin::Pin::new(&mut self.0).poll_next(cx)
|
std::pin::Pin::new(&mut self).poll_next(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user