You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
5.4 KiB
Rust

use std::collections::HashMap;
use std::mem;
use std::sync::Arc;
use std::thread;
use std::thread::{JoinHandle, ThreadId};
use std::time::Duration;
use crossbeam_channel::{Receiver, Sender};
use parking_lot::Mutex;
use crate::event::Event;
use crate::stream::cryptostream::CryptoStream;
use crate::utils::result::{VentedError, VentedResult};
use crate::utils::sync::AsyncValue;
use crate::WaitGroup;
const MAX_ENQUEUED_EVENTS: usize = 50;
pub const CONNECTION_TIMEOUT_SECONDS: u64 = 5;
#[derive(Clone, Debug)]
pub struct ConcurrentStreamManager {
max_threads: usize,
threads: Arc<Mutex<HashMap<ThreadId, JoinHandle<()>>>>,
emitters: Arc<Mutex<HashMap<String, Sender<(Event, AsyncValue<(), VentedError>)>>>>,
event_receiver: Receiver<(String, Event)>,
listener_sender: Sender<(String, Event)>,
}
impl ConcurrentStreamManager {
pub fn new(max_threads: usize) -> Self {
let (sender, receiver) = crossbeam_channel::unbounded();
Self {
max_threads,
threads: Arc::new(Mutex::new(HashMap::new())),
emitters: Arc::new(Mutex::new(HashMap::new())),
event_receiver: receiver,
listener_sender: sender,
}
}
/// Returns if the manager has a connection to the given node
pub fn has_connection(&self, node: &String) -> bool {
self.emitters.lock().contains_key(node)
}
/// Returns the receiver for events
pub fn receiver(&self) -> Receiver<(String, Event)> {
self.event_receiver.clone()
}
/// Sends an event and returns an async value with the result
pub fn send(&self, target: &String, event: Event) -> AsyncValue<(), VentedError> {
let mut value = AsyncValue::new();
if let Some(emitter) = self.emitters.lock().get(target) {
if let Err(_) = emitter.send_timeout(
(event, value.clone()),
Duration::from_secs(CONNECTION_TIMEOUT_SECONDS),
) {
value.reject(VentedError::UnreachableNode(target.clone()));
}
} else {
value.reject(VentedError::UnknownNode(target.clone()))
}
value
}
/// Adds a connection to the manager causing it to start two new threads
/// This call blocks until the two threads are started up
pub fn add_connection(&self, stream: CryptoStream) -> VentedResult<()> {
if self.threads.lock().len() > self.max_threads {
return Err(VentedError::TooManyThreads);
}
let sender = self.listener_sender.clone();
let recv_id = stream.receiver_node().clone();
let (emitter, receiver) = crossbeam_channel::bounded(MAX_ENQUEUED_EVENTS);
self.emitters.lock().insert(recv_id.clone(), emitter);
let wg = WaitGroup::new();
let sender_thread = thread::Builder::new()
.name(format!("sender-{}", stream.receiver_node()))
.spawn({
let stream = stream.clone();
let recv_id = recv_id.clone();
let emitters = Arc::clone(&self.emitters);
let threads = Arc::clone(&self.threads);
let wg = WaitGroup::clone(&wg);
move || {
mem::drop(wg);
while let Ok((event, mut future)) = receiver.recv() {
if let Err(e) = stream.send(event) {
log::debug!("Failed to send event to {}: {}", recv_id, e);
future.reject(e);
break;
}
future.resolve(());
}
if let Err(e) = stream.shutdown() {
log::error!("Failed to shutdown stream: {}", e);
}
emitters.lock().remove(&recv_id);
threads.lock().remove(&thread::current().id());
}
})?;
self.threads
.lock()
.insert(sender_thread.thread().id(), sender_thread);
let receiver_thread = thread::Builder::new()
.name(format!("receiver-{}", stream.receiver_node()))
.spawn({
let threads = Arc::clone(&self.threads);
let wg = WaitGroup::clone(&wg);
move || {
mem::drop(wg);
loop {
match stream.read() {
Ok(mut event) => {
event.origin = Some(stream.receiver_node().clone());
if let Err(e) = sender.send((stream.receiver_node().clone(), event))
{
log::trace!("Failed to get event from {}: {}", recv_id, e);
break;
}
}
Err(e) => {
log::error!("Failed to send event: {}", e);
break;
}
}
}
if let Err(e) = stream.shutdown() {
log::error!("Failed to shutdown stream: {}", e);
}
threads.lock().remove(&thread::current().id());
}
})?;
self.threads
.lock()
.insert(receiver_thread.thread().id(), receiver_thread);
wg.wait();
Ok(())
}
}