use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use taba_client::{Call, Client, Error, Reply};
use crate::tui::api::ApiCall;
use crate::tui::msg::Msg;
pub trait Backend: Send + Sync {
fn call(&self, call: &Call) -> Result<Reply, Error>;
}
impl Backend for Client {
fn call(&self, call: &Call) -> Result<Reply, Error> {
Client::call(self, call)
}
}
pub struct SwitchBackend {
current: std::sync::RwLock<Arc<dyn Backend>>,
}
impl SwitchBackend {
pub fn new(first: Arc<dyn Backend>) -> SwitchBackend {
SwitchBackend {
current: std::sync::RwLock::new(first),
}
}
pub fn switch(&self, next: Arc<dyn Backend>) {
*self.current.write().unwrap_or_else(|e| e.into_inner()) = next;
}
}
impl Backend for SwitchBackend {
fn call(&self, call: &Call) -> Result<Reply, Error> {
let backend = Arc::clone(&self.current.read().unwrap_or_else(|e| e.into_inner()));
backend.call(call)
}
}
pub const THREADS: usize = 4;
struct Job {
id: u64,
generation: u64,
call: ApiCall,
}
pub struct Workers {
jobs: Sender<Job>,
}
impl Workers {
pub fn start(backend: Arc<dyn Backend>, results: Sender<Msg>, threads: usize) -> Workers {
let (jobs, queue) = std::sync::mpsc::channel::<Job>();
let queue: Arc<Mutex<Receiver<Job>>> = Arc::new(Mutex::new(queue));
for _ in 0..threads.max(1) {
let queue = Arc::clone(&queue);
let backend = Arc::clone(&backend);
let results = results.clone();
thread::spawn(move || loop {
let job = match queue.lock() {
Ok(guard) => guard.recv(),
Err(_) => return,
};
let Ok(job) = job else { return };
let result = backend.call(&job.call.to_call());
let reply = Msg::Reply {
id: job.id,
generation: job.generation,
call: job.call,
result,
};
if results.send(reply).is_err() {
return;
}
});
}
Workers { jobs }
}
pub fn submit(&self, id: u64, generation: u64, call: ApiCall) {
let _ = self.jobs.send(Job {
id,
generation,
call,
});
}
}
#[cfg(test)]
mod tests;