use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use crate::concurrent::logbuffer::term_reader::ErrorHandler;
use crate::concurrent::strategies::Strategy;
use crate::utils::errors::{AeronError, GenericError};
pub trait Agent {
fn on_start(&mut self) -> Result<(), AeronError>;
fn do_work(&mut self) -> Result<i32, AeronError>;
fn on_close(&mut self) -> Result<(), AeronError>;
}
pub struct AgentStopper {
thread: Option<thread::JoinHandle<()>>,
tx: Sender<bool>,
}
impl AgentStopper {
pub fn new(thread: thread::JoinHandle<()>, tx: Sender<bool>) -> Self {
Self {
thread: Some(thread),
tx,
}
}
pub fn stop(&mut self) {
crate::log!(trace, "Sending stop command to AgentRunner");
self.tx.send(true).expect("Can't send stop command to AgentRunner");
let _b = self.thread.take().unwrap().join();
crate::log!(trace, "AgentRunner stopped");
}
}
pub struct AgentRunner<
A: 'static + std::marker::Send + std::marker::Sync + Agent,
I: 'static + std::marker::Send + std::marker::Sync + Strategy,
> {
agent: Arc<Mutex<A>>, idle_strategy: Arc<I>,
exception_handler: Box<dyn ErrorHandler + std::marker::Send>,
name: String,
cpu_id: Option<usize>,
}
impl<
A: 'static + std::marker::Send + std::marker::Sync + Agent,
I: 'static + std::marker::Send + std::marker::Sync + Strategy,
> AgentRunner<A, I>
{
pub fn new(
agent: Arc<Mutex<A>>,
idle_strategy: Arc<I>,
exception_handler: Box<dyn ErrorHandler + std::marker::Send>,
name: &str,
) -> Self {
Self {
agent,
idle_strategy,
exception_handler,
name: String::from(name),
cpu_id: None,
}
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn set_name(&mut self, new_name: &str) {
self.name = String::from(new_name);
}
pub fn cpu_id(&self) -> Option<usize> {
self.cpu_id
}
pub fn set_cpu_id(&mut self, cpu_id: usize) {
self.cpu_id = Some(cpu_id);
}
pub fn start(mut this: Self) -> Result<AgentStopper, AeronError> {
let (tx, rx) = channel::<bool>();
let th = thread::Builder::new().name(this.name.clone()).spawn(move || {
if let Some(id) = this.cpu_id {
let res = core_affinity::set_for_current(core_affinity::CoreId { id });
if !res {
panic!("Failed to pin thread to core {}", id);
}
crate::log!(trace, "AgentRunner thread started on CPU {}", id);
} else {
crate::log!(trace, "AgentRunner thread started");
}
this.run(rx);
crate::log!(trace, "AgentRunner thread finished");
});
if let Ok(handle) = th {
Ok(AgentStopper::new(handle, tx))
} else {
Err(GenericError::AgentStartFailed { msg: th.err() }.into())
}
}
pub fn run(&mut self, stop_rx: Receiver<bool>) {
if let Err(error) = self.agent.lock().expect("Mutex poisoned").on_start() {
self.exception_handler.call(error);
}
loop {
if let Ok(time_to_stop) = stop_rx.try_recv() {
if time_to_stop {
crate::log!(trace, "AgentRunner received stop command");
break;
}
}
match self.agent.lock().expect("Mutex poisoned").do_work() {
Ok(work_cnt) => self.idle_strategy.idle_opt(work_cnt),
Err(error) => self.exception_handler.call(error),
}
}
crate::log!(trace, "AgentRunner closing");
if let Err(error) = self.agent.lock().expect("Mutex poisoned").on_close() {
self.exception_handler.call(error);
}
crate::log!(trace, "AgentRunner closed");
}
}